Flutter Writing custom platform-specific code
About 5 min
Flutter Writing custom platform-specific code
Flutter 雖然提供了豐富的 Widget 和 API 來構建跨平台應用,但有時候仍然需要與原生層溝通。以下是需要與原生層溝通的一些主要原因:
- 訪問平台特定功能
- 平台 API: 有些功能 Flutter 沒有內建支持,但可以通過平台特定的 API 來實現。例如,訪問相機、傳感器、藍牙、NFC、通知系統、地理位置服務等。
- 系統設置: 某些應用可能需要訪問或修改設備的系統設置,如亮度、音量、網絡設置等,這些通常需要使用原生 API。
- 整合現有的原生代碼
- 現有應用: 如果你有一個現有的原生應用,並想逐步將其轉移到 Flutter,可能需要在過渡期間讓 Flutter 和原生代碼協同工作。
- 使用第三方 SDK: 有些第三方 SDK 僅提供原生支持(如一些支付 SDK、廣告 SDK),需要通過原生代碼進行集成。
- 性能優化
- 高效的 UI 渲染: 某些情況下,使用原生 UI 控件可能會比 Flutter Widget 更高效。比如在需要嵌入高性能原生控件時(如地圖或視頻播放器),可以考慮通過 Platform View 來實現。
- 高頻率/低延遲操作: 某些需要頻繁、快速交互的操作,可能通過原生代碼會更高效。
- 處理平台特有的行為
- 平台差異: Android 和 iOS 之間存在一些差異,例如後台處理、生命周期管理、權限處理等。這些行為可能需要通過原生代碼來處理,以確保在各平台上都有一致的用戶體驗。
- 原生 UI 風格: 在某些應用中,可能需要遵循平台的設計規範(如 Material Design 和 iOS 的 Human Interface Guidelines),這時可能需要使用原生代碼來達到最佳的設計一致性。
- 跨平台限制
- Flutter 尚未支持的功能: 雖然 Flutter 正在快速發展,但它並不支持所有的功能。如果你需要使用一個 Flutter 尚未支持的功能,則需要通過與原生層的溝通來實現。
- 平台依賴: 有些操作或特性可能強烈依賴於平台本身,這時使用原生代碼會更合適。
- 繼承和復用
- 現有的原生邏輯: 在遷移到 Flutter 時,可能有一些業務邏輯、算法或服務已經用原生代碼實現,為了復用這些代碼,可以通過與原生層的溝通來整合這些功能。
總之,與原生層溝通可以幫助克服 Flutter 的限制,使你能夠更靈活地實現應用的全部功能,同時確保應用能夠充分利用底層平台的特性和能力。
Architectural overview: platform channels
Messages are passed between the client (UI) and host (platform) using platform channels as illustrated in this diagram:

訊息和回應是非同步傳遞的,以確保使用者介面保持回應。
Platform Channels
Flutter 使用 Platform Channels 來實現與原生層(如 Android 和 iOS)之間的通信。這種通信方式允許 Flutter 的 Dart 代碼與原生代碼(如 Java/Kotlin 或 Objective-C/Swift)進行雙向通信。
- Method Channel: 最常用的方式,適合單次的數據傳輸。Flutter 端使用 MethodChannel,原生端通過 MethodChannel 的 handler 來處理來自 Flutter 的請求並返回結果。
- Event Channel: 用於持續傳輸數據流,比如傳感器數據或其他連續性數據。
- Basic Message Channel: 用於傳輸二進制或字符串數據,雙向通信,適合更複雜的通信場景。
sample
Flutter 端:
MethodChannel platform = MethodChannel('com.example.channel');
Future<void> _getNativeValue() async {
try {
final String result = await platform.invokeMethod('getNativeValue');
print('Native value: $result');
} on PlatformException catch (e) {
print("Failed to get native value: '${e.message}'.");
}
}Android 端(Kotlin):
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.channel")
.setMethodCallHandler { call, result ->
if (call.method == "getNativeValue") {
result.success("Hello from Android")
} else {
result.notImplemented()
}
}iOS 端(Swift):
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel(name: "com.example.channel",
binaryMessenger: controller.binaryMessenger)
methodChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getNativeValue" {
result("Hello from iOS")
} else {
result(FlutterMethodNotImplemented)
}
})Platform channel data types support and codecs
Flutter 的標準平台通道使用標準訊息編解碼器,支援簡單的類似 JSON 的值的高效二進位序列化,例如布林值、數字、字串、位元組緩衝區以及這些的列表和映射。
下表顯示了平台端如何接收 Dart 值,反之亦然:
Kotlin
| Dart | Kotlin |
|---|---|
| null | null |
| bool | Boolean |
| int (<=32 bits) | Int |
| int (>32 bits) | Long |
| double | Double |
| String | String |
| Uint8List | ByteArray |
| Int32List | IntArray |
| Int64List | LongArray |
| Float32List | FloatArray |
| Float64List | DoubleArray |
| List | List |
| Map | HashMap |
Java
| Dart | Java |
|---|---|
| null | null |
| bool | java.lang.Boolean |
| int (<=32 bits) | java.lang.Integer |
| int (>32 bits) | java.lang.Long |
| double | java.lang.Double |
| String | java.lang.String |
| Uint8List | byte[] |
| Int32List | int[] |
| Int64List | long[] |
| Float32List | float[] |
| Float64List | double[] |
| List | java.util.ArrayList |
| Map | java.util.HashMap |
Swift
| Dart | Swift |
|---|---|
| null | nil (NSNull when nested) |
| bool | NSNumber(value: Bool) |
| int (<=32 bits) | NSNumber(value: Int32) |
| int (>32 bits) | NSNumber(value: Int) |
| double | NSNumber(value: Double) |
| String | String |
| Uint8List | FlutterStandardTypedData(bytes: Data) |
| Int32List | FlutterStandardTypedData(int32: Data) |
| Int64List | FlutterStandardTypedData(int64: Data) |
| Float32List | FlutterStandardTypedData(float32: Data) |
| Float64List | FlutterStandardTypedData(float64: Data) |
| List | Array |
| Map | Dictionary |
Objective-C
| Dart | Objective-C |
|---|---|
| null | nil (NSNull when nested) |
| bool | NSNumber numberWithBool: |
| int (<=32 bits) | NSNumber numberWithInt: |
| int (>32 bits) | NSNumber numberWithLong: |
| double | NSNumber numberWithDouble: |
| String | NSString |
| Uint8List | FlutterStandardTypedData typedDataWithBytes: |
| Int32List | FlutterStandardTypedData typedDataWithInt32: |
| Int64List | FlutterStandardTypedData typedDataWithInt64: |
| Float32List | FlutterStandardTypedData typedDataWithFloat32: |
| Float64List | FlutterStandardTypedData typedDataWithFloat64: |
| List | NSArray |
| Map | NSDictionary |
C++
| Dart | C++ |
|---|---|
| null | EncodableValue() |
| bool | EncodableValue(bool) |
| int (<=32 bits) | EncodableValue(int32_t) |
| int (>32 bits) | EncodableValue(int64_t) |
| double | EncodableValue(double) |
| String | EncodableValue(std::string) |
| Uint8List | EncodableValue(std::vector<uint8_t>) |
| Int32List | EncodableValue(std::vector<int32_t>) |
| Int64List | EncodableValue(std::vector<int64_t>) |
| Float32List | EncodableValue(std::vector<float>) |
| Float64List | EncodableValue(std::vector<double>) |
| List | EncodableValue(std::vector<EncodableValue>) |
| Map | EncodableValue(std::map<EncodableValue, EncodableValue>) |
C (GObject)
| Dart | C (GObject) |
|---|---|
| null | FlValue() |
| bool | FlValue(bool) |
| int | FlValue(int64_t) |
| double | FlValue(double) |
| String | FlValue(gchar*) |
| Uint8List | FlValue(uint8_t*) |
| Int32List | FlValue(int32_t*) |
| Int64List | FlValue(int64_t*) |
| Float32List | FlValue(float*) |
| Float64List | FlValue(double*) |
| List | FlValue(FlValue) |
| Map | FlValue(FlValue, FlValue) |