当前位置: 首页 > news >正文

Flutter的三棵树

“三棵树”是 Flutter 渲染和构建UI的核心机制,理解它们对于掌握 Flutter 至关重要。这三棵树分别是:

  1. Widget 树

  2. Element 树

  3. RenderObject 树

它们协同工作,以实现 Flutter 的高性能渲染和高效的响应式编程模型。

Flutter 是声明式的UI,它只需要描述UI是什么样的,而不需要一步步地指挥框架如何去构建和更新这个界面。

为了更好的理解可先了解:命令式 UI 和 声明式 UI

一、Flutter的三棵树

1. Widget 树 (What to render)

  • 是什么:Widget 是你用代码声明的UI配置。它是一个不可变的(immutable)描述,告诉 Flutter 这部分UI应该长什么样子。你可以把它看作是一份蓝图

  • 特点

    • 轻量级:Widget 本身并不负责实际的渲染或状态管理,它只持有最终的配置信息(如颜色、字体、尺寸等)。

    • 不可变:一旦创建就不能修改。当UI需要变化时,你必须创建一个新的 Widget。这种immutability使得Widget的创建和销毁非常快速。

    • 组合性:复杂的UI由无数个简单的小Widget嵌套组合而成(如Column > Row > Container > Text)。

例子:这段代码就定义了一棵小小的 Widget 树。

Container( // Widgetcolor: Colors.blue,child: Center( // Widgetchild: Text('Hello World'), // Widget),
);

2. Element 树 (How to render & Where)

  • 是什么:Element 是 Widget 在UI树中具体位置的实例化体现。它是连接 Widget 和 RenderObject 的粘合剂,负责管理UI的更新和生命周期。

  • 特点

    • 可变且有状态:Element 是长寿命的,在UI重建时会持续存在(只要同一个位置的runtimeTypekey没变)。

    • 职责

      1. 挂载:它持有对对应 Widget 和 RenderObject 的引用。

      2. 比较:当UI重建,新的 Widget 树到来时,Element 会负责将新的 Widget 与它当前持有的旧 Widget 进行对比(Widget.canUpdate)。

      3. 更新:如果新的 Widget 和旧的 Widget 是同一类型(runtimeTypekey相同),Element 会更新自己持有的 Widget 引用,并告诉 RenderObject 是否需要更新(reconfigure)。

      4. 重建:如果对比失败,Element 会销毁旧的并创建新的 Element 和 RenderObject。

简单来说,Element 决定了是复用现有的UI结构,还是销毁重建。

3. RenderObject 树 (Actually rendering)

  • 是什么:RenderObject 是真正负责布局(Layout)和绘制(Paint) 的核心组件。它计算每个UI元素的大小和位置,并将它们绘制到屏幕上。

  • 特点

    • 重量级:布局和绘制的计算成本很高,因此 RenderObject 的创建和更新需要非常谨慎。

    • 核心方法

      • performLayout():计算自身和子节点的大小和位置。

      • paint():将自己绘制到画布(Canvas)上。

    • 持久化:只要有可能,Flutter 会极力避免重新创建和重新布局 RenderObject,以保持渲染性能的流畅。

大多数开发者通常不直接操作 RenderObject,而是通过熟悉的 Widget(如ContainerStackAlign)来间接使用它们。

二、三棵树如何协同工作?

让我们通过一个简单的计数器例子来看整个流程:

初始构建阶段

  1. 你编写了 MyHomePage Widget 树。

  2. Flutter 遍历你的 Widget 树,自上而下地创建对应的 Element

  3. 每个 Element 又会调用 Widget 的 createRenderObject() 方法,创建相应的 RenderObject

  4. 三棵树都构建完毕,RenderObject 树进行布局和绘制,UI显示在屏幕上。

更新阶段

(当你按下按钮,counter增加)

​​​​​​​​​​​​​​
  1. setState(() { _counter++; }) 被调用,标记该 StatefulWidget 的 Element 为“脏”状态。

  2. 下一帧到来时,Flutter 会触发重建对应的 Widget 子树。build 方法被再次调用,返回一棵新的 Text($_counter) Widget。

  3. 关键的对比过程(Diff)

    • 对应的 Element 会拿着这个新的 Text Widget,与它当前持有的旧的 Text Widget 进行比较。

    • 它发现两者的 runtimeType 都是 Text,并且都没有设置 key,所以可以更新。

  4. 高效的更新

    • Element 简单地更新它持有的 Widget 引用为新的 Widget。

    • 然后,Element 会通知它对应的 RenderObject:“配置有变化,你需要更新了”。

    • RenderObject 检查发现只是文本内容变了,它可能会标记自己需要重绘(repaint),但通常不需要重新布局(relayout)(因为文字大小可能没变)。

  5. 下一帧,RenderObject 只进行必要的重绘,新的数字就显示出来了。

三、为什么需要三棵树? (优点)

  1. 性能优化:将轻量级的、不可变的 Widget 与重量级的、可变的 RenderObject 分离。UI的频繁重建(创建新Widget)成本极低,而真正昂贵的布局和绘制过程只有在必要时才进行。

  2. 高效的响应式编程:通过 Element 树的 Diff 算法,Flutter 可以精确地知道UI的哪一部分发生了变化,从而只更新必要的 RenderObject,而不是整个界面。这比传统的命令式UI(如Android/iOS原生)手动操作View要高效得多。

  3. 逻辑与渲染分离:开发者只需关心如何用 Widget 描述UI(声明式),而无需关心具体的渲染细节和更新逻辑,框架帮你处理了所有复杂性。

四、总结

角色特点职责
Widget 树蓝图/配置轻量、不可变描述UI元素应该是什么样子
Element 树粘合剂/管理者可变、长寿命管理Widget的更新,决定是复用还是重建UI
RenderObject 树渲染工人重量级、持久负责实际的布局、绘制工作,计算尺寸和位置,渲染到屏幕

简单记忆Widget 是配置,Element 是管家,RenderObject 是干活的。 管家(Element)根据新的图纸(Widget)来决定是让工人(RenderObject)在原基础上修改,还是直接换一个新工人。


文章转载自:

http://3bgETGXq.ywqsk.cn
http://vN0yW0sc.ywqsk.cn
http://CbdGR0ku.ywqsk.cn
http://8yeELYDC.ywqsk.cn
http://qT7tb084.ywqsk.cn
http://66r2MGLn.ywqsk.cn
http://cwasuvTH.ywqsk.cn
http://md5d9Gpj.ywqsk.cn
http://v7caJsMK.ywqsk.cn
http://lOk5w1EV.ywqsk.cn
http://EiTZqZ83.ywqsk.cn
http://CFXPKjH3.ywqsk.cn
http://69iGx1jM.ywqsk.cn
http://5iXU6Y3f.ywqsk.cn
http://AgdEeyOL.ywqsk.cn
http://zPXkYZXh.ywqsk.cn
http://5JAfLNjG.ywqsk.cn
http://hTt0SLGk.ywqsk.cn
http://EZhwEWBm.ywqsk.cn
http://dT0BQQRu.ywqsk.cn
http://WTq92xQK.ywqsk.cn
http://xT7z9yPy.ywqsk.cn
http://UvyR6Jwp.ywqsk.cn
http://a1c4LHei.ywqsk.cn
http://snqKGsio.ywqsk.cn
http://8nOISeVN.ywqsk.cn
http://eNbcknAs.ywqsk.cn
http://IAGaXCNN.ywqsk.cn
http://qWy1oQRU.ywqsk.cn
http://vP6SkKDb.ywqsk.cn
http://www.dtcms.com/a/368203.html

相关文章:

  • React 样式隔离核心方法和最佳实践
  • 踩坑实录:Django继承AbstractUser时遇到的related_name冲突及解决方案
  • 【Flutter】flutter_local_notifications并发下载任务通知实践
  • 覆盖Transformer、GAN:掩码重建正在重塑时间序列领域!
  • 数据结构基础之队列:数组/链表
  • 数据可视化工具推荐:5款让图表制作轻松上手的神器
  • 【网安基础】--ip地址与子网掩码
  • spring AI 的简单使用
  • 【yolo】YOLOv8 训练模型参数与多机环境差异总结
  • 算法(keep learning)
  • C/C++中的可变参数 (Variadic Arguments)函数机制
  • 深度学习:CNN 模型训练中的学习率调整(基于 PyTorch)
  • Mattermost教程:用Docker搭建自己的开源Slack替代品 (团队聊天)
  • Electron 性能优化:内存管理和渲染效率
  • 数字隔离器,新能源汽车PTC中的“电气安全卫士”
  • 2025 汽车租赁大会:九识智能以“租赁+运力”革新城市智能配送
  • 云原生部署_Docker入门
  • javaweb(【概述和安装】【tomeat的使用】【servlet入门】).
  • 基于SpringBoot的社区智能垃圾管理系统【2026最新】
  • 基于飞算JavaAI的在线图书借阅平台设计实现
  • dbeaver工具连接inceptor星环数据库
  • Linux内核网络安全序列号生成机制解析
  • Buzz语音转文字:开源神器,高效记录会议
  • Docker 容器核心指令与数据库容器化实践
  • 自制扫地机器人 (五) Arduino 手机远程启停设计 —— 东方仙盟
  • docker 安装kafaka常用版本
  • Pytorch Yolov11 OBB 旋转框检测+window部署+推理封装 留贴记录
  • PyTorch 中.backward() 详解使用
  • conda配置pytorch虚拟环境
  • Conda环境隔离和PyCharm配置,完美同时运行PaddlePaddle和PyTorch