Flutter 嵌入安卓原生 View,以及与原生交互
在跨端开发里,有些场景是 Flutter 处理起来比较麻烦或者利用原生组件实现更高效。
这时候就得祭出 PlatformView 和 MethodChannel。不仅把一个 Android 原生 TextView 塞进了 Flutter 布局,还能实现Flutter和原生Android View的双向交互。
为了直观,我打算基于Flutter默认的计数器模板演示, 界面布局和悬浮按钮还是 Flutter 的,但中间显示的那个数字,换成安卓原生的TextView。

把原生 View 嵌进来
Android 的 View 集成到 Flutter 的 Widget 树里的关键是使用 PlatformView。它能像常规 Widget 一样参与层级覆盖和事件分发。 (类似于Compose中的AndroidView, 不过因为跨语言的问题, Flutter的嵌入会比Compose复杂一些)
1. 实现原生渲染层 (Kotlin)
如果要把Android原生View嵌入Flutter, 就得为原生View做一个包装类, 继承自PlatformView。 并且需要实现getView()方法, 返回想要嵌入的View.
为了能让后续的 MethodChannel 找到这个 View 实例,我专门用一个 HashMap 来管理它们。
下面是用一个叫做NativeAndroidView`来演示, 他的构造函数内参数有三个, 分别是viewId, args, viewMaps.
viewId:这是由 Flutter 侧自动生成的唯一标识。当你页面上有多个相同的原生组件时,它是区分“谁是谁”的唯一凭证。
args (CreationParams):这是从 Dart 传过来的初始化“大礼包”。建议在这里处理那些只需设置一次的属性(如初始颜色、模式等),以减少后续 MethodChannel 的通信压力。
viewMaps:这是我们在插件层维护的“通讯录”。通过把 viewId 和实例绑定,我们才能在收到 Flutter 指令时,精准地找到对应的 TextView 进行更新。
1 | // NativeAndroidView.kt |
2. 衔接用的工厂类
Flutter得通过一个工厂类, 才能创建刚才定义的 View.
所以手动定义一个 NativeAndroidViewFactory 类, 继承自 PlatformViewFactory.
1 | // NativeAndroidViewFactory.kt |
逻辑通信:MethodChannel
构建View的工厂类和View都有了, 接下来就是如何让Flutter和原生View进行通信了.
提起跨端通信,难免想起以前 Hybrid 开发时监控
alert来传消息的野路子。
而Flutter的跨端通信基于MethodChannel,基于二进制消息,清爽且高效。
为了让代码更整洁,我把 View 注册和消息处理都封装在插件类(FlutterPlugin)里。注意看这里的 increment 分支,它通过 viewId 准确定位到了屏幕上的那个原生 View 实例并操作它。
FlutterPlugin为我们提供onAttachedToEngine和onDetachedFromEngine两个方法来感知Flutter中的生命周期.
1 | // NativeAndroidViewPlugin.kt |
最后, 别忘了在 MainActivity.kt 里把插件装上。
Flutter 这一头怎么接?
Flutter 这边就非常直白了, 繁琐的工作已经在前面做过了, 直接使用 AndroidView 再传递前面定义好的参数;想原生View进一步通信,就拿着 onPlatformViewCreated 回调里的 id借助MethodChannel去和原生通信.
1 | // lib/main.dart |
PlatformView 是“贪婪”的
在折腾的过程中,可能发现 PlatformView 有个很显著的特性:它是贪婪的。, 他想尽可能占居所有空间.
在 Android 开发里,我们习惯用 WRAP_CONTENT 来让 View 自适应内容大小。但在 Flutter 的 PlatformView 体系下,这一招失效了。你会发现,即便你原生代码里写了自适应,这个原生 View 仍会强行铺满父布局给它的所有空间。
为什么会出现这种情况?
因为从渲染层面看,PlatformView 在 Flutter 的 Layer Tree 里其实是一个独立的合成层。Flutter 渲染引擎(比如新版的 Impeller)需要预先分配好一个固定大小的纹理或 Surface 来接收原生的渲染输出。它不像普通的 Flutter Widget 能够实时根据子插件的大小来动态收缩边界。
如何解决?
不要试图在 Android 原生侧去控制边界(改 LayoutParams 几乎没用)。你必须在 Flutter 侧给 AndroidView 套上一层约束,比如用 SizedBox 指定宽高,或者用 AspectRatio 锁定比例。只有在那一侧限制住“水池”的大小,里面的原生 View 才会乖乖服帖。
总结一下
底层视角:
PlatformView 虽好,但它本质上是在 Flutter 的渲染画布上“挖孔”。如果你熟悉 OpenGL,可以将其理解为:Flutter 与原生端共享 EGLContext,原生组件渲染后的结果直接写入指定的 textureID。对 Flutter 而言,它只负责消费这张纹理,而不参与其内部复杂的渲染指令。
适用场景:
重度原生 SDK:地图、WebView、视频播放器等。
极高频动态更新:如串口日志实时滚动刷新、频谱图等,这类场景利用原生 View 的缓存池和局部刷新机制更高效。
慎用场景:
简单的 UI 样式(圆角、阴影、动画):能用 Flutter 自绘解决的,永远优先选择 Flutter。
性能代价:每一层 PlatformView 都会涉及跨线程的纹理提交和同步开销,过度使用会导致内存增长和明显的掉帧。
Flutter 嵌入安卓原生 View,以及与原生交互
https://chaosgoo.com/flutter-deep-dive-platform-view-and-method-channel/