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

相关推荐
LinXunFeng4 小时前
Flutter - 使用本地 DevTools 验证 SVG 加载优化
flutter·性能优化·svg
上海大哥9 小时前
Flutter 实现工程组件化(Windows电脑操作流程)
前端·flutter
风·之痕11 小时前
Flutter Packge - 组件应用
flutter·packge
二哈喇子!1 天前
v-model双向绑定指令
flutter
吴Wu涛涛涛涛涛Tao1 天前
用 Flutter 实现一个「类 Instagram」Feed 列表页
flutter
叽哥2 天前
flutter学习第 8 节:路由与导航
android·flutter·ios
叽哥2 天前
flutter学习第 7 节:StatefulWidget 与状态管理基础
android·flutter·ios
落魄的Android开发2 天前
FLutter 如何在跨平台下实现国际化多语言开发
flutter
libo_20252 天前
HarmonyOS5原生开发 vs. Flutter:谁更适合你的项目?
flutter