Skip to content

三棵树与页面更新 ​

三棵树各自保存什么 ​

层次保存的信息生命周期与职责
Widget不可变的界面配置构建时可以频繁创建,用于描述想要的 UI
Element组件在树中的位置、配置关联和依赖管理挂载与更新,BuildContext 由 Element 实现
RenderObject布局、绘制、命中测试等所需的信息在配置更新后按需要重新布局或绘制

这三层并非逐节点一一对应。StatelessWidget、StatefulWidget 等组合组件通过构建其他 Widget 完成工作,只有对应的渲染组件会直接创建或更新 RenderObject。

可以用 React 的声明式 UI 帮助入门,但 Flutter 的更新与绘制由自己的框架和引擎协作完成。准确理解仍应回到 官方架构说明 和 SDK 源码。

一次状态更新的路径 ​

以点击按钮后执行 setState(() => count++) 为例,阅读时可以按以下顺序设置断点:

  1. State.setState 同步执行回调,状态数据发生变化。
  2. 对应 Element 被标记为需要重建,框架安排后续帧处理。
  3. 构建阶段执行需要更新的 build,生成新的 Widget 配置。
  4. Element 更新子节点;通常结合位置、runtimeType 和 key 判断能否复用已有节点。
  5. RenderObject 接收属性更新,只在需要时进入布局、绘制与合成流程。

因此 setState 并不等于立刻重画整个屏幕。重建范围与 State 的位置、子树结构和依赖有关;新建 Widget 配置也不等于一定创建新的渲染对象。

const 有助于稳定不变的配置,但不会消除所有由依赖变化触发的工作。优化前先记录是哪一阶段耗时,再选择改动。

启动与一帧的工作 ​

应用由 main 进入 runApp,初始化必要绑定并挂载根组件。框架在帧调度下处理构建、布局、绘制、合成与语义等工作,随后由引擎和平台完成后续显示。

阶段重点问题调查线索
构建哪些 Element 被标脏?为何重建?setState、依赖与父级配置更新
布局约束是什么?尺寸怎样确定?RenderBox、performLayout、Flex
绘制哪些对象需要重绘?markNeedsPaint、paint、边界
合成与栅格化图层如何提交并转成像素?Layer、Scene、引擎时间线

布局规则对应 约束笔记。渲染后端随平台和版本不同,分析时应记录实际环境;不要将所有平台都概括为同一种 Skia 流程。当前后端支持情况参见 Impeller 文档。

从事件到页面变化 ​

指针事件先经过命中测试找到相关对象,手势识别器参与竞技场,最终由适合的回调处理点击或拖动。业务回调再更新状态,进入前面的页面更新路径。

路由侧可以先观察 Navigator 管理的栈、Route 的进入退出及其过渡。带深链接的应用还要关注路由配置如何与外部 URL 对应。实践入口见 事件与路由。

源码阅读清单 ​

源码符号与位置可能随版本变化。先在自己的 Flutter SDK 中搜索符号,再结合断点验证,而不是只记忆一条固定调用链。

想回答的问题起点符号常见源码位置
如何启动应用?runApp、WidgetsFlutterBindingpackages/flutter/lib/src/widgets/binding.dart
状态更新怎样安排重建?State.setState、Element.markNeedsBuild、BuildOwner.buildScopepackages/flutter/lib/src/widgets/framework.dart
一帧如何调度?SchedulerBinding、WidgetsBinding.drawFramescheduler/binding.dart、widgets/binding.dart
布局和绘制如何执行?PipelineOwner、RenderObject、RenderBoxrendering/object.dart、rendering/box.dart
手势如何决出响应者?GestureBinding、GestureArenaManagergestures/binding.dart、gestures/arena.dart
页面如何入栈与退出?NavigatorState、Routewidgets/navigator.dart、widgets/routes.dart

上表后四行省略了共同前缀 packages/flutter/lib/src/。在线阅读可从 Flutter 官方仓库 进入,分析本地问题时则切换到与当前 SDK 一致的版本。

练习与验收 ​

为一个有状态的列表项打印 initState、build 与 dispose,对比没有 Key、索引 Key 和业务 ID Key 时的重排结果。再为一次点击设置 setState 断点,说明哪些节点复用、哪些配置更新,以及为什么不是每次都要重新布局。

持续记录,持续理解。