likes
comments
collection
share

Flutter-官方推荐的Flutter与原生交互插件Pigeon

作者站长头像
站长
· 阅读数 3

插件介绍

插件信息

插件地址:pub.flutter-io.cn/packages/pi…

插件版本:pigeon: 3.0.3

插件的使用

Flutter的准备工作

在Flutter项目的lib目录外创建一个pigeons文件夹,在pigeons文件夹中创建schema.dart

因为目前是原生和Flutter混编,我需要从原生获取用户信息和获取localHost,所以schema.dart的代码如下

/// Description : 定义与原生通信--通过自动生成减少手写代码量
/// 请求参数和返回结果都必需是类结构 否则无法生成文件
/// - Flutter 调用 Native 方法 ( @HostApi() )
/// - Native 调用 Flutter 方法 ( @FlutterApi() )

///Flutter 调用原生代码
@HostApi()
abstract class UserInfoApi {
  ///获取用户信息
  UserInfo getUserInfo();
  ///获取LocalHost用于抓包
  String getLocalHost();
}

///用户信息实体类
class UserInfo {
  String? userId;
  String? realName;
  String? phone;
  String? headImg;
}

使用到的命令

命令拆解:
①` flutter pub run pigeon`  
生成代码的命令

②` --input pigeons/schema.dart `
指定生成代码的输入`dart`文件

③ `--dart_out lib/schema.dart `
指定输出生成`dart`文件的目录文件

④ `--objc_header_out ios/Flutter/FlutterPluginRegistrant/Classes/schema.h  `
指定要生成的`iOS`的`.h`文件路径

⑤ `--objc_source_out ios/Flutter/FlutterPluginRegistrant/Classes/schema.m  `
指定要生成的`iOS`的`.m`文件路径

⑥ `--java_out android/Flutter/src/main/java/io/flutter/plugins/Schema.java `
指定要生成的`Android`的`.java`文件路径

⑦  `--java_package "io.flutter.plugins`
指定`Android`的包名,在`android/src/main/`下的`AndroidManifest.xml`里的`package`

⑧ `--objc_prefix Z`(可选)指定生成OC文件的前缀为Z,前缀自己定义为自己的。


在项目目录~/flutter_pigeon_plugin下,直接执行,命令太麻烦了,这里我就创建了一个脚本,每次有更新的时候直接运行脚本就可以了。

创建脚本

这个脚本就是根据上面的命令写的,这个也是在pigeons文件夹创建的run pigeon.sh脚本。

flutter pub run pigeon \
  --input pigeons/schema.dart \
  --dart_out lib/schema.dart \
  --objc_header_out ios/Flutter/FlutterPluginRegistrant/Classes/schema.h \
  --objc_source_out ios/Flutter/FlutterPluginRegistrant/Classes/schema.m \
  --java_out android/Flutter/src/main/java/io/flutter/plugins/Schema.java \
  --java_package "io.flutter.plugins"

  # 点击绿色三角形按钮,或者选择文件右键选择运行此文件,开启运行以上脚本文件,
  # 然后会自动生产跨端通信的Android文件(Java文件)和iOS文件(Object_C)
 No newline at end of file

这个就是运行脚本后自动生成的文件

Flutter-官方推荐的Flutter与原生交互插件Pigeon

Flutter-官方推荐的Flutter与原生交互插件Pigeon

Flutter-官方推荐的Flutter与原生交互插件Pigeon

如何进行通信呢?

举个例子:上面我不是写了两个方法,那假如我想抓包,就需要设置成Mac的ip

Flutter端代码

if (!kReleaseMode){
  UserInfoApi().getLocalHost().then((value) {
    if(value.isNotEmpty){
      (_dio!.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (HttpClient client) {
        client.findProxy = (uri) {
          return "PROXY $value:8888";
        };
      };
    }
  });
}

原生端代码

首先肯定要把Flutter Module引入原生工程,这里就不做说明了。创建了一个类继承自FlutterViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [GeneratedPluginRegistrant registerWithRegistry:self.engine];
    //这里是需要传入一个对象的
    UserInfoApiSetup(self.binaryMessenger, [UserInfoAPI new]);
}

- (void)viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];
    self.navigationController.navigationBarHidden = YES;
}

- (void)viewWillDisappear:(BOOL)animated {

    [super viewWillDisappear:animated];
    self.navigationController.navigationBarHidden = NO;
}

或者也可以在AppDelegate里面做初始化

FlutterViewController *flutterVC = self.window.rootViewController;
UserInfoApiSetup(flutterVC.binaryMessenger, [UserInfoAPI new]);

还需要创建一个遵循UserInfoApi协议的类,并且实现协议方法

@interface UserInfoAPI : NSObject<UserInfoApi>

@end

@implementation UserInfoAPI
- (nullable UserInfo *)getUserInfoWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
    UserInfo *userInfo = [[UserInfo alloc] init];
    return userInfo;
}

- (nullable NSString *)getLocalHostWithError:(FlutterError * _Nullable * _Nonnull)error {
    return @"10.210.3.16";
}
@end

Flutter页面返回原生页面

这样就实现了Flutter调用原生的方法,一般我们跳转到Flutter页面中,想返回原生页面也可以实现,通过调用原生的方法,原生处理返回,比如iOS端可以这样实现

if ([mthodStr isEqualToString:@"closeFlutterVC"]) {
   //调用popViewControllerAnimated
}

Flutter返回Android端则直接可以调用方法完成

SystemNavigator.pop();

原生进入Flutter页面

//这里可以传入要进入flutter页面的路由
TestViewController *flutterVC = [[TestViewController alloc] initWithProject:nil initialRoute:@"" nibName:nil bundle:nil];
[self.navigationController pushViewController:flutterVC animated:YES];

Android端其实也是同理,实现getInitialRoute方法,返回要跳转页面的路由即可。

总结

由于以前都是使用原生的通信方式,那种确实麻烦一些,使用这个插件的方式确实比较简单。但是这样有点增加了代码阅读的难度,没有直接原生通信的可读性高,凡事都有两面性就看自己去衡量了。