Flutter MethodChannel 跨端通信框架 zh_native_channel:快速入门、优势分析与 Pigeon 对比
Flutter MethodChannel 跨端通信框架 zh_native_channel:快速入门、优势分析与 Pigeon 对比
Flutter 做跨端开发时,很多人都会遇到一个熟悉的问题:Dart 要调用原生能力,原生也可能要回调 Dart。官方提供了 MethodChannel,它简单、直接、能力强,但项目一大之后,channel name、method name、Map 字段、注册逻辑、类型转换就会散落在 Dart、iOS、Android 多端代码里。
为了解决这个问题,我做了一个开源项目:zh_native_channel。
仓库地址:
https://github.com/911hzh/zh_native_channel
Flutter package 包名:
zh_native_channel
zh_native_channel 是什么?
zh_native_channel 是一个面向 Flutter 的跨端 MethodChannel 消息框架。
它的核心思路是:把一次平台调用抽象成“消息对象 + 处理器”。
也就是说,业务侧不再直接到处写 channel 字符串、method 字符串、Map 参数解析,而是通过统一的消息类和 handler 来完成 Dart、iOS、Android、Web 之间的通信。
配套的 zh_native_channel_generator 负责扫描 Dart 消息定义和平台 handler 标记,生成 Dart 注册代码、Swift/Kotlin/TypeScript 消息模型和平台注册代码。运行时能力由 zh_native_channel 提供,代码生成能力由 generator 提供。
它能做什么?
目前它主要解决 Flutter 跨端通信中的这些问题:
- Dart 侧可以通过统一入口调用平台侧:
final response = await ZHNativeChannel.instance.invoke(msg);
-
平台侧可以接收 Flutter 的 MethodChannel 调用,解析消息,查找 handler,并返回消息对象。
-
支持平台侧主动调用 Dart,并把返回值解析成对应的
ChannelBaseMsg。 -
通过注解标记消息和 handler:
()
class PingMsg extends ChannelBaseMsg {
// ...
}
('PingMsg')
class PingHandler extends ChannelBaseHandler {
// ...
}
- 原生侧可以通过注释标记 handler:
// PlatformChannelHandler("PingMsg")
// PlatformChannelHandler("PingMsg")
- 通过配置文件生成 Dart、iOS、Android、Web 的注册代码和平台消息模型。
当前生成器支持的字段类型包括 String、int、double、num、bool、Map<String, dynamic>、List<...>。iOS Swift 和 Android Kotlin 侧还支持同文件内自定义 model、嵌套 model、List<Model>、Map<String, Model> 和可空嵌套 model 的平台侧转换。
为什么需要它?
Flutter 官方的 MethodChannel 是一个命名通道,用于和平台插件进行异步方法调用。官方文档也说明,Dart 侧参数和返回值类型是 dynamic,调用双方需要自己保证参数和数据类型一致。
这在小功能里没什么问题,比如写一个获取电量的 demo:
static const platform = MethodChannel('samples.flutter.dev/battery');
final result = await platform.invokeMethod('getBatteryLevel');
但当项目变复杂之后,问题就开始出现了:
- Dart、iOS、Android 多端都要维护同一套字符串。
- 参数通过 Map 传递,字段名写错很难在编译期发现。
- 每加一个业务通道,就要写注册、解析、分发、类型转换。
- 多端代码结构不统一,新人接手成本高。
- Dart 调原生、原生调 Dart 两个方向都要各写一套约定。
zh_native_channel 的目标就是把这些重复约定集中起来,让业务开发更关注“消息是什么”和“谁来处理”。
它能带来什么好处?
使用 zh_native_channel 后,新增一个跨端调用通常只需要做三件事:
- 定义一个消息类。
- 编写对应的 Dart 或原生 handler。
- 运行生成命令生成注册代码和平台模型。
它带来的直接收益是:
- 减少手写
MethodChannel字符串和 Map 字段不一致的问题。 - 多端使用同一套消息命名、序列化规则和注册入口。
- 生成代码集中管理,业务代码更干净。
- Dart、Swift、Kotlin、Web 的通信结构更统一。
- 新增业务通道时,不需要重复写大量模板代码。
和同类方案相比怎么样?
对比手写 MethodChannel
手写 MethodChannel 的优点是简单、直接、官方支持、学习成本低。Flutter 官方文档也说明,平台通道本身就是 Flutter 调用平台特定代码的基础机制。
但它的缺点也很明显:当项目里通道越来越多时,字符串、参数 Map、注册表和类型转换都需要手动维护。zh_native_channel 正是基于 MethodChannel 之上,把这些重复劳动抽象成消息对象和 handler,再通过生成器生成多端注册代码。
所以,如果只是一个很小的原生能力调用,手写 MethodChannel 就够了;如果项目里有大量 Dart 与原生互调,zh_native_channel 会更适合维护。
对比 Pigeon
Pigeon 是 Flutter 官方生态里的代码生成工具,用来让 Flutter 和宿主平台之间的通信更类型安全、更简单。官方介绍中也提到,Pigeon 可以减少多平台字符串维护,并生成 Dart 与宿主语言代码。
Pigeon 的优势是成熟、官方生态、类型安全能力强,支持 Android Kotlin/Java、iOS/macOS Swift/Objective-C、Windows C++、Linux GObject 等平台。
zh_native_channel 的不同点在于,它更偏向“消息对象 + handler 注册”的组织方式,更适合想围绕业务消息来管理跨端通信的项目。
如果从日常业务开发的使用流程看,zh_native_channel 会更轻一些。使用 Pigeon 时,通常需要先写一份专门的 Dart API 声明文件,用 @HostApi() 或 @FlutterApi() 描述通信接口,然后运行 Pigeon 生成 Dart 和宿主端代码,再在宿主端实现生成出来的接口。
而 zh_native_channel 的工作方式更接近业务消息本身:
- Dart 侧定义一个消息类,并加上
@ChannelMsg()。 - 原生侧实现对应 handler,并加一行
// PlatformChannelHandler("MessageName")注释。 - 运行脚本后,生成器会帮助生成 platform 端 msg 模型和注册代码。
- 业务侧只需要关注 handler 的实现逻辑。
也就是说,它少了一层“先定义 protocol/API 契约,再实现生成接口”的心智负担。对于大量“一个消息对应一个处理器”的业务场景,它的使用路径会更直观:消息是什么,就定义什么;谁处理它,就实现对应 handler。
它强调统一 channel name、序列化规则和注册入口,并支持通过 Dart 注解、原生注释和配置文件来生成 Dart/iOS/Android/Web 相关代码。
它目前的不足也需要坦诚说明:相比 Pigeon 这种成熟方案,zh_native_channel 还处在持续完善阶段,生态、文档、平台覆盖和稳定性都需要更多真实项目验证。目前 Web 端类型映射也仍以基础类型和结构化 unknown 为主。
简单来说:
- 想要官方成熟方案、强类型 API、更多桌面平台支持,可以优先看 Pigeon。
- 想要基于业务消息组织跨端调用,减少 protocol/API 声明、MethodChannel 手写注册和多端样板代码,可以试试
zh_native_channel。 - 如果你的项目已经大量使用 MethodChannel,并且开始出现维护成本,
zh_native_channel可能正好能解决这类痛点。
快速入门
在 Flutter 项目中添加运行时库和生成器:
dependencies:
zh_native_channel:
path: ../
dev_dependencies:
build_runner: ^2.10.4
zh_native_channel_generator: ^0.0.1
如果你想直接从 GitHub 引入,也可以根据项目情况使用 Git 依赖:
dependencies:
zh_native_channel:
git:
url: https://github.com/911hzh/zh_native_channel.git
下面以“Flutter 调用 platform,然后 platform 返回结果”为例,走一遍完整流程。
第一步:在 Dart 侧定义消息
先定义一个业务消息,比如 PingMsg。这个消息既是 Flutter 发给 platform 的请求对象,也可以作为 platform 返回给 Flutter 的响应对象。
关键点是加上 @ChannelMsg(),并实现 fromMap 和 toMap。
import 'package:zh_native_channel/zh_native_channel.dart';
()
class PingMsg extends ChannelBaseMsg {
final String text;
PingMsg({required this.text});
static PingMsg fromMap(Map<String, dynamic> map) {
return PingMsg(text: map['text'] as String? ?? '');
}
Map<String, dynamic> toMap() {
return {'text': text};
}
}
第二步:在 platform 侧实现 handler
如果这个消息要由 iOS 或 Android 处理,platform 侧只需要实现对应 handler,并通过注释标记它处理哪个消息。
iOS 侧 handler 示例:
import zh_native_channel
// PlatformChannelHandler("PingMsg")
final class PingMsgHandler: ChannelBaseHandler {
func handle(
_ message: ChannelBaseMsg,
completion: @escaping (Result<ChannelBaseMsg, Error>) -> Void
) {
guard let ping = message as? PingMsg else {
completion(.failure(ChannelMsgError.typeMismatch(expected: "PingMsg")))
return
}
completion(.success(PingMsg(text: "ios:\(ping.text)")))
}
}
Android 侧 handler 示例:
import com.example.zh_native_channel.ChannelBaseHandler
import com.example.zh_native_channel.ChannelBaseMsg
// PlatformChannelHandler("PingMsg")
class PingMsgHandler : ChannelBaseHandler {
override fun handle(message: ChannelBaseMsg): ChannelBaseMsg {
val ping = message as PingMsg
return PingMsg(text = "android:${ping.text}")
}
}
这里最重要的是:你只需要实现 handler 的业务逻辑。
platform 端的 PingMsg 模型、消息注册表、handler 注册入口都交给生成器处理,不需要自己手写一堆 MethodChannel 分发代码。
第三步:配置扫描路径和生成路径
项目通过 zh_native_channel_config.json 告诉生成器:
- Dart 消息类在哪里。
- Dart handler 在哪里。
- iOS / Android handler 在哪里。
- platform 端 msg 模型要生成到哪里。
- platform 端注册文件要生成到哪里。
示例配置在后文“配置文件示例”里有完整展示。真实项目建议至少明确配置消息扫描目录、handler 扫描目录、平台输出目录和 Android packageName。
第四步:运行脚本生成代码
常用生成命令:
make gen
它会完成一整套生成流程,包括清理生成产物、运行 build_runner、生成 iOS/Android/Web 侧平台代码等。
也可以直接调用生成器:
dart run zh_native_channel_generator:generate_native_channel zh_native_channel_config.json --platform all
执行完成后,生成器会帮你产出这些内容:
- Dart 侧消息和 handler 的注册代码。
- iOS Swift 的 msg 模型和注册代码。
- Android Kotlin 的 msg 模型和注册代码。
- Web 侧需要的 TypeScript / JavaScript 相关结构。
第五步:在启动阶段注册生成代码
Dart 侧启动时调用生成入口:
initializeGeneratedChannels();
iOS / Android 启动阶段调用生成的平台注册入口,把消息和 handler 注册进原生 manager:
GeneratedChannelRegistrations.registerAll()
GeneratedChannelRegistrations.registerAll()
第六步:Flutter 发起调用
完成上面的步骤后,Flutter 侧就可以直接通过消息对象调用 platform:
final channel = ZHNativeChannel.instance;
final response = await channel.invoke(PingMsg(text: 'hello'));
这一次调用的完整链路是:
- Flutter 创建
PingMsg(text: 'hello')。 ZHNativeChannel.instance.invoke(...)把消息序列化后通过 MethodChannel 发给 platform。- platform 侧
MethodChannelMsgManager接收调用。 - manager 根据消息名找到生成注册表里的
PingMsgHandler。 PingMsgHandler执行业务逻辑,返回一个新的PingMsg。- 返回值通过 MethodChannel 回到 Flutter。
- Dart 侧把返回值解析成对应的
ChannelBaseMsg。
最终,业务代码面对的是消息对象,而不是散落在多端的字符串、Map 字段和注册分发逻辑。
如果消息由 Dart 侧处理,也可以创建 Dart handler:
('PingMsg')
class PingHandler extends ChannelBaseHandler {
Future<ChannelBaseMsg> handle(ChannelBaseMsg msg) async {
final ping = msg as PingMsg;
return PingMsg(text: 'dart:${ping.text}');
}
}
配置文件示例
zh_native_channel_config.json 用来描述“扫描哪里”和“生成到哪里”。
示例:
{
"dart": {
"defaultOutputDirectory": "lib/base/zHNativeChannel",
"messageScanPath": [
"lib/base/zHNativeChannel/msgs"
],
"handlerScanPath": [
"lib/base/zHNativeChannel/handlers"
],
"generatedChannelRegisterOutputPath": "lib/base/zHNativeChannel/ChannelGeneratedRegister.g.dart"
},
"platforms": {
"ios": {
"defaultOutputDirectory": "ios/Runner/zHNativeChannel",
"scanHandlerPath": [
"ios/Runner/zHNativeChannel/handler_ios"
],
"msgsOutputPath": "ios/Runner/zHNativeChannel/msgs",
"generatedChannelRegistrationsOutputPath": "ios/Runner/zHNativeChannel",
"xcodeProjectPath": "ios/Runner.xcodeproj/project.pbxproj"
},
"android": {
"defaultOutputDirectory": "android/app/src/main/kotlin/com/example/zh_native_channel_example/zHNativeChannel",
"scanHandlerPath": [
"android/app/src/main/kotlin/com/example/zh_native_channel_example/zHNativeChannel/handlers"
],
"msgsOutputPath": "android/app/src/main/kotlin/com/example/zh_native_channel_example/zHNativeChannel/msgs",
"generatedChannelRegistrationsOutputPath": "android/app/src/main/kotlin/com/example/zh_native_channel_example/zHNativeChannel",
"packageName": "com.example.zh_native_channel_example.zHNativeChannel"
}
}
}
业务项目建议至少明确配置消息扫描目录、handler 扫描目录、平台输出目录和 Android packageName。
项目地址
GitHub:
https://github.com/911hzh/zh_native_channel
Flutter package 包名:
zh_native_channel
当前仓库版本:
0.0.7
写在最后
zh_native_channel 还在持续完善中。
如果你也在 Flutter 项目里长期和 MethodChannel 打交道,如果你也遇到过 Dart、iOS、Android 多端字段不一致、注册代码重复、消息分发难维护的问题,欢迎试用这个项目,也欢迎提 issue、提 PR、补充文档和示例。
期待大家一起来维护这个开源项目,让 Flutter 跨端通信变得更清晰、更统一、更好维护。
参考资料:
zh_native_channelGitHub 仓库:https://github.com/911hzh/zh_native_channel- Flutter
MethodChannelAPI 文档:https://api.flutter.dev/flutter/services/MethodChannel-class.html - Flutter 平台通道官方文档:https://github.com/flutter/website/blob/main/src/content/platform-integration/platform-channels.md
- Pigeon 官方包介绍:https://pub.dev/packages/pigeon
更多推荐



所有评论(0)