Flutter 三棵树

Flutter 三棵树

Widget :配置与描述

  • 它是一个**不可变(immutable)**的蓝图。你不能在运行时去修改一个Widget的属性,你只能用一个新的Widget来替换它。
  • 它告诉Flutter:"我想要一个长什么样的UI元素,它应该有什么样的配置(颜色、文本、子组件等)。"
  • 轻量级 :创建和销毁Widget对象的成本极低。

Element:对比、生命周期管理与上下文

  • 对比 (Diffing) :这是它的核心职责之一。当新的Widget蓝图下来时,Element负责将新旧Widget进行对比,决定是更新 现有节点还是销-毁并重建 。这是key发挥作用的地方。
  • 生命周期管理 (Lifecycle Management)Element才是真正"活着"的对象。它管理着状态(对于StatefulWidget)和生命周期(mount, unmount, deactivate)。我们熟悉的State对象就是由Element持有和管理的。
  • 上下文 (Context) :每个Element都是一个BuildContext。当我们在build方法中使用context时(例如Theme.of(context)),我们实际上是在向Element树查询信息。它扮演着一个"大管家"和"信息中心"的角色,连接着树中的各个节点。

RenderObject:布局计算、绘制、合成、命中测试

  • 计算 (Layout) :它负责所有尺寸和位置的计算。它会执行一个"2-pass layout"过程:
    1. 父节点向子节点传递约束("你最多可以这么宽/高")。
    2. 子节点根据约束计算自己的尺寸,并报告给父节点。
    3. 父节点根据子节点的尺寸,确定它们最终的位置。
  • 渲染 (Painting & Compositing)
    • Painting : 计算完成后,RenderObject负责将自己"画"出来。
    • Compositing: 对于复杂的UI,它还会创建"图层"(Layers),将绘制好的内容组合起来,以便GPU可以高效地处理和渲染,实现流畅的动画和效果。
  • 命中测试 (Hit Testing) :当你点击屏幕时,是RenderObject树负责判断你到底点中了哪个UI元素。

例子

好的,我们继续用盖房子的比喻来讲述当一个布局改变时,这三棵树是如何协同工作的。

假设我们原来的布局是这样的(这是我们的旧蓝图 Widget):```dart// 旧蓝图Column( children: [ Text('你好'), Icon(Icons.star), ],)

复制代码
现在,您修改了代码,想要改变布局,把`Column`(垂直排列)换成`Row`(水平排列)。

这是我们的**新蓝图 `Widget` 树**:
```dart
// 新蓝图
Row(
  children: [
    Text('你好'),
    Icon(Icons.star),
  ],
)

当您保存代码并触发热重载(Hot Reload),或者调用setState时,Flutter的更新流程就开始了。


更新流程详解

第1步:Flutter拿到新蓝图,开始与"施工队"核对

Flutter框架拿到了你的新Widget树(Row...),然后它会找到对应的"施工队"------Element

它从Element树的根节点开始,一层一层地向下对比新旧Widget

第2步:Element树的对比与决策(最关键的一步)

Element施工队非常聪明,它遵循两个核心原则:

  1. 如果新旧Widget类型key相同 ,就复用 这个Element,只更新Widget的配置。
  2. 如果新旧Widget类型key不同 ,就丢弃 旧的Element(以及它下面的所有子Element),创建一个全新的Element

现在,让我们看看施工队对比的过程:

  • 对比第一层

    • 旧蓝图Column
    • 新蓝图Row
    • 施工队(Element)决策"类型不同!" (Column != Row)。 "好了,旧的Column施工员和它手下所有的人(包括TextIcon的施工员)全部解雇,一个不留!"
    • 动作
      1. Column对应的Element被标记为"待销毁"(deactivate)。
      2. Column对应的RenderObject(负责垂直布局的工人)也被通知"你可以下班了"。
      3. 所有子ElementTextIcon的)和它们对应的RenderObject也一并被销毁。
      4. 根据新的Row蓝图,创建一个全新的RowElement
  • 对比第二层(在新的Row Element下进行)

    • 新的Row施工员(Element)开始为它的孩子们创建骨架。
    • 它看到新蓝图里有Text('你好')Icon(Icons.star)
    • 于是,它创建了全新的TextElement 全新的IconElement
第3步:Element树指挥RenderObject树进行施工

现在,新的Element骨架已经搭建好了,它们需要去指挥"实体工人"(RenderObject树)干活。

  • 新的RowElement会创建一个新的RenderFlex对象(这是RowColumn背后的布局工人),并告诉它:"你的任务是水平排列direction: Axis.horizontal)。"
  • 新的TextElement会创建一个新的RenderParagraph对象,并告诉它:"去把'你好'画出来。"
  • 新的IconElement会创建一个新的RenderParagraph对象(图标在底层也是通过字体文件绘制的),并告诉它:"去把'星星'这个图标画出来。"
第44步:RenderObject树执行布局和绘制

现在,全新的RenderObject工人们开始工作:

  1. 布局 (Layout)
  • RowRenderObject会问它的孩子们(TextIconRenderObject):"你们各自想占多大地方?"
  • TextIconRenderObject计算并报告自己的尺寸。
  • RowRenderObject拿到孩子们的尺寸后,按照水平方向将它们依次摆放好,计算出它们在屏幕上的最终坐标。
  1. 绘制 (Paint)
  • 布局完成后,Flutter的渲染引擎会说:"好了,所有东西的位置和大小都定下来了,开画!"
  • 它会遍历RenderObject树,依次调用每个工人的paint方法,让它们把自己画在屏幕上。Text画出文字,Icon画出图标。

最终,你在屏幕上看到了从垂直布局变成水平布局的新界面。


如果只是改变属性,而不是类型呢?

如果我们只是把Text('你好')改成Text('再见')Column不变。

  • Element对比
    • 第一层 :旧Column vs 新Column类型相同!复用 ColumnElement
    • 第二层 :旧Text vs 新Text类型相同!复用 TextElement
  • Element决策
    • ColumnElement被复用,它对应的RenderObject被复用
    • TextElement被复用,它对应的RenderObject被复用
  • 更新操作
    • 被复用的TextElement发现它的配置变了(文字内容从"你好"变成了"再见")。
    • 它会告诉它管理的RenderObject:"嘿,别的都不用变,把墙上的字擦了,重新画上'再见'就行了。"
  • RenderObject工作
    • TextRenderObject只需要重新计算一下新文字的大小(可能需要重新布局) ,然后重新绘制这部分文字。
    • ColumnRenderObject因为孩子的大小可能变了,所以也需要重新布局,但它本身不需要重绘。

总结

  • 改变布局类型 (如Column -> Row) :会导致Element树和RenderObject树在改变点大规模地销毁和重建,开销较大。
  • 只改变属性 (如color, text) :会尽可能地复用ElementRenderObject,只在必要时更新属性、重新布局或重绘,开销小得多。

这就是为什么Flutter鼓励我们使用小的、组合的Widget,并将状态管理放在尽可能低的层级,因为这样可以把UI更新的范围限制在最小,从而获得最佳性能。

Key的作用

帮助Flutter在Widget树更新时,更精确地识别和匹配Element

相关推荐
LawrenceLan13 小时前
Flutter 零基础入门(九):构造函数、命名构造函数与 this 关键字
开发语言·flutter·dart
一豆羹13 小时前
macOS 环境下 ADB 无线调试连接失败、Protocol Fault 及端口占用的深度排查
flutter
行者9613 小时前
OpenHarmony上Flutter粒子效果组件的深度适配与实践
flutter·交互·harmonyos·鸿蒙
行者9616 小时前
Flutter与OpenHarmony深度集成:数据导出组件的实战优化与性能提升
flutter·harmonyos·鸿蒙
小雨下雨的雨16 小时前
Flutter 框架跨平台鸿蒙开发 —— Row & Column 布局之轴线控制艺术
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨17 小时前
Flutter 框架跨平台鸿蒙开发 —— Center 控件之完美居中之道
flutter·ui·华为·harmonyos·鸿蒙
小雨下雨的雨17 小时前
Flutter 框架跨平台鸿蒙开发 —— Icon 控件之图标交互美学
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨18 小时前
Flutter 框架跨平台鸿蒙开发 —— Placeholder 控件之布局雏形美学
flutter·ui·华为·harmonyos·鸿蒙系统
行者9618 小时前
OpenHarmony Flutter弹出菜单组件深度实践:从基础到高级的完整指南
flutter·harmonyos·鸿蒙
前端不太难19 小时前
Flutter / RN / iOS,在长期维护下的性能差异本质
flutter·ios