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,最稳妥。虽然多了一步序列化/反序列化,但可维护性高很多。

好了,这一章的内容就是这些。三种通道各有各的用途,数据类型映射表建议你截图保存,写代码时经常要查。下一章我们会实际动手,写一个完整的平台通道示例。