Flutter插件中如何封装同时含视图与方法的原生库类
Flutter插件同时暴露原生视图与类方法的实现方案
完全可以实现,核心思路是把平台视图实例和专属方法通道做绑定,替代全局通道的方案,避免多实例冲突,最终给插件使用者的体验和原生端一致:既可以把组件嵌入渲染,又能拿到实例引用调用公开方法。
核心实现逻辑
- 放弃全局注册的通用MethodChannel,为每一个嵌入的原生视图实例分配独立通信通道,通道唯一标识和视图id绑定,保证Flutter侧的控制器和原生侧的具体视图实例一一对应。
- 原生侧创建平台视图实例的同时,直接持有当前视图的引用,和对应id的MethodChannel绑定,通道收到方法调用时直接操作当前持有的视图实例,不需要额外通过
findViewById或者属性查找实例。 - Flutter侧对外暴露的Widget同时承担两个职责:一是作为原生视图的嵌入载体,二是对外暴露和原生实例绑定的方法调用接口,插件使用者拿到配套的控制器即可直接调用对应原生方法。
分端实现要点
Android端
在注册平台视图工厂时,create方法生成自定义原生视图实例后,立刻创建绑定当前viewId的MethodChannel,设置方法调用处理器直接映射到当前视图的公开方法,视图销毁时同步解绑通道避免内存泄漏。
class CustomNativeViewFactory( private val messenger: BinaryMessenger ) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { override fun create(context: Context, viewId: Int, args: Any?): PlatformView { // 实例化你的自定义原生视图 val customView = YourCustomNativeView(context) // 绑定当前实例专属的方法通道 val channel = MethodChannel(messenger, "your_plugin/view_$viewId") channel.setMethodCallHandler { call, result -> when(call.method) { "targetPublicMethod" -> { val params = call.arguments as? Map<String, Any?> val invokeResult = customView.targetPublicMethod(params) result.success(invokeResult) } // 其余公开方法按相同逻辑映射即可 else -> result.notImplemented() } } return object : PlatformView { override fun getView() = customView override fun dispose() { channel.setMethodCallHandler(null) customView.release() } } } }
iOS端
逻辑和Android端完全一致,平台视图工厂创建自定义原生视图实例时,用FlutterBinaryMessenger绑定带唯一视图id的MethodChannel,方法调用处理器直接持有当前视图的引用做方法转发,视图释放时清空handler避免循环引用。
class CustomNativeViewFactory: NSObject, FlutterPlatformViewFactory { private let messenger: FlutterBinaryMessenger init(messenger: FlutterBinaryMessenger) { self.messenger = messenger super.init() } func create( withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any? ) -> FlutterPlatformView { return CustomNativeView( frame: frame, viewId: viewId, args: args, messenger: messenger ) } } class CustomNativeView: NSObject, FlutterPlatformView { private let nativeView: YourCustomNativeView private let channel: FlutterMethodChannel init(frame: CGRect, viewId: Int64, args: Any?, messenger: FlutterBinaryMessenger) { nativeView = YourCustomNativeView(frame: frame) channel = FlutterMethodChannel( name: "your_plugin/view_\(viewId)", binaryMessenger: messenger ) super.init() channel.setMethodCallHandler { [weak self] call, result in guard let self = self else { return } switch call.method { case "targetPublicMethod": let invokeResult = self.nativeView.targetPublicMethod() result(invokeResult) default: result(FlutterMethodNotImplemented) } } } func view() -> UIView { return nativeView } deinit { channel.setMethodCallHandler(nil) } }
Flutter侧封装
对外暴露StatefulWidget嵌入对应平台的原生视图,视图创建完成后用拿到的viewId初始化专属MethodChannel,配套控制器类封装所有和原生对应的公开方法,插件使用者初始化控制器后,既能把Widget放到视图树渲染,又能通过控制器直接调用原生方法。
class CustomNativeWidget extends StatefulWidget { final CustomNativeController controller; const CustomNativeWidget({super.key, required this.controller}); @override State<CustomNativeWidget> createState() => _CustomNativeWidgetState(); } class _CustomNativeWidgetState extends State<CustomNativeWidget> { late MethodChannel _channel; @override Widget build(BuildContext context) { if (Platform.isAndroid) { return AndroidView( viewType: "your_plugin/custom_native_view", onPlatformViewCreated: (viewId) { _channel = MethodChannel("your_plugin/view_$viewId"); widget.controller.attachChannel(_channel); }, ); } else if (Platform.isIOS) { return UiKitView( viewType: "your_plugin/custom_native_view", onPlatformViewCreated: (viewId) { _channel = MethodChannel("your_plugin/view_$viewId"); widget.controller.attachChannel(_channel); }, ); } throw UnsupportedError("当前平台暂不支持"); } } class CustomNativeController { MethodChannel? _channel; /// 内部方法,用于视图创建后绑定通道 void attachChannel(MethodChannel channel) { _channel = channel; } /// 对外暴露的公开方法,和原生类方法一一对应 Future<dynamic> targetPublicMethod(Map<String, dynamic> params) async { assert(_channel != null, "视图未初始化完成,暂不能调用方法"); return _channel!.invokeMethod("targetPublicMethod", params); } }
注意事项
- 禁止用全局静态MethodChannel处理视图相关的方法调用,否则页面同时存在多个原生视图实例时,会出现方法调用错配的问题。
- 所有对外暴露的方法需要加空判断,在视图未创建完成/已经销毁时拦截调用,避免抛出空异常。
- 如果原生视图有主动通知Flutter的事件需求,直接复用当前实例绑定的MethodChannel发送即可,不需要额外注册事件通道。
内容的提问来源于stack exchange,提问作者Wei Wen
相关产品推荐
相关产品推荐

