## 插件開發:平臺通道簡介
所謂“平臺特定”或“特定平臺”,平臺指的就是指Flutter運行的平臺,如Android或IOS,可以認為就是應用的原生部分。所以,平臺通道正是Flutter和原生之間通信的橋梁,它也是Flutter插件的底層基礎設施。
Flutter使用了一個靈活的系統,允許您調用特定平臺的API,無論在Android上的Java或Kotlin代碼中,還是iOS上的ObjectiveC或Swift代碼中均可用。
Flutter與原生之間的通信依賴靈活的消息傳遞方式:
- 應用的Flutter部分通過平臺通道(platform channel)將消息發送到其應用程序的所在的宿主(iOS或Android)應用(原生應用)。
- 宿主監聽平臺通道,并接收該消息。然后它會調用該平臺的API,并將響應發送回客戶端,即應用程序的Flutter部分。
### 平臺通道
使用平臺通道在Flutter(client)和原生(host)之間傳遞消息,如下圖所示:

當在Flutter中調用原生方法時,調用信息通過平臺通道傳遞到原生,原生收到調用信息后方可執行指定的操作,如需返回數據,則原生會將數據再通過平臺通道傳遞給Flutter。值得注意的是消息傳遞是異步的,這確保了用戶界面在消息傳遞時不會被掛起。
在客戶端,[MethodChannel API](https://docs.flutter.io/flutter/services/MethodChannel-class.html) 可以發送與方法調用相對應的消息。 在宿主平臺上,`MethodChannel` 在[Android API](https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodChannel.html) 和 [FlutterMethodChannel iOS API](https://docs.flutter.io/objcdoc/Classes/FlutterMethodChannel.html)可以接收方法調用并返回結果。這些類可以幫助我們用很少的代碼就能開發平臺插件。
> **注意**: 如果需要,方法調用(消息傳遞)可以是反向的,即宿主作為客戶端調用Dart中實現的API。 [`quick_actions`](https://pub.dartlang.org/packages/quick_actions)插件就是一個具體的例子。
### 平臺通道數據類型支持
平臺通道使用標準消息編/解碼器對消息進行編解碼,它可以高效的對消息進行二進制序列化與反序列化。由于Dart與原生平臺之間數據類型有所差異,下面我們列出數據類型之間的映射關系。
DartAndroidiOSnullnullnil (NSNull when nested)booljava.lang.BooleanNSNumber numberWithBool:intjava.lang.IntegerNSNumber numberWithInt:int, if 32 bits not enoughjava.lang.LongNSNumber numberWithLong:int, if 64 bits not enoughjava.math.BigIntegerFlutterStandardBigIntegerdoublejava.lang.DoubleNSNumber numberWithDouble:Stringjava.lang.StringNSStringUint8Listbyte\[\]FlutterStandardTypedData typedDataWithBytes:Int32Listint\[\]FlutterStandardTypedData typedDataWithInt32:Int64Listlong\[\]FlutterStandardTypedData typedDataWithInt64:Float64Listdouble\[\]FlutterStandardTypedData typedDataWithFloat64:Listjava.util.ArrayListNSArrayMapjava.util.HashMapNSDictionary 當在發送和接收值時,這些值在消息中的序列化和反序列化會自動進行。
## 開發Flutter插件
使用平臺通道調用原生代碼
下面我們通過一個獲取電池電量的插件來介紹一下Flutter插件的開發流程。該插件中我們在Dart中通過`getBatteryLevel` 調用Android `BatteryManager` API和iOS `device.batteryLevel` API。
### 創建一個新的應用程序項目
首先創建一個新的應用程序:
- 在終端中運行:`flutter create batterylevel`
默認情況下,模板支持使用Java編寫Android代碼,或使用Objective-C編寫iOS代碼。要使用Kotlin或Swift,請使用-i和/或-a標志:
- 在終端中運行: `flutter create -i swift -a kotlin batterylevel`
### 創建Flutter平臺客戶端
該應用的`State`類擁有當前的應用狀態。我們需要延長這一點以保持當前的電量
首先,我們構建通道。我們使用`MethodChannel`調用一個方法來返回電池電量。
通道的客戶端和宿主通過通道構造函數中傳遞的通道名稱進行連接。單個應用中使用的所有通道名稱必須是唯一的; 我們建議在通道名稱前加一個唯一的“域名前綴”,例如`samples.flutter.io/battery`。
```
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
...
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('samples.flutter.io/battery');
// Get battery level.
}
```
接下來,我們調用通道上的方法,指定通過字符串標識符調用方法`getBatteryLevel`。 該調用可能失敗(平臺不支持平臺API,例如在模擬器中運行時),所以我們將invokeMethod調用包裝在try-catch語句中。
我們使用返回的結果,在`setState`中來更新用戶界面狀態`batteryLevel`。
```
// Get battery level.
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;
});
}
```
最后,我們在build創建包含一個小字體顯示電池狀態和一個用于刷新值的按鈕的用戶界面。
```
@override
Widget build(BuildContext context) {
return new Material(
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new RaisedButton(
child: new Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
new Text(_batteryLevel),
],
),
),
);
}
```
在接下來的兩節中,我們會分別介紹Android和iOS端API的實現。
## 自定義平臺通道和編解碼器
除了上面提到的`MethodChannel`,還可以使用[`BasicMessageChannel`](https://docs.flutter.io/flutter/services/BasicMessageChannel-class.html),它支持使用自定義消息編解碼器進行基本的異步消息傳遞。 此外,可以使用專門的[`BinaryCodec`](https://docs.flutter.io/flutter/services/BinaryCodec-class.html)、[`StringCodec`](https://docs.flutter.io/flutter/services/StringCodec-class.html)和 [`JSONMessageCodec`](https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html)類,或創建自己的編解碼器。
- 緣起
- 起步
- 移動開發技術簡介
- Flutter簡介
- 搭建Flutter開發環境
- 常見配置問題
- Dart語言簡介
- 第一個Flutter應用
- 計數器示例
- 路由管理
- 包管理
- 資源管理
- 調試Flutter APP
- Dart線程模型及異常捕獲
- 基礎Widgets
- Widget簡介
- 文本、字體樣式
- 按鈕
- 圖片和Icon
- 單選框和復選框
- 輸入框和表單
- 布局類Widgets
- 布局類Widgets簡介
- 線性布局Row、Column
- 彈性布局Flex
- 流式布局Wrap、Flow
- 層疊布局Stack、Positioned
- 容器類Widgets
- Padding
- 布局限制類容器ConstrainedBox、SizeBox
- 裝飾容器DecoratedBox
- 變換Transform
- Container容器
- Scaffold、TabBar、底部導航
- 可滾動Widgets
- 可滾動Widgets簡介
- SingleChildScrollView
- ListView
- GridView
- CustomScrollView
- 滾動監聽及控制ScrollController
- 功能型Widgets
- 導航返回攔截-WillPopScope
- 數據共享-InheritedWidget
- 主題-Theme
- 事件處理與通知
- 原始指針事件處理
- 手勢識別
- 全局事件總線
- 通知Notification
- 動畫
- Flutter動畫簡介
- 動畫結構
- 自定義路由過渡動畫
- Hero動畫
- 交錯動畫
- 自定義Widget
- 自定義Widget方法簡介
- 通過組合現有Widget實現
- 實例:TurnBox
- CustomPaint與Canvas
- 實例:圓形漸變進度條(自繪)
- 文件操作與網絡請求
- 文件操作
- Http請求-HttpClient
- Http請求-Dio package
- 實例:Http分塊下載
- WebSocket
- 使用Socket API
- Json轉Model
- 包與插件
- 開發package
- 插件開發:平臺通道簡介
- 插件開發:實現Android端API
- 插件開發:實現IOS端API
- 系統能力調用
- 國際化
- 讓App支持多語言
- 實現Localizations
- 使用Intl包
- Flutter核心原理
- Flutter UI系統
- Element和BuildContext
- RenderObject與RenderBox
- Flutter從啟動到顯示