注册

Flutter 与原生通信的三种方式

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。

截屏2021-11-27 下午3.09.28.png



  • 然后在 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>

一、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通信的三种方式,消息传递是异步的,这确保了用户界面在消息传递时不会被挂起。


作者:周糖果
链接:https://juejin.cn/post/7035211019618091045
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册