“平臺(tái)特定”或“特定平臺(tái)”中的平臺(tái)指的就是Flutter應(yīng)用程序運(yùn)行的平臺(tái)脑题,如Android或iOS入挣。我們知道一個(gè)完整的Flutter應(yīng)用程序?qū)嶋H上包括原生代碼和Flutter代碼兩部分说铃。由于Flutter本身只是一個(gè)UI系統(tǒng)柔滔,它本身是無法提供一些系統(tǒng)能力佑惠,比如使用藍(lán)牙朋腋、相機(jī)、GPS等膜楷,因此要在Flutter APP中調(diào)用這些能力就必須和原生平臺(tái)進(jìn)行通信旭咽。
為此,F(xiàn)lutter中提供了一個(gè)平臺(tái)通道(platform channel)赌厅,用于Flutter和原生平臺(tái)的通信穷绵。平臺(tái)通道正是Flutter和原生之間通信的橋梁,它也是Flutter插件的底層基礎(chǔ)設(shè)施特愿。
Flutter 使用了一個(gè)靈活的系統(tǒng)仲墨,允許調(diào)用特定平臺(tái)的API,無論在Android上的Java或Kotlin代碼中洽议,還是iOS上的ObjectiveC或Swift代碼中均可用宗收。
Flutter與原生之間的通信依賴靈活的消息傳遞方式:
- 應(yīng)用的Flutter部分通過平臺(tái)通道(platform channel)將消息發(fā)送到其應(yīng)用程序的所在的宿主(iOS或Android)應(yīng)用(原生應(yīng)用)。
- 宿主監(jiān)聽平臺(tái)通道亚兄,并接受該消息混稽。然后它會(huì)調(diào)用該平臺(tái)的API,并將相應(yīng)發(fā)送回應(yīng)用程序的Flutter部分审胚。
平臺(tái)通道
使用平臺(tái)通道在Flutter和原生之間傳遞消息匈勋,如下:
在Flutter中調(diào)用原生方法時(shí),調(diào)用信息通過平臺(tái)通道傳遞到原生膳叨,原生收到調(diào)用信息后可以執(zhí)行指定的操作洽洁,如果需要返回?cái)?shù)據(jù),則原生會(huì)將數(shù)據(jù)再通過平臺(tái)通道傳遞給Flutter菲嘴。這里的消息傳遞是異步的饿自,這確保了用戶在消息傳遞時(shí)不會(huì)被掛起汰翠。
在Flutter,MethodChannel API 可以發(fā)送與方法調(diào)用相對(duì)應(yīng)的消息昭雌。在宿主平臺(tái)上复唤,MethodChannel
在Android API 和 FlutterMethodChannel iOS API可以接收方法調(diào)用并返回結(jié)果。這些類可以幫助我們用很少的代碼就能開發(fā)平臺(tái)插件烛卧。
注意: 如果需要佛纫,方法調(diào)用(消息傳遞)可以是反向的,即宿主作為客戶端調(diào)用Dart中實(shí)現(xiàn)的API总放。 quick_actions
插件就是一個(gè)具體的例子呈宇。
平臺(tái)通道數(shù)據(jù)類型支持
平臺(tái)通道使用標(biāo)準(zhǔn)消息編/解碼器對(duì)消息進(jìn)行編解碼,它可以高效的對(duì)消息進(jìn)行二進(jìn)制序列化與反序列化局雄。由于Dart與原生平臺(tái)之間數(shù)據(jù)類型有所差異甥啄,下面我們列出數(shù)據(jù)類型之間的映射關(guān)系。
自定義編解碼器
除了上面提到的MethodChannel
哎榴,還可以使用BasicMessageChannel
型豁,它支持使用自定義消息編解碼器進(jìn)行基本的異步消息傳遞。 此外尚蝌,可以使用專門的BinaryCodec
、StringCodec
和 JSONMessageCodec
類充尉,或創(chuàng)建自己的編解碼器飘言。
如何獲取平臺(tái)信息
Flutter中提供了一個(gè)全局變量 defultTargetPlatform 來獲取當(dāng)前應(yīng)用的平臺(tái)信息,defaultTargetPlatform定義在 “platform.dart”中驼侠,它的類型是TargetPlatform姿鸿,這是一個(gè)枚舉類,定義如下:
enum TargetPlatform {
android,
fuchsia,
iOS,
}
可以看到目前Flutter只支持這三個(gè)平臺(tái)倒源。我們可以通過如下代碼判斷平臺(tái):
if(defaultTargetPlatform==TargetPlatform.android){
// 是安卓系統(tǒng)苛预,do something
...
}
...
由于不同平臺(tái)有它們各自的交互規(guī)范,F(xiàn)lutter Material庫(kù)中的一些組件都針對(duì)相應(yīng)的平臺(tái)做了一些適配笋熬,比如路由組件MaterialPageRoute热某,它在android和ios中會(huì)應(yīng)用各自平臺(tái)規(guī)范的切換動(dòng)畫。那如果我們想讓我們的APP在所有平臺(tái)都表現(xiàn)一致胳螟,比如希望在所有平臺(tái)路由切換動(dòng)畫都按照ios平臺(tái)一致的左右滑動(dòng)切換風(fēng)格該怎么做昔馋?Flutter中提供了一種覆蓋默認(rèn)平臺(tái)的機(jī)制,我們可以通過顯式指定debugDefaultTargetPlatformOverride全局變量的值來指定應(yīng)用平臺(tái)糖耸。比如:
debugDefaultTargetPlatformOverride=TargetPlatform.iOS;
print(defaultTargetPlatform); // 會(huì)輸出TargetPlatform.iOS
上面代碼即在Android中運(yùn)行后秘遏,F(xiàn)lutter APP就會(huì)認(rèn)為是當(dāng)前系統(tǒng)是iOS,Material組件庫(kù)中所有組件交互方式都會(huì)和iOS平臺(tái)對(duì)齊嘉竟,defaultTargetPlatform的值也會(huì)變?yōu)門argetPlatform.iOS邦危。
下面以一個(gè)獲取電池電量的插件來介紹一下洋侨,通過在Dart中通過 getBatteryLevel 調(diào)用 iOS device.batteryLevel API。
Flutter部分:
- 導(dǎo)入import 'package:flutter/services.dart';
- 創(chuàng)建通道名稱倦蚪,單個(gè)應(yīng)用使用的所有通道名稱必須是唯一的:
static const platform = const MethodChannel('samples.flutter.io/battery'); - 調(diào)用方法希坚,這里是異步的 final int result = await platform.invokeMethod('getBatteryLevel');
iOS部分:
在application didFinishLaunchingWithOptions:方法內(nèi)部創(chuàng)建一個(gè)FlutterMethodChannel,并添加一個(gè)處理方法审丘。 確保與在Flutter客戶端使用的通道名稱相同吏够。
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
methodChannelWithName:@"samples.flutter.io/battery"
binaryMessenger:controller];
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
// TODO
}];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
在AppDelegate類中添加以下新的方法:
- (int)getBatteryLevel {
UIDevice* device = UIDevice.currentDevice;
device.batteryMonitoringEnabled = YES;
if (device.batteryState == UIDeviceBatteryStateUnknown) {
return -1;
} else {
return (int)(device.batteryLevel * 100);
}
}
最后,我們完成之前添加的setMethodCallHandler方法滩报。我們需要處理的平臺(tái)方法名為getBatteryLevel锅知,所以我們?cè)赾all參數(shù)中需要先判斷是否為getBatteryLevel。 這個(gè)平臺(tái)方法的實(shí)現(xiàn)只需調(diào)用我們?cè)谇耙徊街芯帉懙膇OS代碼脓钾,并使用result參數(shù)返回成功或錯(cuò)誤的響應(yīng)售睹。如果調(diào)用了未定義的API,我們也會(huì)通知返回:
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"getBatteryLevel" isEqualToString:call.method]) {
int batteryLevel = [self getBatteryLevel];
if (batteryLevel == -1) {
result([FlutterError errorWithCode:@"UNAVAILABLE"
message:@"電池信息不可用"
details:nil]);
} else {
result(@(batteryLevel));
}
} else {
result(FlutterMethodNotImplemented);
}
}];
至此可训,就可以了昌妹。