Flutter 与原生通信的难点不在“调用”,而在协议、稳定性与可维护性。本文用一套可直接复用的代码范式,讲清 MethodChannel / EventChannel 的正确打开方式。
1. 先定义通信协议(最关键)
不要让 Flutter 与原生“随意传参”,必须先定义协议:
{
"action": "getDeviceInfo",
"payload": {}
}
以及统一响应:
{
"ok": true,
"code": 0,
"message": "",
"data": {"model": "..."}
}
这样才能避免双方升级时互相“猜数据”。
2. Flutter 侧:封装成可测试的服务层
class NativeBridge {
static const MethodChannel _channel = MethodChannel('app/native');
static Future<Map<String, dynamic>> getDeviceInfo() async {
final res = await _channel.invokeMethod('getDeviceInfo');
return Map<String, dynamic>.from(res as Map);
}
}
建议再封一层 ApiResponse,统一错误处理。
3. Android 侧:统一入口处理
class MainActivity: FlutterActivity() {
private val CHANNEL = "app/native"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"getDeviceInfo" -> {
val info = mapOf("model" to Build.MODEL)
result.success(info)
}
else -> result.notImplemented()
}
}
}
}
核心:所有原生处理集中到一个入口,避免散落。
4. iOS 侧:保持与 Android 对齐
let channel = FlutterMethodChannel(name: "app/native", binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler { call, result in
switch call.method {
case "getDeviceInfo":
result(["model": UIDevice.current.model])
default:
result(FlutterMethodNotImplemented)
}
}
保持方法名与返回结构一致,是长期维护的关键。
5. EventChannel:处理持续事件流
例如原生监听网络变化或传感器数据:
Flutter 侧:
static const EventChannel _event = EventChannel('app/events');
static Stream<dynamic> get events => _event.receiveBroadcastStream();
原生侧:
- Android:
EventChannel+StreamHandler - iOS:
FlutterEventChannel+FlutterStreamHandler
原则:事件流必须可取消,避免内存泄漏。
6. 常见错误与规避
- Method 名称不一致 → 必须统一定义常量
- 返回结构不一致 → 统一响应协议
- 异常未捕获 → 所有调用包 try/catch
- 长时任务阻塞 UI → 放到原生线程池
7. 实践清单
- 定义统一协议与错误码
- Flutter 侧封装服务层
- 原生侧集中处理入口
- EventChannel 订阅可取消
- 双端结构一致
总结
Flutter + 原生通信不是“能调通就行”,而是一个小型系统:协议、入口、错误、事件流必须清晰。只要规范到位,这一层的维护成本会非常低。