Flutter基礎系列之混合開發(二)

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) ], ), ) ); } }
View Code

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]; } }]; }
相關文章
相關標籤/搜索