1.混合开发的场景
1.1做为独立页面加入
这是以页面级做为独立的模块加入,而不是页面的某个元素。html
- 原生页面能够打开Flutter页面
- Flutter页面能够打开原生页面
1.2做为页面的一部分嵌入
好比说原生页面中只有某一个item是Flutter;android
- Flutter页面中只有某一部分是原生视图
2.Flutter混合开发的集成步骤
2.1建立Flutter Module
在作混合开发以前,咱们首先须要建立一个Flutter Module。ios
这里建议Flutter Module的建立目录和原生工程的目录同级。假设Native的目录是这样的:xxx/Native项目。git
cd xxx/ flutter create -t module flutter_module
能够看到生成的flutter_module目录下有这些文件:github
README.md pubspec.lock flutter_module.iml pubspec.yaml flutter_module_android.iml test .android lib .ios
上面的.android和.ios目录,是隐藏文件, 也是这个flutter_module的宿主工程。由于有宿主工程的存在,这个flutter_module在不添加额外配置的状况下是能够独立运行的:json
- .android:flutter_module的Android宿主工程;
- .ios:flutter_module的iOS宿主工程;
- lib:flutter_module的Dart部分的代码;
- pubspec.yaml:flutter_module的项目依赖配置文件。
2.2添加Flutter Module依赖:为原生项目添加Flutter的依赖
官方解决方案:https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-appsxcode
2.2.1为已存在的iOS原生项目添加Flutter Module依赖
【说明】:在原生项目中添加Flutter Module,须要配置好CocoaPods到工程中。若是没有使用CocoaPods的,能够参考http://www.javashuo.com/article/p-fdprzbzd-ky.html进行配置。服务器
第一步:在Podfile文件中添加依赖:网络
flutter_application_path = "../flutter_module" eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
第二步:安装依赖:app
在项目的根目录中,执行以下指令:
pod install
第三步:禁用Bitcode:
目前Flutter还不支持Bitcode,因此集成了Flutter的iOS项目须要禁用Bitcode。
第四步:添加Build Phase来构建Dart代码。
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
添加完以后,要将这个Run Script拖动到Target Dependencies phase下面,接下来就能够运行项目了。
2.2.2为已存在的Android应用添加Flutter Module依赖
第一步:配置Android项目的Flutter Module依赖:打开Android项目的settings.gradle添加以下代码。这段脚本的做用是让Flutter做为一个单独的模块打包进来。
//HybridAndroid/settings.gradle setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir.parentFile, 'flutter_module/.android/include_flutter.groovy' ))
setBinding和evaluate容许Flutter模块包括它本身在内的任何Flutter插件,在settings.gradle中以相似::flutter、package_info、:video_player的方式存在。
第二步:添加:flutter依赖。
//app/build.gradle //... dependencies { //... implementation project(':flutter') }
在build.gradle中配置的时候,有两个地方要特别注意:
compileOptions { //编译须要设置成JAVA8 sourceCompatibility 1.8 targetCompatibility 1.8 } defaultConfig { minSdkVersion 16 //Flutter中要求最低SDK版本为16 //... }
2.3在Java/OC中调用Flutter Module
2.3.1OC中调用Flutter Module
在OC中调用Flutter Module有两种方式:
- 直接使用FlutterViewController的方式;
- 使用FlutterEngine的方式。
下面咱们分别来看一下这两种方式。
2.3.1.1直接使用FlutterViewController
FlutterViewController* flutterViewController = [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil]; [GeneratedPluginRegistrant registerWithRegistry:flutterViewController]; //若是使用了插件 [flutterViewController setInitialRoute:@"myApp"]; [self.navigationController pushViewController:flutterViewController animated:YES];
经过上面的代码,咱们能够看到setInitialRoute方法传递了参数“myApp”,该参数用于告诉Dart代码显示哪一个Flutter视图。在Flutter Module的main.dart文件中,须要经过window.defaultRouteName来获取Native指定要显示的路由名,以肯定要建立哪一个窗口小部件并传递给runApp:
void main() => runApp(_widgetForRoute(window.defaultRouteName)); Widget _widgetForRoute(String route) { switch (route) { case 'myApp': return MyApp(); default: return MaterialApp( home: Center( child: Text('没找到'), ), ); } }
2.3.1.2使用FlutterEngine的方式
第一步:须要AppDelegate继承自FlutterAppDelegate
//AppDelegate.h #import <UIKit/UIKit.h> #import <Flutter/Flutter.h> @interface AppDelegate : FlutterAppDelegate @property (strong, nonatomic) FlutterEngine *flutterEngine; @end //AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //FlutterEngine初始化 self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil]; [self.flutterEngine runWithEntrypoint:nil]; [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine]; //有插件 //设置RootVC self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; UIViewController *vc = [[ViewController alloc] init]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; self.window.rootViewController = nav; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; }
第二步:经过FlutterEngine来初始化FlutterViewController。
FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine]; FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil]; [self.navigationController pushViewController:flutterViewController animated:YES];
由于在AppDelegate中,咱们已经提早初始化了FlutterEngine,因此这种方式打开一个Flutter模块的速度,比第一种方式要快一些。
【注意】:使用FlutterEngine方式,调用 setInitialRoute 方法会无效,在Flutter端拿到的永远是“I”,这是Flutter SDK的一个BUG,所以若是必须依赖 setInitialRoute 参数,那么只能使用方式一进行赋值。
2.3.2Java中调用Flutter Module
在Java中调用Flutter Module有两种方式:
- 使用Flutter.createView API的方式;
- 使用FlutterFragment的方式。
2.3.2.1使用Flutter.createView API的方式
fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { View flutterView = Flutter.createView( MainActivity.this, getLifecycle(), "myApp" ); FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800); layout.leftMargin = 100; layout.topMargin = 200; addContentView(flutterView, layout); } });
2.3.2.2FlutterFragment的方式
fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { FragmentTransaction tx = getSupportFragmentManager().beginTransaction(); tx.replace(R.id.someContainer, Flutter.createFragment("myApp")); tx.commit(); } });
上面都使用了字符串“myApp”来告诉Dart代码,在Flutter视图中显示哪一个widget。在Flutter项目中能够经过 window.defaultRouteName 来获取Native传过来的“myApp”字符串,以肯定要建立哪一个widget并传递给runApp。
2.4编写Dart代码


import 'package:flutter/material.dart'; import 'dart:ui'; import 'package:flutter/services.dart'; void main() => runApp(_widgetForRoute(window.defaultRouteName)); Widget _widgetForRoute(String route) { switch (route) { case 'myApp': return new MyApp(); default: return Center( child: Text('Unknown route: $route', textDirection: TextDirection.ltr), ); } } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter 混合开发'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { static const platform = const MethodChannel('gof.flutter.io/battery'); String _batteryLevel = 'Unknown battery level.'; Future<Null> _getBatteryLevel() async { String batteryLevel; try { final int result = await platform.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch(e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { _batteryLevel = batteryLevel; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ RaisedButton( child: Text('Get Battery Level'), onPressed: _getBatteryLevel, ), Text(_batteryLevel) ], ), ) ); } }
2.5运行项目
代码编写完以后,就能够运行项目了。
2.6热重启/从新加载
咱们知道,在作纯Flutter开发的时候,它带有热重启/从新加载的功能。可是,在混合开发中,是在原生工程中集成了Flutter项目,这时热重启/从新加载的功能失效了,那么咱们怎样启用混合开发中的热重启/从新加载功能呢?
- 第一步:打开一个模拟器,或者链接一个设备到电脑上;
- 第二步:关闭咱们的app,而后运行指令 “flutter attach”。
【注意】:执行“flutter attach”指令,有可能遇到以下报错:
NoSuchMethodError: NoSuchMethodError: The getter 'port' was called on null. Receiver: null Tried calling: port
【解决方案】:https://github.com/flutter/flutter/issues/32471
flutter channel master flutter upgrade
若是有多个模拟器或设备,那么须要使用以下指令来使用热重启/从新加载:
flutter attach -d BD1389B9-FF73-4114-96E9-4EE9A572A2AE
[注意]:热重启/从新加载,须要原生工程是debug模式。
2.7调试Dart代码
上一节讲了在混合工程中使用热重启/从新加载,一样的,咱们是否可以调试咱们的Dart代码呢? 答案是确定的,只须要以下两个步骤:
- 第一步:关闭咱们的app;
- 第二步:点击Android Studio的Flutter Attach按钮(须要安装flutter和dart插件);
- 第三步:启动咱们的app。
接下来就能够像调试普通Flutter项目同样来调试混合开发模式的Dart代码了。
[注意]:调试Dart代码,须要原生工程是debug模式。
2.8发布应用
2.8.1发布iOS应用
发布iOS应用,首先须要一个99美圆的我的开发者帐号用于将app上传到App Store,或者是299美圆的企业级帐号用于将App发布到公司本身的服务器或者第三方服务器上。
接下来,大概就是这几个步骤:申请AppID -> 在iTunes Connect建立应用 -> 打包程序 -> 将应用提交到App Store。
2.8.2发布Android应用
发布Android应用,主要有两大步骤:签名打包 -> 发布到各个Store。
那么如何签名打包一个Flutter开发的App呢?
第一步:生成Android签名证书。签名APK须要一个证书用于为APP签名,生成签名证书能够在Android Studio中,以可视化的方式生成,也可使用终端命令的方式生成。
第二步:设置gradle变量。
- 将你的签名证书拷贝到android/app目录下;
- 编辑~/.gradle/gradle.properties或../android/gradle.properties(一个是全局gradle.properties,一个是项目中的gradle.properties),加入以下代码:
- 在gradle配置文件中添加签名配置。编辑android/app/build.gradle文件,添加以下代码:
- 签名打包APK。终端进入android目录,运行以下代码:
./gradlew assembleRelease
3.Flutter与Native通讯机制
在讲解Flutter与Native之间是如何传递数据以前,咱们先来了解一下它们的通讯机制,Flutter与Native的通讯是经过Channel来完成的。
消息使用Channel(平台通道)在Flutter和Native之间传递,以下图所示:
平台所支持的数据类型以下表所示:
Flutter定义了三种不一样类型的Channel:
- BasicMessageChannel:用于传递字符串和半结构化的信息,持续通讯,收到消息后能够回复这次消息。例如:Native将遍历到的文件信息陆续传递到Dart;Flutter将服务端获取的数据交给Native加工,Native处理完以后返回。
- MethodChannel:用于传递方法调用,一次性通讯。例如:Flutter调用Native拍照。
- EventChannel:用于数据流的通讯,持续通讯,收到消息后没法回复这次消息,一般用于Native向Dart的通讯。例如:手机电量变化,网络链接变化,陀螺仪,传感器等。
这三种类型的Channel都是全双工通讯,即A <=> B,Dart能够主动发送消息到Native端,而且Native接收消息后能够作出回应。一样地,Native端也能够主动发送消息到Dart端,Dart端接受消息后返回给Native端。
3.1Flutter与iOS通讯开发指南
如今咱们来看看上面三种类型的Channel,在iOS端是怎么实现的。
3.1.1BasicMessageChannel
咱们先看一下iOS端该Channel的构造函数:
+ (instancetype)messageChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger codec:(NSObject<FlutterMessageCodec>*)codec;
- name:channel的名称,也是惟一标识符;
- messenger:消息信使,是消息发送和接收的工具;
- codec:消息的编解码器,它有几种不一样类型的实现:
-
- BinaryCodec:最为简单的一种Codec,由于其返回值类型和入参的类型相同,都是二进制格式(Android平台为ByteBuffer,iOS平台为NSData)。实际上,FlutterBinaryCodec在编解码过程当中什么都没作,只是原封不动将二进制数据消息返回。能够在这种状况下使用:传递内存数据块时,在编解码阶段免于内存拷贝。
- FlutterBinaryCodec:是FlutterBinaryMessenger的默认编解码器,其支持基础数据类型、二进制数据、列表、字典。
-
- FlutterStringCodec:用于字符串和二进制数据之间的编解码,其编码格式为UTF-8;
-
- FlutterJSONMessageCodec:用于基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表、字典。其在iOS端使用了NSJSONSerialization做为序列化的工具,而在Android端则使用了其自定义的JSONUtil和StringCodec做为序列化工具。
在建立好BasicMessageChannel以后,若是要让其接收到来自Dart端主动发出的消息,则须要设置它的setMessageHandler方法为其设置一个消息处理器:
- (void)setMessageHandler:(FlutterMessageHandler _Nullable)handler;
- handler:消息处理器,配合BinaryMessenger完成消息的处理。
FlutterMessageHandler的定义以下:
//message:消息内容 //callback:回复消息的回调函数 typedef void (^FlutterMessageHandler)(id _Nullable message, FlutterReply callback);
若是要给Dart发送消息,能够调用以下方法:
- (void)sendMessage:(id _Nullable)message; - (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback;
- message:要传递给Dart的信息;
- callback:消息发出去后,收到Dart回复的回调函数。
主动发送数据到Dart和接收来自Dart的消息的代码,能够参考:
- (void)initMessageChannel{ self.messageChannel = [FlutterBasicMessageChannel messageChannelWithName:@"BasicMessageChannelPlugin" binaryMessenger:self.flutterViewController codec:[FlutterStringCodec sharedInstance]]; __weak typeof(self)weakSelf = self; //设置消息处理器,处理来自Dart主动发出的消息 [self.messageChannel setMessageHandler:^(NSString* message, FlutterReply reply) { reply([NSString stringWithFormat:@"BasicMessageChannel收到:%@",message]); [weakSelf sendShow:message]; }]; } //使用FlutterBasicMessageChannel发送数据 [self.messageChannel sendMessage:self.tfInput.text reply:^(id _Nullable reply) { if (reply != nil) { [self sendShow:reply]; } }];
接下来咱们看一下Dart端的实现:
仍是先看构造函数:
const BasicMessageChannel(this.name, this.codec);
- name:Channel的名字,要和Native端保持一致;
- codec:消息的编解码器,要和Native端保持一致,有四种类型的实现。
建立好BasicMessageChannel以后,若是要让其接收来自Native端主动发出的消息,则须要调用它的setMessageHandler方法为其设置一个消息处理器。
void setMessageHandler(Future<T> handler(T message))
- handler:消息处理器,配合BinaryMessenger完成消息的处理。
若是要主动给Native发送消息,能够调用send方法:
Future<T> send(T message)
- message:要传递给Native的消息;
- 返回值:消息发出去后,收到Native回复的回调函数。
主动发送数据到Native和接收来自Native的消息的代码,能够参考:
static const BasicMessageChannel<String> _basicMessageChannel = const BasicMessageChannel('BasicMessageChannelPlugin', StringCodec()); //使用BasicMessageChannel接收来自Native主动发出的消息,并向Native回复 _basicMessageChannel.setMessageHandler((String message) => Future<String>(() { setState(() { showMessage = 'BasicMessageChannel:'+message; }); return "收到Native的消息:" + message; })); //使用BasicMessageChannel向Native发送消息,并接收Native的回复 String response; try { response = await _basicMessageChannel.send(value); } on PlatformException catch (e) { print(e); }
3.1.2FlutterMethodChannel
咱们仍是先从iOS端的构造函数看起:
//建立FlutterStandardMethodCodec类型的codec + (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger; + (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger codec:(NSObject<FlutterMethodCodec>*)codec;
- name:Channel名字,也是惟一标识符;
- messenger:消息信使,消息发送和接收的工具;
- codec:用做MethodChannel的编解码器。
在建立好MethodChannel以后,须要设置一个消息处理器,以便可以接收来自Dart端主动发出的消息:
- (void)setMethodCallHandler:(FlutterMethodCallHandler _Nullable)handler;
- handler:消息处理器,配合BinaryMessenger完成消息的处理。
FlutterMethodCallHandler的定义以下:
typedef void (^FlutterMethodCallHandler)(FlutterMethodCall* call, FlutterResult result);
- call:消息内容,它有两个成员变量:字符串类型的call.method表示调用的方法名;id类型的call.arguments表示调用方法所传的参数。
- result:回复此消息的回调函数。
iOS端的具体使用:
- (void)initMethodChannel{ self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"MethodChannelPlugin" binaryMessenger:self.flutterViewController]; __weak typeof(self)weakSelf = self; [self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) { if ([@"send" isEqualToString:call.method]) { result([NSString stringWithFormat:@"MethodChannelPlugin收到:%@",call.arguments]);//返回结果给Dart); [weakSelf sendShow:call.arguments]; } }]; }
接下来看一下Dart端的实现:
MethodChannel的构造函数:
const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), this.binaryMessenger = defaultBinaryMessenger ])
- name:Channel的名字,和Native端保持一致;
- codec:消息的编解码器,默认是StandardMethodCodec,要和Native端保持一致;
建立好MethodChannel以后,若是要主动给Native端发送消息,能够调用 invokeMethod 方法:
Future<T> invokeMethod<T>(String method, [ dynamic arguments ])
- method:调用的方法名称,Native端要作对应;
- arguments:传递参数。
主动发送数据到Native的代码,能够参考:
//初始化 static const MethodChannel _methodChannelPlugin = const MethodChannel('MethodChannelPlugin'); //主动发送数据到Native String response; try { response = await _methodChannelPlugin.invokeMethod('send', value); } on PlatformException catch (e) { print(e); }
3.1.3EventChannel
先看iOS端的构造函数:
//建立一个FlutterStandardMethodCodec类型的EventChannel + (instancetype)eventChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger; + (instancetype)eventChannelWithName:(NSString*)name binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger codec:(NSObject<FlutterMethodCodec>*)codec;
- name:Channel名称,也是惟一标识符;
- messenger:消息信使,消息发送和接收的工具;
- codec:EventChannel的编解码器。
在建立好EventChannel以后,若是要让其接收Dart发来的消息,须要调用以下方法来设置一个消息处理器:
- (void)setStreamHandler:(NSObject<FlutterStreamHandler>* _Nullable)handler;
- handler:消息处理器,是一个协议,配合BinaryMessenger完成消息的处理。
@protocol FlutterStreamHandler //Native监听事件时调用 //arguments:传递的参数 //events:Native回调Dart时的回调函数,它提供success、error、endOfStream三个回调方法分别对应事件的不一样状态 - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(FlutterEventSink)events; //Flutter取消监听时调用 - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments; @end
iOS的具体使用:
- (void)initEventChannel{ self.eventChannel = [FlutterEventChannel eventChannelWithName:@"EventChannelPlugin" binaryMessenger:self.flutterViewController]; //设置消息处理器,处理来自Dart的消息 [self.eventChannel setStreamHandler:self]; } #pragma mark - <FlutterStreamHandler> //这个onListen是Flutter端开始监听这个channel时的回调,第二个参数 EventSink是用来传数据的载体 - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(FlutterEventSink)eventSink { //arguments flutter给native的参数 //回调给flutter,建议使用实例指向,由于该block可使用屡次 self.eventSink = eventSink; return nil; } //flutter再也不接收 - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments { // arguments flutter给native的参数 self.eventSink = nil; return nil; } //使用EventChannel发送数据 if (self.eventSink != nil) { self.eventSink(message); }
接下来看一下dart端的实现:
dart端主要是实现一个事件的监听,来接听来自Native端主动发送的数据,代码以下:
//初始化 static const EventChannel _eventChannelPlugin = EventChannel('EventChannelPlugin'); StreamSubscription _streamSubscription = _eventChannelPlugin .receiveBroadcastStream('123') .listen(_onToDart, onError: _onToDartError); //消息监听 void _onToDart(message) { setState(() { showMessage = message; }); } //错误 void _onToDartError(error) { print(error); }
3.2Flutter与Android通讯开发指南
略。
4.混合开发实战
4.1初始化时Native向Dart传递数据
Flutter容许咱们在初始化Flutter页面时,向Flutter传递一个string类型的 initialRoute 参数,从这个名字能够看出,它是用做路由名称的。既然是string类型的,那么咱们在初始化Flutter时,就能够很灵活的去应用了,好比说传一个json格式的字符串,传递更多参数给Flutter端。示例以下:
- (FlutterViewController *)flutterViewController { if (nil == _flutterViewController) { _flutterViewController = [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil]; [_flutterViewController setInitialRoute:@"{'name':'myApp','data':{'userId':'00001','userName':'LeeGof'}}"]; } return _flutterViewController; }
而后在Dart端经过以下方式接收参数:
void main() { String initParams = window.defaultRouteName; runApp(_widgetForRoute(initParams)); }
4.2Native到Dart的通讯(Native发送数据到Dart)
在Flutter中,Native向Dart传递消息能够经过 BasicMessageChannel 或 EventChannel 来实现。
首先咱们看一下 BasicMessageChannel 方式的实现。
- (void)initMessageChannel{ self.messageChannel = [FlutterBasicMessageChannel messageChannelWithName:@"BasicMessageChannelPlugin" binaryMessenger:self.flutterViewController codec:[FlutterStringCodec sharedInstance]]; __weak typeof(self)weakSelf = self; //设置消息处理器,处理来自Dart的消息 [self.messageChannel setMessageHandler:^(NSString* message, FlutterReply reply) { reply([NSString stringWithFormat:@"BasicMessageChannel收到:%@",message]); [weakSelf sendShow:message]; }]; } - (void)btnSend:(id)sender { [self.view endEditing:YES]; NSString *message = self.tfInput.text; if (message && message.length > 0) { if (self.isEventChannel) { //使用EventChannel发送数据 if (self.eventSink != nil) { self.eventSink(message); } } else { //使用FlutterBasicMessageChannel发送数据 [self.messageChannel sendMessage:message reply:^(id _Nullable reply) { if (reply != nil) { [self sendShow:reply]; } }]; } } }
EventChannel 方式和 BasicMessageChannel 方式相似:
- (void)initEventChannel{ self.eventChannel = [FlutterEventChannel eventChannelWithName:@"EventChannelPlugin" binaryMessenger:self.flutterViewController]; //设置消息处理器,处理来自Dart的消息 [self.eventChannel setStreamHandler:self]; } #pragma mark - <FlutterStreamHandler> //这个onListen是Flutter端开始监听这个channel时的回调,第二个参数 EventSink是用来传数据的载体 - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(FlutterEventSink)eventSink { //arguments flutter给native的参数 //回调给flutter,建议使用实例指向,由于该block可使用屡次 self.eventSink = eventSink; return nil; } //flutter再也不接收 - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments { // arguments flutter给native的参数 self.eventSink = nil; return nil; }
Dart端经过以下方式接收数据:
static const BasicMessageChannel<String> _basicMessageChannel = const BasicMessageChannel('BasicMessageChannelPlugin', StringCodec()); //使用BasicMessageChannel接受来自Native的消息,并向Native回复 _basicMessageChannel .setMessageHandler((String message) => Future<String>(() { setState(() { showMessage = 'BasicMessageChannel:' + message; }); return "收到Native的消息:" + message; })); static const EventChannel _eventChannelPlugin = EventChannel('EventChannelPlugin'); void _onToDart(message) { setState(() { showMessage = 'EventChannel:' + message; }); } void _onToDartError(error) { print(error); }
4.3Dart到Native的通讯(Dart发送数据到Native)
在Flutter中,Dart向Native传递消息能够经过 BasicMessageChannel 或 MethodChannel 来实现。
首先看一下Dart发送的相关代码:
String response; try { if (_isMethodChannelPlugin) { //使用BasicMessageChannel向Native发送消息,并接受Native的回复 response = await _methodChannelPlugin.invokeMethod('send', value); } else { response = await _basicMessageChannel.send(value); } } on PlatformException catch (e) { print(e); } setState(() { showMessage = response ?? ""; });
Native端接收:
- (void)initMessageChannel{ self.messageChannel = [FlutterBasicMessageChannel messageChannelWithName:@"BasicMessageChannelPlugin" binaryMessenger:self.flutterViewController codec:[FlutterStringCodec sharedInstance]]; __weak typeof(self)weakSelf = self; //设置消息处理器,处理来自Dart的消息 [self.messageChannel setMessageHandler:^(NSString* message, FlutterReply reply) { reply([NSString stringWithFormat:@"BasicMessageChannel收到:%@",message]); [weakSelf sendShow:message]; }]; } - (void)initMethodChannel{ self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"MethodChannelPlugin" binaryMessenger:self.flutterViewController]; __weak typeof(self)weakSelf = self; [self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) { if ([@"send" isEqualToString:call.method]) { result([NSString stringWithFormat:@"MethodChannelPlugin收到:%@",call.arguments]);//返回结果给Dart); [weakSelf sendShow:call.arguments]; } }]; }