【Flutter】Flutter 组件 ④ ( 组件渲染 的 三棵树理论 | Widget 树 → Element 树 → RenderObject 树 )

文章目录

  • [一、组件渲染 的 三棵树理论 ( Widget 树 → Element 树 → RenderObject 树 )](#一、组件渲染 的 三棵树理论 ( Widget 树 → Element 树 → RenderObject 树 ))
    • [1、Widget 树](#1、Widget 树)
      • [Widget 分类](#Widget 分类)
      • [Widget 本质体现](#Widget 本质体现)
      • 核心特点
    • [2、Element 树](#2、Element 树)
    • [3、 RenderObject 树](#3、 RenderObject 树)
  • 二、三者之间的关系
  • [三、StatelessWidget 体系中的三棵树](#三、StatelessWidget 体系中的三棵树)
    • [1、StatelessWidget 基础概念](#1、StatelessWidget 基础概念)
    • [2、StatelessWidget 运行流程](#2、StatelessWidget 运行流程)
    • [3、StatelessWidget 运行流程](#3、StatelessWidget 运行流程)
    • [4、StatelessWidget 基础代码结构分析](#4、StatelessWidget 基础代码结构分析)
    • [5、StatelessWidget 运行流程](#5、StatelessWidget 运行流程)
    • [6、BuildContext 参数](#6、BuildContext 参数)
    • [7、StatelessWidget 生命周期与三棵树对应关系](#7、StatelessWidget 生命周期与三棵树对应关系)
      • [阶段 1 : 组件被创建 ------ 构造函数执行](#阶段 1 : 组件被创建 —— 构造函数执行)
      • [阶段 2 : 组件被挂载 ------ Framework 暗箱操作](#阶段 2 : 组件被挂载 —— Framework 暗箱操作)
      • [阶段 3 : 首次绘制界面 ------ build () 执行](#阶段 3 : 首次绘制界面 —— build () 执行)
      • [阶段 4 : 界面更新 ------ build () 再次执行](#阶段 4 : 界面更新 —— build () 再次执行)
      • [阶段 5 : 组件销毁 ------ 移除树](#阶段 5 : 组件销毁 —— 移除树)
  • [四、StatefulWidget 体系中的三棵树](#四、StatefulWidget 体系中的三棵树)
    • [1、StatefulWidget 概念](#1、StatefulWidget 概念)
    • [2、StatefulWidget 代码组成](#2、StatefulWidget 代码组成)
    • [3、StatefulWidget 生命周期与三棵树对应关系](#3、StatefulWidget 生命周期与三棵树对应关系)
    • [4、初始化阶段 与 更新阶段 生命周期 汇总对照表](#4、初始化阶段 与 更新阶段 生命周期 汇总对照表)

参考文档 :

一、组件渲染 的 三棵树理论 ( Widget 树 → Element 树 → RenderObject 树 )


关系链条 : Widget ( 描述 ) → 创建 / 更新 → Element ( 实例 ) → 创建 / 更新 → RenderObject ( 渲染 )

树名称 身份定位 核心作用 生命周期
Widget 树 配置文件 / 蓝图 描述页面长什么样 ( 数据 + 结构 ) 极短 , 每次 build 都会新建
Element 树 中间代理人 / 实例 持有 Context 和 State , 管理生命周期 长 , 尽量复用、不轻易销毁
RenderObject 树 渲染实体 真正 测量、布局、绘制 到屏幕 最长 , 必须复用 , 性能开销最大

把 Flutter 页面当成一栋房子 , 三棵树 分工不同、层层协作 :

  • Widget 树 = 整套设计图纸 , 描述 UI 长啥样 , 频繁更新、不可变 ;
  • Element 树 = 现场施工管理员 ( 工长 ) , 中间调度、状态载体、优先复用 , 三树核心 ;
  • RenderObject 树 = 实际砌好的墙体 / 门窗 / 家具 ( 真实建筑 ) , 负责布局、绘制、交互 , 最终展示 ;

页面从无到有、刷新更新 , 就是 " 图纸 → 工长指挥 → 建成实物 " 的过程 ;

1、Widget 树

Widget 是 描述层 , 描述配置 / 模板 , 其 本质 是 不可变的 " 配置描述类 " , 是纯数据 , 无渲染能力 ;

  • 不可变 : Widget 是不可变的 ( immutable ) 纯数据类 , 其 属性全部是 final 修饰的 , 一旦创建不能修改 ;
  • 不修改 : 状态变化不会修改旧 Widget , 而是 新建 Widget ;
  • 图纸 : Widget 不是真正的页面 , 只是 描述页面应该长什么样的配置信息 ;
  • 重建: 每次 调用 build() 方法 , 都会 重新创建一个新的 Widget ;
  • 轻量 : 一个 Widget 只有几十个字节 , 重建对性能无压力 ;
  • 无状态 : 不存储数据 , 不管理生命周期 , 不负责渲染 ;

每次 setState 或者 父组件重建 , 大概率会新建一批 Widget 实例 ;

Widget 分类

所有 Widget 继承自 DiagnosticableTreeWidget , 分两大类 :

  • StatelessWidget : 无 可变状态 , build 只依赖入参 ;
  • StatefulWidget : 携带 可变状态 , 自身依然不可变 ;

Widget 本质体现

Widget 树 是 设计图纸 , 只写规则 , 不是实物 ;

代码中写的 Container、Text、Button、Column 等所有组件代码 , 层层嵌套 , 组成了 Widget 树 ;

下面的 嵌套组件 代码 就是 Widget 树 ;

dart 复制代码
// 这只是一份配置 ; 每次 build 都会生成新的 Scaffold/Text 实例
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
		Column(
		  children: [
		    Text("你好"),
		    ElevatedButton(child: Text("点击"))
		  ]
		)
    ),
  );
}

核心特点

Widget 树 核心特点 :

  • 描述信息 : 纯描述 , 静态、不可修改 ;
  • 建筑图纸 : 只画样式、大小、颜色、位置、用什么材料 , 图纸本身不能住人、不能用 ;
  • 复用规则 : 每次页面刷新、setState , 都会 重新生成一版新图纸 ( 新 Widget ) , 旧图纸直接丢掉 ;
  • 渲染能力 : 没有渲染能力 , 自己没法显示在屏幕上 ;

2、Element 树

Element 树 是 实例层 , 是 挂载 & 调度中枢 , 是 Widget 的 " 实例代理 " , 框架内部核心调度单元 , 是 连接 Widget 和 RenderObject 的 桥梁 ;

  • 实体对象 : 代码中的 BuildContext 的 本质就是 Element 树的实体对象 ;
  • 创建 Element : 在 Framework 层 调用 Widget.createElement() 函数创建 , 对应 Element 树 , 长期存活 ( 页面不销毁就尽量复用 ) ;
  • Element 分类 对应 Widget :
    • StatelessElement → 绑定 StatelessWidget
    • StatefulElement → 绑定 StatefulWidget + 持有 State
  • Element 是 Widget 的实例化对象 , 是三棵树的核心中枢 ;
  • Element 持有 Widget 配置 , 同时持有 State 状态 , 还持有对应的 BuildContext ;
  • 跨页面、跨组件共享的状态、GlobalKey , 全都存在 Element 里 ;
  • 它负责 对比新旧 Widget , 决定 是否复用 / 创建 RenderObject ;
dart 复制代码
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
    );
  }
}

通俗理解

Element 树 是 现场工长 , 负责 中间调度、核心枢纽 ;

Flutter 拿到 Widget ( 图纸 / 代码 ) 后 , 自动 为每一个 Widget 生成一个对应的 Element , 层层嵌套形成 Element 树 ;

Element 树 是 图纸 ( Widget 树 ) 和 实物 ( RenderObject 树 ) 之间的中间人 , 也是三棵树里生命周期最长、最核心的一环 ;

核心特点

Element 树 核心特点 :

  • 绑定关系 : 一张图纸 (Widget) → 对应一位工长 (Element) ;
  • 复用规则 : Element 树 能复用 , 不随便销毁 ;
    • 重建时机 : 图纸换新 ( Widget 重建 ) , 只要结构差别不大 , 工长继续留任 , 不用换人 ;
    • 高性能 : 这就是 Flutter 性能高的关键 , 不反复创建销毁管理人员 ;

主要工作

Element 树 主要工作 :

  • BuildContext 就是 Element : 在组件里用的 context , 本质就是当前组件的 Element ;
  • 持有对应 Widget 引用 , 触发 build() 方法 , 生成子 Widget , 拿着最新图纸 , 指挥下一步施工 ;
  • 树的结构固定 : Element 树结构 一旦生成 , 不会轻易改变 ( Widget 重建但 Element 复用 ) ;
  • 树更新时做 Diff 比对 ( 核心优化逻辑 ) , 决定复用 / 新建 Element , 对比新旧图纸差异 ( Diff ) , 判断要不要改实物 ;
  • 管理组件状态 ( State 就挂在它身上 ) , 持有 State , StatefulWidget 状态 , 全部存在 Element 中 ;
  • 挂载到 RenderObject , 驱动渲染 , 对接下层真正负责显示的 RenderObject ;
  • 管理生命周期 : create → update → deactivate → dispose ;

与 GlobalKey 的关系

Element 与 GlobalKey 的关系 :

  • GlobalKey 会把 Element 注册到全局 Map 中 ;
  • 通过 GlobalKey 直接拿到 Element , 进而拿到 State/Context , navigatorKey.currentContext 能全局获取上下文 ;
dart 复制代码
// 定义全局导航键 , 用于在无上下文环境下全局跳转页面
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

// 应用根组件 , 无状态组件
class MyApp extends StatelessWidget {
  // 构造函数 , 传递父组件的 key
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    // 构建应用根页面 , MaterialApp 是 Material 风格应用入口
    return MaterialApp(
      // 绑定全局导航键 , 实现全局路由跳转
      navigatorKey: navigatorKey,
    );
  }
}

State 可变数据

State 是 可变数据 , 是 StatefulWidget 的 可变状态容器 , 只被 StatefulElement 持有 , StatelessElement 不持有 State 状态数据 ;

State 仅存在于 StatefulWidget 体系 , StatelessWidget 无 State ;

State 关键特性 :

  • 可变 : 内部变量可以修改 , 是页面交互、数据变化的载体 ;
  • 依附 Element 存活 : State 的生命周期 = 绑定的 Element 生命周期 ;
  • setState() : 标记当前 Element 为脏节点 , 触发局部重建 ;

比喻 : State = 房间里的家具 / 电器状态 ( 灯亮不亮、计数器数字 ) , 会随使用变化 ;

3、 RenderObject 树

RenderObject 树 负责 屏幕上的「像素渲染」, 是 真正负责 把内容画到屏幕上 的 实体 ;

  • Widget 是图纸 , Element 是 工长 , RenderObject 是 真实建筑 , 也就是 最终显示在屏幕的东西 ;
  • 唯一真正 参与绘制、布局、点击交互的一层 ;
  • 屏幕上看到的文字、按钮、颜色、大小、点击响应 , 全靠它实现 ;
  • 负责三件事 : 布局 ( 算大小位置 ) 、绘制 ( 上色画图 ) 、事件响应 ( 点击 / 滑动 ) ;
  • 尽量复用 : RenderObject 性能开销最大 , Flutter 会尽可能复用它 , 避免重建 , 界面小改动时 , 不会推倒重建 , 只进行局部修改 ;

Element ( 工长 ) 根据图纸 , 最终 指挥 搭建出真正能看见、能触摸、有尺寸位置的界面 , 所有渲染节点组合起来 , 就是 RenderObject 树 ;

二、三者之间的关系


1、三棵树对应关系

三棵树固定绑定规则 :

  • 一个 Widget 对应一个 Element : Element 由 Widget 生成 , 初始一一对应 ;
  • 一个 StatefulElement 唯一持有一个 State ;

2、三棵树完整运转流程

页面首次渲染运转流程 : 代码写图纸 → 工长来对接 → 渲染真实界面 ;

  • 创建 Widget : 编写代码 → 生成 Widget 树 ( 图纸 ) , 开发者编写的 Widget 树 被构建 ;
  • 创建 Element : Flutter 解析图纸 / 遍历 Widget → 为每个 Widget 创建 Element ( 工长 ) , 形成 Element 树 , Framework 调用 widget.createElement() → 生成 Element ;
  • 创建 RenderObject : 渲染 " 界面 " , 每个 Element 按照图纸要求 → 创建对应的 RenderObject ( 实体界面 ) , 形成 RenderObject 树 , RenderObject 完成 测量、布局、绘制 → 最终显示在手机 / 电脑屏幕上
dart 复制代码
Widget ( 新 )  → 创建 Element → 创建 RenderObject → 渲染显示

页面刷新流程 :

  • 触发刷新 : 点击按钮 → 调用 setState() 方法 , 触发 Widget 树重新构建 ;
  • 新建 Widget : Flutter 重新生成新的 Widget 树 ( 新版图纸 ) ;
  • 对比 Element : Flutter 用新 Widget 对比 旧 Element 持有的旧 Widget , 相当于 原有 Element 树 ( 工长们 ) 不换人 , 拿着新旧两张图纸做对比 :
    • 结构、类型、Key 没有改变 , 只是文字 / 颜色改了 → Element 继续复用 / 继续用原工长、原状态 , 用新 Widget 配置更新自己 ;
    • 组件结构增删、位置调换 / Key 发生改变 → 调整对应工长分工 ;
  • 复用 RenderObject : 工长指挥 RenderObject 局部更新 , 只改变化的地方 , 不全量重建 ;
  • 屏幕刷新 : 看到最新效果 ;
dart 复制代码
Widget ( 新 )  → 复用 Element ( 更新配置 )  → 复用 RenderObject ( 刷新绘制 ) 

3、页面重建刷新逻辑特点

页面重建刷新逻辑特点 :

  • Widget 最爱换新 : 频繁重建 , 每次都创建新的 Widget 树 ;
  • Element 尽量复用 : Element 和 State 优先复用 , 不会随意销毁结构没变就复用 , 结构变了创建新的 Element 实例 ;
  • RenderObject 必须复用 : 只进行局部修改 , 最小成本刷新 , 全局唯一 , 只进行局部更新 , 不重建 ;

4、页面 首次渲染 与 后续更新 流程

页面首次渲染流程 :

  • 编写 Widget ( 代码定义 )
  • Framework 调用 widget.createElement() → 生成 Element
  • StatefulElement 内部调用 widget.createState() → 生成 State
  • Element 执行 build() → 生成下一级子 Widget
  • 递归直到叶子节点 , 创建 RenderObject 树 → 最终渲染到屏幕

页面更新流程 : 状态更新链路 ( setState 流程 )

  • 调用 state.setState(() { 修改变量 })
  • 标记所属 StatefulElement 为 " 脏节点 " ;
  • 下一帧到来 , Framework 只重建脏节点及其子树 ;
  • Element 重新 build → 生成新的子 Widget
  • 新旧 Widget 做 Diff , 决定复用/新建 Element
  • 页面刷新 , 展示最新状态

三、StatelessWidget 体系中的三棵树


1、StatelessWidget 基础概念

StatelessWidget 中的 Stateless = State ( 状态 ) + less ( 无 ) → 无状态组件

  • StatelessWidget 是静态组件 , 一旦创建 , 界面就不会自动改变 ;
  • 没有 setState() 函数 , 无法主动刷新自己 ;
  • 数据只能通过构造函数传参传入 , 内部不能修改数据 ;

StatelessWidget 适用场景 :

  • 纯展示 UI , 标题、文本、图标、按钮、静态布局 ;
  • 不需要动态修改数据的组件 ;
  • 作为页面的静态结构骨架 ;

2、StatelessWidget 运行流程

StatelessWidget 运行流程 :

  • 创建 StatelessWidget 实例
  • 执行 build(BuildContext context) 函数 ;
  • 返回 Widget 树 ( 界面配置 )
  • Flutter 创建 Element + RenderObject
  • 渲染到屏幕 ( 静态显示 , 无法自动更新 )

3、StatelessWidget 运行流程

StatelessWidget 对应三棵树 : Widget ( 配置 ) + Element ( 实例 ) , 无 State 存储 ;

StatelessWidget 体系 的 结构 是 StatelessWidget → StatelessElement ;

没有 State , 页面内容完全由外部入参决定 ;

父组件重建、自身入参变化 → Element 重新执行 build() ;

BuildContext 的 本质就是 当前 Element 的对外接口 , 用来向上查找父组件、主题、路由等 ;

4、StatelessWidget 基础代码结构分析

基础代码结构 : StatelessWidget 是抽象类 , 强制要求子类重写 build 方法 , 否则直接编译报错 ;

dart 复制代码
// 1. 继承 StatelessWidget
class MyTextWidget extends StatelessWidget {
  // 2. 外部传入的参数 ( 必须 final , 不可变 ) 
  final String text;
  final Color color;

  // 3. 构造函数 ( 接收参数 ) 
  const MyTextWidget({super.key, required this.text, required this.color});

  // 4. 必须重写的 build 方法
  @override
  Widget build(BuildContext context) {
    // 5. 返回一个 Widget ( 渲染到屏幕的内容 ) 
    return Text(
      text,
      style: TextStyle(color: color, fontSize: 20),
    );
  }
}

5、StatelessWidget 运行流程

@override Widget build(BuildContext context) 是 StatelessWidget 组件的灵魂 , build 函数是 Flutter 用来获取组件界面配置的方法 , 它的唯一使命是 描述当前组件要渲染的 Widget 树结构 , 该函数有四个核心作用 :

  • 提供界面蓝图 : 告诉 Flutter 「这个组件要显示什么」 ;
  • 接收上下文 context : 获取当前组件的位置、主题、路由、父组件信息 ;
  • 构建子 Widget 树 : 把你写的 UI 代码 ( Text/Row/Column ) 组合成树 ;
  • 返回渲染配置 : 给 Flutter 提供一份 Widget 配置 , 用于生成 Element/RenderObject ;

build 函数的执行时机 :

  • 组件首次创建 ( 页面加载 ) ;
  • 父组件重建 ( 父组件刷新 , 子组件会跟着执行 build ) ;
  • 依赖的 InheritedWidget 变化 ( 如主题、语言切换 ) ;

build 函数注意事项 :

  • build 函数 必须是纯函数 ( 无副作用 ) ;
  • 不能在里面做网络请求、定时器、耗时操作 ;
  • 不能在里面调用 setState ( Stateless 也没有 ) ;
  • 只做界面描述 , 不做业务逻辑 ;

6、BuildContext 参数

参数 BuildContext context 是 当前组件在 Element 树中的位置引用 , 其本质就是当前组件的 Element , 每个 Widget 的 build 方法都有自己独立的 context , 互不干扰 , 其有如下作用 :

  • 获取主题 Theme.of(context) ;
  • 获取导航 Navigator.of(context) ;
  • 获取媒体查询 MediaQuery.of(context) ;
  • 向上查找父组件 / 状态 ;
  • 标记当前组件在树中的位置 ;

7、StatelessWidget 生命周期与三棵树对应关系

阶段 StatelessWidget 生命周期 三棵树对应动作 核心本质
1. 实例化 构造函数 Constructor 创建 Widget 配置 生成一份界面蓝图
2. 挂载 无 ( Framework 执行 ) 创建 Element 实例 生成代理人 , 持有 Widget
3. 渲染 build () 方法 创建 RenderObject 生成渲染实体 , 显示到屏幕
4. 更新 再次执行 build () 更新 Element / RenderObject 复用实例 , 仅刷新配置
5. 销毁 无 ( Framework 执行 ) 卸载 Element 从树中移除 , 回收内存

阶段 1 : 组件被创建 ------ 构造函数执行

执行 Constructor 构造函数 , 创建 Widget 实例 ,

dart 复制代码
// 1. 调用构造函数 , 创建 Widget 实例
MyWidget(title: "Hello");

对应的三棵树状态 :

  • Widget 树 : 新建一个 MyWidget 配置对象 ;
  • Element 树 : 尚未创建 ;
  • RenderObject 树 : 尚未创建 ;

阶段 2 : 组件被挂载 ------ Framework 暗箱操作

对开发者透明 , 开发者感知不到 , 没有应用层代码执行 , Flutter 框架自动做了 2 件大事 :

  • Element 树 : 调用 MyWidget.createElement() , 创建对应 Element , 这个 Element 会 持有刚才创建的 Widget 引用 ;
  • RenderObject 树 : 尚未创建 ;

阶段 3 : 首次绘制界面 ------ build () 执行

执行 build() 函数 , 给 Element 提供最新的 Widget 配置 , 让 Element 去生成渲染实体 , 创建的 Element , 就是 build 函数的 BuildContext 参数 ;

dart 复制代码
@override
Widget build(BuildContext context) {
  // 3. 描述 UI
  return Text(title); 
}

对应的三棵树状态 :

  • Widget 树 : build 方法 返回新的子 Widget ( Text ) ;
  • Element 树 : Element 调用 mount 方法 , context 就是这个 Element ;
  • RenderObject 树 : Element 触发 createRenderObject , 创建真正的渲染对象 , 测量、布局、绘制 , 显示到屏幕 ;

阶段 4 : 界面更新 ------ build () 再次执行

父组件 调用 setState 函数后 , 重新创建了 MyWidget ;

dart 复制代码
// 父组件 setState 后 , 重新创建了 MyWidget
MyWidget(title: "Hello Updated"); 

对应的三棵树状态 : 页面刷新过程中 , Widget 重建了 N 次 , Element 和 RenderObject 始终是同一个 ( 复用 ) , 这就是 Flutter 流畅的原因 ;

  • Widget 树 : 全新创建一个 MyWidget ( 配置变了 ) ;
  • Element 树 : 复用旧 Element ; 框架对比 Key 和类型 , 发现类型没变 , 不新建 Element ; 如果发生了变化 , 则调用 update 方法 , 用新 Widget 替换掉 Element 持有的旧 Widget ;
  • RenderObject 树 : 复用旧 RenderObject , Element 通知 RenderObject 更新属性 ( 文字、颜色等 ) , 不重建 ;

阶段 5 : 组件销毁 ------ 移除树

用户感知不到 , 没有表层代码运行 ;

对应的三棵树状态 :

  • Element 树 : Element 从树中移除 ( Unmount ) ;
  • RenderObject 树 : 随之销毁 ;
  • Widget 树 : 配置对象被垃圾回收 ;

四、StatefulWidget 体系中的三棵树


1、StatefulWidget 概念

StatefulWidget = 有状态的组件 ( 可以动态刷新 UI ) , 有可变状态 State , 可以 通过 setState() 主动刷新界面 , 适用于 表单、地图、动画、计时器、视频、交互页面 场景 ;

由 Widget ( 壳 ) + State ( 核心 ) 组成 , Widget 是临时配置 不可变 , State 是永久状态 可变 , 状态存在 State 里 , 不是 Widget 里 , setState () 会触发 build () 重绘 ,

Widget 是临时配置 ( 随时销毁重建 ) , State 是永久状态 ( 不销毁 ) ;

真正管理数据、逻辑、生命周期、刷新的是 State , 不是 Widget

2、StatefulWidget 代码组成

StatefulWidget 由两部分组成 :

  • 外部 Widget : 壳 , 不可变 , 只接收参数、创建 State ;
    • 必须继承 StatefulWidget ;
    • createState() → 生命周期最重要方法 , 执行 1 次 , 创建并返回 State 实例 , State 会被 Element 持有 , 永久存活 ;
dart 复制代码
class 组件名 extends StatefulWidget {
  const 组件名({super.key});
  
  @override
  State<组件名> createState() => 组件State();
}
  • 内部 State : 是 核心 , 存储 State 状态 + 逻辑 , 是真正的 组件本体 , 存变量、写逻辑、生命周期、build 函数 ;
dart 复制代码
class 组件State extends State<组件名> {
  // 状态变量
  int count = 0;

  @override
  void initState() {} // 初始化

  @override
  Widget build(BuildContext context) {} // 构建UI

  @override
  void dispose() {} // 销毁
}

基础代码结构示例 :

dart 复制代码
// 1. Widget 本身不可变
class Counter extends StatefulWidget {
  const Counter({super.key});

  // 2. 由 Element 调用 , 创建 State ( 只执行一次 , Element 不复用才会重走 ) 
  @override
  State<Counter> createState() => _CounterState();
}

// 3. 可变状态类
class _CounterState extends State<Counter> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => setState(() => count++),
      child: Text("计数: $count"),
    );
  }
}

3、StatefulWidget 生命周期与三棵树对应关系

StatefulWidget 三棵树 : StatefulWidget → 生成 StatefulElement → Element 同时持有 Widget + State ;

  • Widget : 不可变配置蓝图 , 每次重建都会生成新实例 , 轻量 ;
  • Element : 组件运行实例 , 三棵树中枢 , 永久持有 State 对象 , 组件不卸载就不会销毁 ;
  • RenderObject : 渲染实体 , 负责测量、布局、绘制 , 开销最大 , 框架优先复用 ;

执行顺序 : StatefulWidget 构造函数 → createState() → Element 创建挂载 → initState() → didChangeDependencies() → build() → RenderObject 创建 ;

State 完整生命周期 ( 按执行顺序 ) :

  • StatefulWidget 构造函数 : 创建 StatefulWidget 实例 , 仅传递配置 , 无状态存储 , 属性均为 final ;
    • Widget 树 : 新增一个 SurfaceMapView 配置节点 ;
    • Element 树 / RenderObject 树 : 尚未创建 ;
  • ① createState() : 创建 State 状态数据 , 令 Element 持有 State ; @override State<SurfaceMapView> createState() => SurfaceMapViewState(); , Widget 树 : 无变化 ; Element 树 : 框架根据 Widget 类型 , 创建 StatefulElement ; 该 Element 内部强引用绑定 : 当前 Widget + 刚创建的 State ; RenderObject 树 : 仍未创建 ;
    • 所属 : StatefulWidget 方法 ;
    • 时机 : Element 首次创建时调用 , 全局仅执行 1 次 ;
    • 特点 : Element 不复用才会再次执行 , Element 复用则永远不执行 ;
    • State 状态 : State 从诞生起就被 Element 持有 , 后续无论 Widget 重建多少次 , State 始终不变 , 这是状态不丢失的根源 ;
  • Element 执行 mount 挂载 : 在 createState() 与 initState() 之间执行 , 无暴露方法 , 框架内部执行逻辑 , 开发者无感知 ;
    • Element 正式接入整棵 Element 树 , 确立父子层级、上下文关系 ;
    • 代码里 build(BuildContext context) 中的 context , 就是这个 Element 本身 ;
  • ② initState() : 初始化 State 状态数据 , Element 挂载 ; 三棵树结构已就位 , 但 RenderObject 还未生成 ; 当前仅在 Element + State 层做业务初始化 , 不触发渲染 ;
    • 时机 : Element 挂载到树中 , 只执行一次
    • 用途 : 初始化数据、订阅事件、网络请求、动画初始化
    • 限制 : 内部不能调用 setState ( 节点还未就绪 )
  • ③ didChangeDependencies() : 该阶段 Element 检测上层依赖变化 , 更新自身上下文环境 , 不重建任何树节点 ;
    • 时机 : initState 之后、首次 build 之前执行 ; 当组件依赖的 InheritedWidget ( 主题、语言、路由、全局配置 ) 变化时 , 也会重复触发 ;
    • 场景 : 主题切换、语言切换
  • ④ build() : 第一次界面构建 , 渲染 CustomPaint + 手势 + 布局 , Widget 生成 → Element 更新 → RenderObject 渲染 ;
    • 时机 : 初始化 + setState + 依赖变化 + 父组件重建 都会触发 , 每次调用 setState 都会执行 ;
    • 核心 : 构建 UI 结构 ; 不要在其中执行耗时操作 ;
    • 三棵树状态 : 初始化完成后 , 三棵树全部成型 , 结构固定 , 后续优先复用 ;
      • Widget 树 : build 返回一整棵子 Widget 树 ( 布局、手势、绘制组件 ) ;
      • Element 树 : 遍历返回的子 Widget , 批量创建对应子 Element , 完善整棵 Element 树 ;
      • RenderObject 树 : Element 调用 createRenderObject , 首次创建渲染对象 , 执行测量、布局、绘制 , 地图画布正式显示在屏幕 ;
  • ⑤ didUpdateWidget(Widget oldWidget) :
    • 时机 : 新旧 Widget 替换 , 但 Element 被复用时 ( 同级 Widget 更新、未重建 Element )
    • 场景 : 父组件重建 , 传入新参数 , Element 保留 , 更新配置
  • ⑥ deactivate() : 组件即将从当前树移除时触发 ; 如果只是临时移动树位置 ( 路由跳转复用组件 ) , 后续会重新挂载 ;
    • 时机 : Element 从树中临时移除 ( 页面跳转、组件移出可视区 )
    • 临时过渡阶段 , 不一定销毁 ;
    • 三棵树状态 : Element 临时脱离原有树结构 , RenderObject 暂时保留 ;
  • ⑦ dispose() : 释放绘制资源 ;
    • 时机 : Element 永久从树中移除 ( 页面销毁、组件彻底卸载 )
    • 用途 : 取消定时器、取消网络监听、释放资源、关闭流 ;
    • 特点 : 执行后 State 彻底失效 , 不能再调用 setState ;

运行时更新 生命周期 :

  • 调用 setState() 函数 触发 运行时更新 : 内部调用 setState () 函数 , 执行流程 : 调用 setState() 函数 → Element 标记为脏节点 → build () → 更新渲染 ; 对应的 三棵树 状态 :
    • Widget 树 : 原有 Widget 实例不变 ;
    • Element 树 : 当前 Element 被标记为 Dirty 脏节点 , 框架在下一帧会统一处理脏节点 , 不会立即刷新 ;
    • RenderObject 树 : 暂时无动作 ;
  • 再次执行 build () 函数 : 重新构建子 Widget 树 ; 三棵树状态 :
    • Widget 树 : 重新生成全新的子 Widget 实例 ( 轻量 , 无性能压力 ) ;
    • Element 树 : 完全复用原有 Element ; 框架对比新旧 Widget ( 类型 + Key ) , 匹配成功 , 调用 update 方法 , 让 Element 持有新的 Widget 配置 ; Element、State 全程不销毁、不重建 ;
    • RenderObject 树 : 复用原有 RenderObject ; 仅根据新 Widget / 新状态更新绘制属性 ( 画布大小、图形位置、缩放比例 ) , 不重建渲染对象 ;
dart 复制代码
createState() → initState() → didChangeDependencies() → build()
                ↓
        【运行中】
  setState() / 依赖变化 → build()
  父组件更新、Widget 替换 → didUpdateWidget() → build()
                ↓
        组件移除
        deactivate() → dispose()

4、初始化阶段 与 更新阶段 生命周期 汇总对照表

初始化阶段 生命周期 : 只走一次 ;

  • StatefulWidget() 构造函数 → 创建 Widget 配置
  • createState() → 创建 State 实例
  • initState() → 初始化数据、监听、控制器
  • didChangeDependencies() → 处理依赖 ( 主题 / 路由 )
  • build() → 第一次构建界面

更新阶段 生命周期 : 执行很多次 ;

  • setState() → 触发刷新
  • didUpdateWidget() → 父组件刷新传递新 Widget
  • build() → 重新构建界面

销毁阶段 生命周期 : 只执行一次 ;

  • dispose() → 释放资源 ( 控制器、监听 )
  • State 销毁
生命周期方法 执行时机 Widget 树 Element 树 RenderObject 树
Widget 构造函数 组件首次创建 新建节点
createState() 构造后立即执行 不变 创建 StatefulElement , 绑定 State
initState() Element 挂载后 , 首次 build 前 不变 Element 已挂载 , 状态初始化
didChangeDependencies() 依赖变更 /initState 后 不变 更新依赖上下文
首次 build () 初始化阶段最后一步 生成子 Widget 树 创建子 Element 树 首次创建 RenderObject
setState() 主动更新状态 不变 Element 标记为脏节点 不变
重复 build () 刷新 UI 重建子 Widget 复用原有 Element 复用 RenderObject , 仅更新属性
didUpdateWidget() 父组件传入新 Widget 替换旧 Widget 复用 Element+State 不变
deactivate() 组件即将移出树 不变 Element 临时脱离树 不变
dispose() 组件永久销毁 节点回收 Element 彻底销毁 RenderObject 销毁回收
相关推荐
恋猫de小郭4 小时前
Android 17 正式版发布,全新 AI 和各种破坏性更新
android·前端·flutter
kingbal5 小时前
Windows:flutter环境搭建
windows·flutter
911hzh6 小时前
Flutter MethodChannel 跨端通信框架 zh_native_channel:快速入门、优势分析与 Pigeon 对比
flutter
911hzh6 小时前
Flutter 快速搭建新项目:用 Flutter Foundation Kit 一条命令生成带基础架构的 App 模板
flutter
kingbal7 小时前
Flutter:Flutter SDK版本管理工具FVM
android·flutter·ios·android-studio·window
风华圆舞7 小时前
鸿蒙 Flutter 页面怎么感知防窥状态并调整 UI 可见性
flutter·ui·harmonyos
天天开发7 小时前
Flutter状态管理新宠:RiverPod全面解析与实战指南
android·flutter
「、皓子~17 小时前
海狸IM 2.0 正式发布:六端齐发,开源 IM 迈入新阶段
flutter·electron·开源软件·ai编程·交友·im