Flutter 与原生之间的通讯依赖灵活的音讯传递办法

  • 运用的Flutter部分经过渠道通道(platform channel)将音讯发送到其运用程序的地点的宿主(iOS或Android)运用(原生运用)

  • 宿主监听渠道通道,并接纳该音讯。然后它会调用该渠道的 API,并将响应发送回客户端,即运用程序的 Flutter 部分

Flutter 与原生存在三种交互办法

  • MethodChannel:用于传递办法调用(method invocation)一般用来调用 native 中某个办法

  • BasicMessageChannel:用于传递字符串和半结构化的信息,这个用的比较少

  • EventChannel:用于数据流(event streams)的通讯。有监听功用,比方电量改变之后直接推送数据给flutter端

三种 Channel 之间互相独立,各有用处,但它们在设计上却十分附近。每种 Channel 均有三个重要成员变量

  • name: String类型,代表 Channel 的名字,也是其仅有标识符

  • messager:BinaryMessenger 类型,代表音讯信使,是音讯的发送与接纳的工具

  • codec: MessageCodec 类型或 MethodCodec 类型,代表音讯的编解码器

具体运用

  • 首先别离创立 Native 工程和 Flutter Module。我这里是以 iOS 端和 Flutter 通讯为例,创立完 iOS 工程后,需求经过 CocoaPods 管理 Flutter Module。

Flutter 与原生通信的三种方式

  • 然后在 iOS 工程里边创立 Podfile ,然后引入 Flutter Module ,具体代码如下:
platform :ios,'11.0'
inhibit_all_warnings!
#flutter module 文件途径
flutter_application_path = '../flutter_module' 
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'Native_iOS' do
install_all_flutter_pods(flutter_application_path)
end

留意: flutter_application_path 这个是 Flutter 工程的途径,我是原生项目和 Flutter在一个目录下

  • 最终在终端 pod install 一下,看是否能正常引入 Flutter Module。这样就能够在iOS工程里边导入#import <Flutter/Flutter.h>

Flutter 与原生通信的三种方式

一、MethodChannel的运用

这里写的代码完成了以下功用

1.完成了点击原生页面的按钮跳转到 Flutter 页面,在 Flutter 点击回来按钮能正常回来原生页面

2.完成在Flutter页面点击当时电量,从原生界面传值到 Flutter 页面

原生端代码

@property (nonatomic, strong)FlutterEngine *flutterEngine;
@property (nonatomic, strong)FlutterViewController *flutterVC;
@property (nonatomic, strong)FlutterMethodChannel *methodChannel;
- (void)viewDidLoad {
  [super viewDidLoad];
  //隐藏了原生的导航栏
  self.navigationController.navigationBarHidden = YES;
  UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 80, 80)];
  btn.backgroundColor = [UIColor redColor];
  [btn addTarget:self action: @selector(onBtnClick) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:btn];
  self.flutterVC = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
    //创立channel
  self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"methodChannel" binaryMessenger:self.flutterVC.binaryMessenger];
}
- (void)onBtnClick {
  //告知Flutter对应的页面
    //Method--办法称号,arguments--参数
  [self.methodChannel invokeMethod:@"EnterFlutter" arguments:@""];
    //push进入Flutter页面
  [self.navigationController pushViewController:self.flutterVC animated:YES];
  __weak __typeof(self) weakSelf = self;
    //监听Flutter发来的事情
  [self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
        //响应从Flutter页面发送来的办法
    if ([call.method isEqualToString:@"exit"]) {
      [weakSelf.flutterVC.navigationController popViewControllerAnimated:YES];
    } else if ([call.method isEqualToString:@"getBatteryLevel"]) {
            //传值回Flutter页面
      [weakSelf.methodChannel invokeMethod:@"BatteryLevel" arguments:@"60%"];
    }
  }];
}
//创立引擎,真正在项目中,引擎能够界说为一个单例。这样处理防止在原生里边存在多引擎,是十分占有内存的
- (FlutterEngine *)flutterEngine {
  if (!_flutterEngine) {
    FlutterEngine * engine = [[FlutterEngine alloc] initWithName:@"flutterEngin"];
    if (engine.run) {
      _flutterEngine = engine;
    }
  }
  return _flutterEngine;
}

Flutter 端代码

class _MyHomePageState extends State<MyHomePage> {
  String batteryLevel = '0%';
  //界说通道
  final MethodChannel _methodhannel =
      const MethodChannel('com.pages.your/native_get');
  @override
  void initState() {
    super.initState();
     //Flutter端监听发送过来的数据
    _methodhannel.setMethodCallHandler((call) {
      if (call.method == 'EnterFlutter') {
        print(call.arguments);
      } else if (call.method == 'BatteryLevel') {
        batteryLevel = call.arguments;
      }
      setState(() {});
      return Future(() {});
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
              onPressed: () {
                //发送音讯给原生
                _methodhannel.invokeListMethod('exit');
              },
              child: Text('回来'),
            ),
            ElevatedButton(
              onPressed: () {
              //发送音讯给原生
                _oneChannel.invokeListMethod('getBatteryLevel');
              },
              child: Text('当时电量${batteryLevel}'),
            ),
          ],
        ),
      ),
    );
  }
}

二、BasicMessageChannel的运用

它是能够双端通讯的,Flutter 端能够给 iOS 发送音讯,iOS 也能够给 Flutter 发送音讯。这段代码完成了在 Flutter 中的 TextField 输入文字,在 iOS 端能及时输出。

原生端代码

需求在上面代码的基础上添加 MessageChannel ,并接纳音讯和发送音讯

@property (nonatomic, strong) FlutterBasicMessageChannel *messageChannel;
self.messageChannel = [FlutterBasicMessageChannel messageChannelWithName:@"messgaeChannel" binaryMessenger:self.flutterVC.binaryMessenger];
[self.messageChannel setMessageHandler:^(id _Nullable  message, FlutterReply _Nonnull callback) {
    NSLog(@"收到Flutter的:%@",message);
  }];

Flutter 端代码

//需求创立和iOS端相同称号的通道
final messageChannel =
    const BasicMessageChannel("messgaeChannel", StandardMessageCodec());

监听音讯

messageChannel.setMessageHandler((message) {
  print('收到来自iOS的$message');
  return Future(() {});
});

发送音讯

messageChannel.send(str);

三、EventChannel的运用

只能是原生发送音讯给 Flutter 端,例如监听手机电量改变,网络改变,传感器等。

我这里在原生端完成了一个定时器,每隔一秒发送一个音讯给 Flutter 端,模仿这个功用。

原生端代码

记得地点的类要完成这个协议 FlutterStreamHandler

//界说属性
//通道
@property (nonatomic, strong) FlutterEventChannel *eventChannel;
//事情回调
@property (nonatomic, copy) FlutterEventSink events;
//用于计数
@property (nonatomic, assign) NSInteger count;
//初始化通道
self.eventChannel = [FlutterEventChannel eventChannelWithName:@"eventChannel" binaryMessenger:self.flutterVC.binaryMessenger];
[self.eventChannel setStreamHandler:self];
//调用创立定时器
[self createTimer];
//创立定时器
- (void)createTimer {
  NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector: @selector(timeStart) userInfo:nil repeats:YES];
}
//发送音讯
- (void)timeStart{
  self.count += 1;
  NSDictionary *dic = [NSDictionary dictionaryWithObject:@(self.count) forKey:@"count"];
  if (self.events != nil) {
    self.events(dic);
  }
}
//代表通道已经建好,原生端能够发送数据了
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(FlutterEventSink)eventSink {
  self.events = eventSink;
  return nil;
}
//代表Flutter端不再接纳
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments {
  self.events = nil;
  return nil;
}

Flutter 端代码

//创立通道
final EventChannel eventChannel = const EventChannel('eventChannel');
//开始监听数据
eventChannel.receiveBroadcastStream().listen((event) {
  print(event.toString());
});

以上就是iOS原生和Flutter通讯的三种办法,音讯传递是异步的,这确保了用户界面在音讯传递时不会被挂起。

Mac Flutter环境配置及Android Studio的运用详解过程

Flutter 常用快捷键

Flutter 页面生命周期和 Widget 生命周期详解

Flutter-Package插件的开发、发布、运用

如果我们想要检查 Flutter 引擎底层源码,就需求用到 Ninja 编译, Ninja 的安装过程能够点击检查

欢迎关注、点赞及转发。