2. Flutter平台通道(Platform Channel)基础
好,咱们直接进入正题。平台通道,说白了就是Flutter和原生代码之间的「翻译官」。你想想看,Flutter跑在Dart虚拟机里,原生代码跑在Java/Kotlin或Objective-C/Swift里,它们俩语言不通,怎么交流?就得靠这个通道。
我个人习惯把平台通道想象成一根管子。Flutter这边把数据塞进去,原生那边接住,处理完再塞回来。反过来也一样。就这么简单。
2.1 三种通道类型
Flutter官方提供了三种通道,分别对应不同的通信场景。我刚开始接触时也觉得有点晕,后来发现其实很好区分。
| 通道类型 | 通信模式 | 典型场景 |
|---|---|---|
| BasicMessageChannel | 单向/双向消息传递 | 传递字符串、二进制数据 |
| MethodChannel | 方法调用(请求-响应) | 调用原生API、获取设备信息 |
| EventChannel | 事件流(单向推送) | 传感器数据、位置变化、电池状态 |
2.2 BasicMessageChannel —— 最基础的「传话筒」
这个通道最简单。它不关心你调了什么方法,只管把消息发过去、收回来。我在项目中遇到过需要把加密后的图片数据传给原生端做处理,用的就是它。
它的核心特点是:你发什么,它传什么。没有方法名,没有返回值约定,就是纯粹的异步消息。
适用场景:
- 传递序列化后的数据(JSON、二进制)
- 不需要严格请求-响应模式
- 两端各自处理逻辑,只交换数据
// Flutter端
final channel = BasicMessageChannel<String>(
'com.example/channel',
StringCodec(),
);
channel.send('Hello from Flutter');
channel.setMessageHandler((message) async {
print('收到原生回复: $message');
return '收到';
});
// Android端 (Kotlin)
val channel = BasicMessageChannel<String>(
flutterEngine.dartExecutor.binaryMessenger,
"com.example/channel",
StringCodec()
)
channel.setMessageHandler { message, reply ->
Log.d("TAG", "收到Flutter消息: $message")
reply.reply("Hello from Android")
}
2.3 MethodChannel —— 最常用的「远程调用」
这个通道我用得最多。它模拟了方法调用的模式:Flutter端调用一个方法,传参数,原生端执行并返回结果。说白了就是RPC(远程过程调用)。
你想想看,你要获取设备的电池电量,或者调用相机拍照,用MethodChannel最合适。它有一个明确的「方法名+参数+返回值」结构。
我的经验:方法名建议用反域名命名法,比如 com.example.device/getBatteryLevel。这样不容易跟其他插件冲突。我曾经因为方法名太随意,跟另一个库撞了,排查了半天。
// Flutter端
final channel = MethodChannel('com.example/device');
final batteryLevel = await channel.invokeMethod('getBatteryLevel');
print('电池电量: $batteryLevel%');
// iOS端 (Swift)
let channel = FlutterMethodChannel(
name: "com.example/device",
binaryMessenger: flutterEngine.binaryMessenger
)
channel.setMethodCallHandler { (call, result) in
if call.method == "getBatteryLevel" {
let batteryLevel = UIDevice.current.batteryLevel
result(Int(batteryLevel * 100))
} else {
result(FlutterMethodNotImplemented)
}
}
注意:MethodChannel的调用是异步的,但它是「一次请求一次响应」。如果你需要持续接收数据,比如传感器数值不断变化,那就别用MethodChannel了,用EventChannel。
2.4 EventChannel —— 持续不断的「数据流」
EventChannel跟前面两个完全不同。它是单向的,原生端主动往Flutter端推送数据。Flutter端只需要监听这个流,数据来了就处理。
我记得第一次用EventChannel是做步数统计。原生端每隔几秒就更新一次步数,Flutter端实时显示。如果用MethodChannel轮询,效率太低,而且不优雅。
// Flutter端
final channel = EventChannel('com.example/sensor');
channel.receiveBroadcastStream().listen(
(data) {
print('收到传感器数据: $data');
},
onError: (error) {
print('错误: $error');
},
onDone: () {
print('数据流关闭');
},
);
// Android端 (Kotlin)
val channel = EventChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.example/sensor"
)
channel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
// 开始监听传感器,通过events.success(data)发送数据
}
override fun onCancel(arguments: Any?) {
// 取消监听,释放资源
}
})
关键区别:
- BasicMessageChannel:双向消息,无方法名
- MethodChannel:请求-响应,有方法名
- EventChannel:单向推送,持续流
2.5 通道的数据类型支持
嗯,这里要注意。Flutter和原生之间传数据,不是什么东西都能直接传的。它们之间有一个「标准类型映射表」。说白了,Flutter的int到原生那边得变成Java的int或Kotlin的Int,不能乱来。
| Dart类型 | Android类型 | iOS类型 |
|---|---|---|
| null | null | nil |
| bool | java.lang.Boolean | NSNumber (bool) |
| int | java.lang.Integer | NSNumber (int) |
| double | java.lang.Double | NSNumber (double) |
| String | java.lang.String | NSString |
| Uint8List | byte[] | FlutterStandardTypedData |
| Int32List | int[] | FlutterStandardTypedData |
| Int64List | long[] | FlutterStandardTypedData |
| Float64List | double[] | FlutterStandardTypedData |
| List | java.util.ArrayList | NSArray |
| Map | java.util.HashMap | NSDictionary |
避坑指南:我曾经在项目里传了一个自定义对象,结果原生端收到的是Map。因为Flutter平台通道不支持自定义对象序列化。你得先把对象转成Map,传过去,原生端再自己解析。这个坑我踩过,后来老老实实用了JSON序列化。
另外,如果你传的数据类型不在这个表里,比如传一个Function或者一个Stream,那肯定不行。平台通道只支持这些基础类型和它们的组合(List、Map嵌套)。
我的建议:复杂数据结构统一用JSON字符串传递。两边都解析JSON,最稳妥。虽然多了一步序列化/反序列化,但可维护性高很多。
好了,这一章的内容就是这些。三种通道各有各的用途,数据类型映射表建议你截图保存,写代码时经常要查。下一章我们会实际动手,写一个完整的平台通道示例。