Flutter中渲染过程
flutter三个部分
首先在App开发的时候, 我们就会关注一个问题: 如何结构化地组织视图数据, 提供给渲染引擎, 最终完成界面显示。
flutter对视图树的概念进行一些扩展, 它将视图数据的组织和渲染抽象为三个部分分别是widget,element,renderobject
这三者之间的关系为:
- 首先通过Widget树生成对应的Element树
- 创建相应的RenderObject并关联到Element.renderObject属性上
- 最后, 构建成RenderObject树, 以完成最终的渲染
widget
其中widget叫做组件, 它是flutter这个框架的基石,因为它就是用来描的数据,
widget不可变的, 如果需要修改widget, 那么渲染引擎就会对它进行重新渲染。但是这样会导致框架的效率变得很低。
element
element可以看成是一个中间件, 它是连接widget和renderobject的工具, 我们可以使用widget树中的每个控件创建不同类型的渲染对象, 组成渲染对象树。
其实我们是直接可以由widget命令renderObject去干活不好吗?
答案是可以的, 但是这样会增加性能损耗, 主要的原因前面说过widget具有不可变性, 但是Element却是可变的。实际上, Element树这一层将widget树的变化做了抽象, 可以将真正需要修改的部分同步到真实的renderObject树中, 最大程度降低对真实渲染视图的修改, 提高渲染效率, 而不是销毁整个渲染视图树重建。
renderobject
渲染对象树的使用在RenderObject阶段中有四个阶段, 分别是布局, 绘制, 合成和渲染, 其中布局和绘制在RenderObject中完成, flutter采用深度优先遍历渲染对象树, 确定树中各个对象的位置和尺寸, 并将它们绘制到不同的图层上, 绘制完成之后, 最终将会交给skia去做。
renderObjectWidget
widget中的statelessWidget和statefulwidget只是用来组装控件的容器, 但是并不负责组件最后的布局和绘制, 在Flutter中, 布局和绘制工作实际上是在Widget的另外一个子类RenderObjectWidget内完成的。
abstract class RenderObjectWidget extends Widget { @override RenderObjectElemnet createElement(); @override RenderObject createRenderObject(BuildContext context); @protected void updateRenderObject(BuildContext contxet, covariant RenderObject renderObject) }
renderObjectWidget是一个抽象类, 可以看到, 这个类中同时拥有创建Element, RenderObject, 以及更新RenderObject, 以及更新RenderObject的方法。
但实际上, renderObjectWidget本身并不负责这些对象的创建与更新。
首先对于Element的创建, flutter会遍历widget树时, 调用createElement去同步widget自身配置, 从而生成对应节点的Element对象, 而对于RenderObject的创建与更新,其实是在RenderObjectElement类中完成的。
abstract class RenderObjectElemnet extends Elements { RenderObject _renderObject; @override void mount(Elements parent, dynamic newSlot) { super.mount(parent, newSlot); _renderObject = widget.createRnederObject(this); attachRenderObject(newSlot); _dirty = false; } @override void update(convariant RenderObjectWidget newWidget) { super.update(newWidget); widget.updateRenderObject(this, renderObject); _dirty = false; } ... }
在Element创建完毕后, flutter会调用Element的mount方法, 在这个方法里面, 会完成与之关联的RenderObject对象的创建, 以及与渲染树的插入工作, 插入到渲染树后的Element就可以显示到屏幕中了。
如果widget的配置数据发生了改变, 那么持有该widget的Element节点也会被标记为dirty, 在下一个周期的绘制时, flutter就会触发Element树的更新, 并使用最新的widget数据更新自身以及关联的RenderObject对象, 接下来便会进入layout和Paint的流程, 而真正的绘制和布局过程, 则完全交由RenderObject完成
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { void layout(Constraints constraints, { bool parentUsesSize = false }) {} void paint(PaintingContext context, Offset offset) {} }
布局和绘制完成之后, 接下来的事情就交给了skia, 在Vsync信号同步时, 直接从渲染树合成bitmap, 然后交给Gpu。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!