flutter渲染
Flutter 被设计为一个可扩展的分层系统。它可以被看作是各个独立的组件的系列合集,上层组件各自依赖下层组件。组件无法越权访问更底层的内容,并且框架层中的各个部分都是可选且可替代的。
我理解的flutter渲染
- Flutter自己完成一切:Flutter应用在屏幕上看到的每一个像素,从按钮到文字,都是由Flutter的渲染引擎(Skia/Impeller)绘制的。它不依赖iOS的UIKit或Android的View系统来生成原生控件。
- 平台嵌入层:这是理解的关键。Flutter引擎本身(C++编写)是一个不知道自己是运行在手机、桌面还是Web上的“盲盒”。平台嵌入层 就是这个“盲盒”与具体操作系统(如iOS、Android、Windows)进行对话和连接的“翻译官”或“适配器”。
- 稳定的ABI:ABI可以理解为引擎与嵌入层之间约定好的“电话线路”和“通话协议”。无论底层平台如何变化,引擎都通过这套固定的接口(方法调用、数据格式)与嵌入层通信,保证了引擎核心的稳定性和跨平台性。 可以理解为这个三层模型
flutter与android/IOS渲染逻辑对比
核心区别:两种思维模式
让我们用一个对比表来直观理解:
你的Dart代码完全不是转化成Android的“可绘制代码”或iOS的“UIView”的。 你写的Dart Flutter代码,其最终产物在屏幕上不直接对应任何平台的原生控件。
所以,这句话揭示的正是Flutter实现“高一致性”和“高性能”跨端能力的秘诀:
- 自上而下的统一渲染:通过自己控制渲染管线,保证UI的绝对一致性。
- 自下而上的抽象接口:通过定义稳定的ABI和平台嵌入层,将千差万别的平台原生能力抽象成一套统一的接口供引擎调用。
更加详细的 架构解析
比喻:自己作画 vs 拼装乐高套件
- 原生开发(拼装乐高套件):平台(苹果/谷歌)给你提供了标准的“乐高零件库”(UIView, Button, TextView)。你的代码负责选择零件、拼装起来。最终作品是由标准零件构成的。
- Flutter(在白纸上作画):平台只提供给你一张固定大小的空白画布和一个画室环境(生命周期、输入事件)。Flutter引擎就是你的手和画笔。你的Dart代码是“绘画指令”(画一个圆角矩形在这里,写一行文字在那里)。最终作品是一幅完全由你(Flutter)掌控的、独一无二的画,画布上没有任何平台的“标准零件”。
技术流程详解
你提到的“获取纹理”正是关键所在。整个过程是这样的:
- 你的Dart代码:定义了一棵Widget树,它只是一个配置的蓝图,存在于Dart的内存中。
- Flutter引擎(C++):
-
- 根据Widget树,构建出对应的RenderObject树(负责布局和绘制计算)。
- 布局引擎计算每个元素在屏幕上的位置和大小。
- 合成与绘制引擎(Skia/Impeller)遍历渲染树,生成一系列底层的绘图指令(如“填充路径”、“绘制图像”)。
- 提交给GPU:这些绘图指令被提交到GPU,在一块由Flutter引擎请求、平台分配给的纹理(Texture)上进行光栅化,最终生成一张包含你整个UI的位图。
- 平台嵌入层的工作:平台(如iOS)的任务仅仅是:
-
- 创建一个原生的UIView(作为容器)。
- 将Flutter引擎绘制好的那块纹理,作为这个UIView的内容显示出来。
- 负责将这个UIView插入到原生视图层级中,并传递触摸事件。
平台看到的只是一个“黑盒”的、会变化的内容视图,它完全不知道、也不关心这个视图里面画的是按钮、列表还是动画。 它只知道这是一张图。
fultter 与 react native 渲染逻辑对比
React Native采用了与Flutter完全不同的架构哲学。如果说Flutter是自己作画的画家,那么React Native更像是指挥两支本地乐队的指挥家。
核心区别:桥接与映射
RN的核心设计是:用JavaScript编写业务逻辑和组件声明,通过一个“桥接”将其翻译并映射到各自平台的原生控件上。
RN 技术流程详解
当你写一个RN组件时,流程如下:
// 1. 你写的JSX代码 (JavaScript) <View style={{backgroundColor: 'red'}}> <Text>Hello World</Text> </View> // 2. RN框架通过JSI直接内存访问 // 消息大致是:{ "type": "createView", "reactTag": 1, "viewType": "RCTView", "props": {style: {backgroundColor: 'red'}} } // 3. 原生端(iOS/Android)接收消息 // - iOS: 创建一个 UIView,背景色设为红色。 // - Android: 创建一个 View,背景色设为红色。 // 4. 屏幕上显示的是一个如假包换的原生视图。
关键点:你在RN中使用的 、、 等核心组件,在运行时确实会一对一地对应生成一个平台原生的 UIView/TextView/ImageView。你的界面最终是由这些真正的原生控件混合拼装而成的树。
flutter 与 android/IOS 渲染体系技术栈对比
三大渲染体系技术栈对比
| 对比维度 | iOS 原生渲染体系 | Android 原生渲染体系 | Flutter 渲染引擎 |
| 核心图形引擎 | 不直接使用第三方引擎,使用自主技术栈。 | Skia 作为其2D图形绘制的核心引擎。 | 自带引擎:在 iOS/Android 上主要为 Impeller(新)或 Skia(旧/后备)。 |
| 底层图形API | Metal (为主),专为 Apple 硬件优化。 | Vulkan (现代) / OpenGL ES (旧),跨平台标准。 | 通过引擎适配层调用:在 iOS 用 Metal,在 Android 用 Vulkan/OpenGL ES。 |
| UI框架与合成器 | UIKit (控件) -> Core Animation (合成与动画)。 | View 系统 (控件) -> RenderEngine/SurfaceFlinger (合成)。 | Flutter Framework (Widget) -> 自建渲染树 -> 引擎直接合成与绘制。 |
| 与Flutter的关系 | Flutter 引擎使用 Metal,但完全绕过 UIKit 和 Core Animation 的绘制逻辑,自行管理合成与动画。 | Flutter 引擎可能与系统共用 Skia 库,但绕过整个 View 系统及硬件加速画布,自行管理所有绘制。 | 在两大平台上,Flutter 都作为一个独立的、顶层的图形客户端运行,输出一整张纹理。 |
| 输出形态 | 由无数个 CALayer(对应各个UIView)合成的多层图像树。 | 由 Surface 承载的多层图像树。 | 一张统一的、由Flutter引擎绘制的单层位图(纹理)。 |
| 核心优势 | 极致优化:与硬件/OS深度集成,能效比高。体验统一:100%符合iOS设计规范与交互细节。 | 硬件兼容:通过驱动适配各种硬件。生态开放:图形栈各层可定制。 | 绝对一致:像素级跨平台一致性。性能可控:消除平台渲染差异,动画与滚动流畅度可自主优化到底。 |
| 主要代价/挑战 | 封闭:技术锁定Apple生态,无法跨平台。 | 碎片化:硬件与驱动差异导致图形行为偶有不一致。 | 平台特性损耗:需额外工作模拟或接入平台特有UI效果(如文本选择菜单、系统滚动条)。 |
核心差异与架构选择
通过对比,我们可以得出三个核心结论,它们直接影响您的技术选型:
- 架构哲学完全不同
-
- 原生:是 “集成式” 架构。UI框架、合成器、图形驱动紧密集成在操作系统中,不可分割。
- Flutter:是 “便携式” 架构。它自带一个完整的、可插拔的图形运行时,将自己“嵌入”到各个宿主平台中。
- Flutter 如何实现“覆盖”你可以将Flutter应用想象成一个全屏播放的、极高帧率的视频播放器。平台(iOS/Android)只负责提供“播放窗口”(UIView/SurfaceView)和通知事件(触摸、生命周期),而“视频”的每一帧内容(即整个UI)完全由Flutter引擎计算并绘制。它不理会平台当前原生的UI是什么,它只输出自己的画面。
- 如何根据渲染需求做技术选型
-
- 选择原生开发,当你的应用极度依赖平台的原生特性与完美融合感(例如需要深度定制系统键盘、使用精准的平台无障碍服务、或要求与最新OS设计语言无缝同步)。
- 选择 Flutter,当你的项目将“多端 UI 绝对一致”和“复杂自定义动画的性能”置于最高优先级,且愿意为适配平台特性(如边缘手势、原生组件嵌入)支付额外的开发成本。
Skia与Impeller技术对比解析
那么最新的flutter已经升级到Impeller 渲染,而android使用的skia,那我是否可以认为,最新版本的flutter比android的渲染效率要高?
核心结论
这本质上是 “为特定场景优化的新引擎” 与 “通用、成熟但需兼容旧设备的系统引擎” 之间的对比。
下面的表格清晰地展示了二者的核心差异:
Impeller优化提升场景
🎨 1. 高频实时绘画与手写
这正是“高频绘画”的典型场景。
- 业务举例:绘图应用、白板协作、签名、笔记类应用。
- 传统问题 (Skia JIT):用户每一笔划过屏幕,都可能触发新的路径绘制和着色器编译。在复杂笔刷(如带有纹理、混色效果)下,首笔或变化笔刷时的卡顿感非常明显,破坏创作流畅度。
- Impeller优势:所有笔刷效果的着色器均已预编译。无论用户如何快速、多变地绘画,每一笔都能即时、稳定地渲染,真正做到“笔随心动”,帧率稳如直线。
✨ 2. 复杂动画与矢量图形
- 业务举例:加载动画、图标变形(Lottie类动画)、页面过渡特效、数据可视化图表。
- 传统问题:任何非矩形、带渐变、阴影、裁剪的动画,在首次渲染时都需要编译着色器。导致动画启动时掉帧,或复杂图表交互时响应不及时。
- Impeller优势:动画所需的着色器早已就绪。动画从第一帧开始就是全速的,即使是非常复杂的矢量路径动画,也能保持始终如一的高帧率。
👆 3. 重度手势驱动的交互
- 业务举例:地图缩放拖拽、图片/视频缩放浏览、可拖拽排序的列表、3D模型查看器。
- 传统问题:手势事件每秒触发数十次,UI需实时更新。若更新过程中遭遇着色器编译,就会导致触控反馈与视觉更新脱节,感觉“不跟手”。
- Impeller优势:渲染管线全程无编译中断,能极快地响应每一帧手势更新,实现“丝滑般”的跟手体验,延迟感大幅降低。
📜 4. 快速滚动的复杂列表
- 业务举例:社交媒体信息流(每条动态样式不一)、电商商品瀑布流、聊天列表(含各种气泡、图片)。
- 传统问题:在快速滚动时,不断出现的新Item样式可能触发编译,导致滚动中突然的、无法预测的微小卡顿。
- Impeller优势:列表Item的渲染方式(圆角、阴影、渐变)已预知,滚动过程中的渲染成本是恒定且可预测的,从而维持高速滚动的绝对流畅。