1.嵌入Flutter
先创建ios工程
我们先创建NativeDemo ios工程然后,导入cocopad通过配置pod文件来引入Flutter
框架。
flutter_app_path = '../flutter_module'
load File.join(flutter_app_path,'.iOS','Flutter','podhelper.rb')
platform :ios, '11.0'
post_install do |installer|
flutter_post_install(installer)
end
target 'NativeDemo' do
install_all_flutter_pods(flutter_app_path)
use_frameworks!
end
创建flutter_module工程
,用于原生引入调用:
在来看ios原生如何嵌入Flutter页面:
#import <Flutter/Flutter.h>
@property(nonatomic, strong) FlutterEngine * flutterEngine;
@property(nonatomic, strong) FlutterViewController* flutterVc;
@property(nonatomic, strong) FlutterBasicMessageChannel* msgChannel;
- (IBAction)pushFlutter:(id)sender {
//告诉Flutter显示one_page
FlutterMethodChannel * methodChannel = [FlutterMethodChannel methodChannelWithName:@"one_page" binaryMessenger:self.flutterVc];
[methodChannel invokeMethod:@"one" arguments:nil];
//弹出Flutter页面
[self presentViewController:self.flutterVc animated:YES completion:nil];
//监听Flutter页面的回调
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
//如果是要退出
if([call.method isEqualToString:@"exit"]){
[self.flutterVc dismissViewControllerAnimated:YES completion:nil];
}
}];
}
- (IBAction)pushFlutterTwo:(id)sender {
//告诉Flutter显示one_page
FlutterMethodChannel * methodChannel = [FlutterMethodChannel methodChannelWithName:@"two_page" binaryMessenger:self.flutterVc];
[methodChannel invokeMethod:@"two" arguments:nil];
//弹出Flutter页面
[self presentViewController:self.flutterVc animated:YES completion:nil];
//监听Flutter页面的回调
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
//如果是要退出
if([call.method isEqualToString:@"exit"]){
[self.flutterVc dismissViewControllerAnimated:YES completion:nil];
}
}];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.flutterVc = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
self.msgChannel = [FlutterBasicMessageChannel messageChannelWithName:@"messageChannel" binaryMessenger:self.flutterVc];
[self.msgChannel setMessageHandler:^(id _Nullable message, FlutterReply _Nonnull callback) {
NSLog(@"收到Flutter的%@",message);
}];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
static int a = 0;
[self.msgChannel sendMessage:[NSString stringWithFormat:@"%d",a++]];
}
然后我们在看flutter_module工程代码:
void initState() {
_messageChannel.setMessageHandler((message) {
print('收到来自iOS的$message');
return Future(() {});
});
super.initState();
_oneChannel.setMethodCallHandler((call) {
pageIndex = call.method;
print(call.method);
setState(() {});
return Future(() {});
});
_twoChannel.setMethodCallHandler((call) {
pageIndex = call.method;
print(call.method);
setState(() {});
return Future(() {});
});
}
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: _rootPage(pageIndex),
);
}
_rootPage(String pageIndex) {
switch (pageIndex) {
case 'one':
return Scaffold(
appBar: AppBar(
title: Text(pageIndex),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_oneChannel.invokeMapMethod('exit');
},
child: Text(pageIndex),
),
TextField(
onChanged: (String str) {
_messageChannel.send(str);
},
)
],
),
);
case 'two':
return Scaffold(
appBar: AppBar(
title: Text(pageIndex),
),
body: Center(
child: ElevatedButton(
onPressed: () {
_twoChannel.invokeMapMethod('exit');
},
child: Text(pageIndex),
),
),
);
default:
return Scaffold(
appBar: AppBar(
title: const Text('default'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
_twoChannel.invokeMapMethod('exit');
},
child: Text(pageIndex),
),
),
);
}
}
}
运行效果:
下面是理论介绍:
2.Flutter Channel
Flutter 作为一个灵活的UI框架。无论是ios平台上的Objective-C或Swift,还是Android平台上的Java或Kotlin都可以通过Platform Channel
机制来与Flutter进行通讯。需要注意的是Platform Channel
不依赖代码生成,而是建立在消息传递方式上。实际上,它的工作模式和原理非常类似基于二进制协议开发的网络服务。
iOS Channel 原理
Flutter 提供了三种Channel
用作Flutter与iOS原生平台之间的数据传递:
1.FlutterBasicMessageChannel
:用作字符串和半结构化的数据传递。
**结构化数据:**: 包括预定义的数据类型、格式和结构的数据,常见的比如关系型数据库中数据表里的数据。
**半结构化数据:**: 具有可识别的模式并可以解析的文本数据文件,比如XML数据文件。
**非结构化数据:**: 没有固定结构的数据,通常保存为不同类型的文件,比如文本文档、图片、视频等。
2.FlutterMethodChannel
:用来调用方法(method invocation),包括从 Flutter向原生平台发起方法调用,也支持从原生平台Flutter发起方法调用。
3.FlutterEventChannel
:用来支持数据流(streams)通信。
三种 Channel 分别带来不同的作用。但是在设计上大同小异。都有以下三个成员变量:
- name:Channel 名称 作为每一个Channel的唯一标志。
在我们的Flutter 应用中,通常会存在多个 Platform Channel。 那么这些 Channel 之间就是通过唯一标志name 来区分。 例如,使用FlutterMethodChannel
发起方法调用时,就需要我们为MethodChannel指定对应name。 - messenger:消息信使(BinaryMessenger)
用作消息的发送和接收的工具,主要负责Flutter与原生之间的相互通讯
。
通俗来讲, messenger就是咱们现在的外卖小哥。messenger负责把数据从 Flutter 送到iOS平台,或者从iOS传输数据到Flutter。尽管Flutter中存在三种不同用途的Channel, 但是对应的沟通工具都是 BinaryMessenger
。
在创建一个 Channel 后,不论是通过设置代理 (Delegate),还是通过 setXXXHandler:来进行消息处理。最终会为该 Channel 绑定一个 FlutterBinaryMessageHandler
。 并以 Channel的name作为key , 保存在一个 Map 结构中。 当接受到发送消息后,会根据消息中携带的 Channel 名称取出对应 FlutterBinaryMessageHandler, 并交由BinaryMessenger处理。
注意:在 iOS平台BinaryMessenger 是一个名为 FlutterBinaryMessenger
的协议。
3 .Codec (编解码器)
在 Channel 中,messenger 携带的数据需要在 Dart 层, Native (iOS/Android平台)层中传输,所以就需要一种与平台无关的数据协议。既能支持图片,又能支持文件等资源。因此官方最终采用了二进制字节流
作为数据传输协议。
二进制字节流: 发送方法需要把数据编码成二进制数据,接收方再把数据解码成原始数据。而负责编解码操作的就是 Codec。
在 Flutter 中有两种 Codec:
MessageCodec:对message进行编解码 MessageCodec 用于二进制数据与基础数据之间的编解码,其中 FlutterBasicMessageChannel
中采用的就是该Codec。
//用于实现二进制数据NSData和不同类型数据之间的转换
@protocol FlutterMessageCodec
+(instancetype)sharedInstance;
//将指定的类型message编码为二进制数据
- (NSData *_nullable)encode:(id _nullable)message;
//将二进制数据NSData解码成指定类型
-(id _nullable)decode:(NSData* _nullabel)message;
@end
在 Flutter 中,MessageCodec有多种实现:
-
FlutterStandardMessageCodec
:是FlutterBasicMessageChannel 中默认使用的编解码器。(底层是用FlutterStandardReaderWriter 实现的)。用于数据类型和二进制数据之间的编解码。支持基础数据类型包(bool、char、double、float、int、long、short、String、Array、Dictionary)以及二进制数据。 -
FlutterBinaryCodec
:用于二进制数据和二进制数据之间的编解码,在实现上只是原封不动的将接收到的二进制数据返回。 -
FlutterStringCodec
:用于字符串与二进制数据之间的编解码,对于字符串采用 UTF-8编码格式。 -
FlutterJSONMessageCodec
:用于数据类型与二进制数据之间的编解码,支持基础数据类型(bool、char、double、float、int、long、short、String、Array、Dictionary)。在iOS端使用 NSJSONSerialization作为序列化工具。
FlutterMethodCodec:对 FlutterMethodCall编解码
FlutterMethodCodec
用于二进制数据与方法调用(FlutterMethodCall)和返回结果之间的编解码。主要用在FlutterMethodChannel 和 FlutterEventChannel中。
@protocol FlutterMethodCodec
+(instancetype)sharedInstance;
//将FlutterMethodCall编码为二进制NSData
-(NSData *)encodeMethodCall:(FlutterMethodCall*)methodCall;
//将二进制NSData methodCall解码为FlutterMethodCall
- (FlutterMethodCall *)decodeMethodCall:(NSData*)methodCall;
//将正常响应结果result编码为二进制
-(NSData*)encodeSuccess Envelope:(id _nullable)result;
//将错误响应提示编码为二进制NSData
-(NSData*)encodeErrorEnvelope:(FlutterError*)error;
//将二进制数据NSData解码,失败返回FlutterError
-(id _nullable)decodeEnvelope:(NSData*)envelope;
FlutterMethodCall 代表从 Flutter端发起的方法调用。方法调用包括:方法名、方法参数以及方法返回结果。因此和FlutterMessageCodec 相比,FlutterMethodCodec 中多了两个处理调用结果的方法:
- 方法调用成功:使用 encodeSuccessEnvelope:编码result。
- 方法调用失败:使用encodeErrorEnvelope:编码FlutterError。
decodeEnvelope
:方法则用与解码iOS平台代码调用Dart中方法的结果。比如通过FlutterMethodChannel调用Flutter中的方法,且获得其返回结果。