1. BasicMessageChannel 简介
BasicMessageChannel
是以发送消息的形式传递数据, 类似于 iOS 的通知, channel 一旦建立, 消息的监听处理是持续性的, 可以随时收发, 属于双向通信, 可以 iOS
端主动调用,也可以 Flutter
主动调用, 并且自在发送消息的同时还可以设置一个回调监听, 收到消息之后通过回调给消息发送方一个结果(返回值).
本文的代码是在上一篇文章iOS 与 Flutter 通信之 MethodChannel的基础上做了相应的修改, 去掉一部分多余的, 加上最新的 BasicMessageChannel
相关的代码, 具体完整代码在最后已经给出.
2. 使用场景
2.1 iOS 发送 Message
2.1.1 场景描述
Flutter
模块初始化之后, 点击屏幕, 给 flutter
端发送一个自增的数字. flutter
收到数据之后, 在控制台输出收到的数字, 并通过回调函数返回给 iOS
一个信息, iOS
端在控制台输出 flutter
回传的信息.
2.1.1 场景效果
2.2 Flutter 发送 Message
2.2.1 场景描述
弹出 flutter
页面, 在 flutter
页面的输入框中输入内容, 在输入框内容发生变化的监听中, 给 iOS
端发送现有内容, iOS
端收到数据之后, 在控制台输出收到的消息, 并通过回调函数返回给 flutter
一个信息, flutter
端在控制台输出 iOS
回传的信息.
2.2.1 场景效果
3. 使用方法
Flutter
与 iOS
对消息的发送
和监听
是对应关系, iOS 发送一个 Message 同时可以设置一个等待返回结果的 block, 也可以不设置,根据自己的具体需求而定, Flutter 端在收到消息之后, 对消息进行相应的处理, 同时可以通过回调给 iOS 端一个反馈或者返回一些数据; 反过来也是一样的, 下面让我们来看具体的对应关系.
iOS 的回调监听, 对应 Flutter 的返回值, 没有具体对应要求, Flutter 没有给返回值, iOS 监听了, 那就是无用功, 反之亦然, 所以还要根据我的们具体需求去设计.
3.1 BasicMessageChannel 初始化
保证两端 channel
名称一致. Flutter 端需要设置一个消息解码器参数.
3.1.1 iOS 初始化
self.msgChannel = [FlutterBasicMessageChannel messageChannelWithName:@"messageCahnnel" binaryMessenger:self.flutterVC.binaryMessenger];
3.1.2 flutter 初始化
final BasicMessageChannel _messageChannel = const BasicMessageChannel('messageCahnnel', StandardMessageCodec());
3.2 iOS 发送 Message
3.2.1 iOS 发送 Message
Message
: id 类型, 要发送出去的消息.reply
: block, 监听回调.reply
: id 类型, block 回调参数, flutter 传回来的消息.
没有回调监听的
static int a = 0;
[self.msgChannel sendMessage:[NSString stringWithFormat:@"%d", a++]];
有回调监听的
static int a = 0;
[self.msgChannel sendMessage:[NSString stringWithFormat:@"%d", a++] reply:^(id _Nullable reply) {
NSLog(@"%@", reply);
}];
3.2.2 Flutter 接收 Message
handler
: 逆名函数message
: 要发送出去的消息.- 返回值: 需要给 iOS 回调数据就给一个返回值, 不需要就不给返回值, 但是要加上
async
, 具体位置看代码.
不给 iOS 端返回值的
_messageChannel.setMessageHandler((message) async {
print('收到了来自 ios 的 message: $message');
});
给 iOS 端返回值的
_messageChannel.setMessageHandler((message) {
print('收到了来自 ios 的 message: $message');
return Future.value('flutter 已经收到了 $message');
});
3.3 Flutter 发送 Message
3.3.1 Flutter 发送 Message
onChanged
: 是输入框的监听方法, 输入框内容发生变化时调用.str
: 要发送的消息result
: 返回值, iOS 收到消息后, 回调回来的值.
没有回调监听的
onChanged: (String str) {
_messageChannel.send(str);
},
有回调监听的
onChanged: (String str) async {
var result = await _messageChannel.send(str);
print(result);
},
3.3.2 iOS 接收 Message
没有回调监听的
[self.msgChannel setMessageHandler:^(id _Nullable message, FlutterReply _Nonnull callback) {
NSLog(@"收到了来自 flutter 的 message: %@", message);
}];
有回调监听的
[self.msgChannel setMessageHandler:^(id _Nullable message, FlutterReply _Nonnull callback) {
NSLog(@"收到了来自 flutter 的 message: %@", message);
callback(@"ios 已经收到了 flutter 输入的内容");
}];
4. 总结
BasicMessageChannel 通信特点:
- 用于二者互相发送消息, 可接收返回值.
- 双向通信
- 可持续性通信
5. 完整代码
5.1 iOS 原生代码
#import "ViewController.h"
#import <Flutter/Flutter.h>
@interface ViewController ()
@property (nonatomic, strong) FlutterEngine *flutterEngine;
@property (nonatomic, strong) FlutterViewController *flutterVC;
@property (nonatomic, strong) FlutterBasicMessageChannel *msgChannel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
FlutterViewController *vc = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
// 全屏时 flutter 页面就需要写回调来告诉原生 dismiss
vc.modalPresentationStyle = UIModalPresentationFullScreen;
self.flutterVC = vc;
self.msgChannel = [FlutterBasicMessageChannel messageChannelWithName:@"messageCahnnel" binaryMessenger:self.flutterVC.binaryMessenger];
[self.msgChannel setMessageHandler:^(id _Nullable message, FlutterReply _Nonnull callback) {
NSLog(@"收到了来自 flutter 的 message: %@", message);
callback(@"ios 已经收到了 flutter 输入的内容");
}];
}
- (FlutterEngine *)flutterEngine {
if (!_flutterEngine) {
FlutterEngine *flutterEngine = [[FlutterEngine alloc] initWithName:@"andy"];
if ([flutterEngine run]) {
_flutterEngine = flutterEngine;
}
}
return _flutterEngine;
}
// one page
- (IBAction)pushFlutter:(id)sender {
// 初始化 FlutterMethodChannel
FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"channel_1" binaryMessenger:self.flutterVC.binaryMessenger];
[methodChannel invokeMethod:@"one" arguments: nil];
[self presentViewController:self.flutterVC animated:YES completion:nil];
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
if ([call.method isEqual:@"dismiss"]) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
static int a = 0;
[self.msgChannel sendMessage:[NSString stringWithFormat:@"%d", a++] reply:^(id _Nullable reply) {
NSLog(@"%@", reply);
}];
}
@end
5.2 Flutter 代码
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String pageIndex = '';
final MethodChannel _channel_1 = const MethodChannel('channel_1');
final BasicMessageChannel _messageChannel = const BasicMessageChannel('messageCahnnel', StandardMessageCodec());
@override
void initState() {
// TODO: implement initState
super.initState();
_messageChannel.setMessageHandler((message) {
print('收到了来自 ios 的 message: $message');
return Future.value('flutter 已经收到了 $message');
});
_channel_1.setMethodCallHandler((call) async {
setState(() {
if (call.method == 'one') {
pageIndex = call.method;
}
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Module',
theme: ThemeData(
primarySwatch: Colors.blue
),
home: _rootPage(pageIndex),
);
}
Widget _rootPage(String pageIndex) {
switch(pageIndex) {
case 'one':
return Scaffold(
appBar: AppBar(
title: Text(pageIndex),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text(pageIndex),
onPressed: () {
_channel_1.invokeMethod('dismiss');
},
),
TextField(
onChanged: (String str) async {
var result = await _messageChannel.send(str);
print(result);
},
)
],
)
);
}
return Scaffold(
appBar: AppBar(
title: const Text('hello flutter'),
),
body: const Center(
child: Text('hello flutter'),
),
);
}
}