💡 示例代码:
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
String msg = "Hello";
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(msg), // <=== 关注点
ElevatedButton(
onPressed: () {
setState(() {
msg = "World";
});
},
child: Text("Change"),
)
],
);
}
}
🔄 当点击按钮 setState() 时,发生了什么?
我们逐步分析:
✅ 第 1 步:调用 setState()
- 通知框架:“State 改变了!”
- 框架会把当前 Widget 标记为 dirty,等待下一帧重建。
✅ 第 2 步:执行 build()
- Flutter 会重新调用
build()方法,返回新的 Widget 树。 Text(msg)→ 变成了Text("World"),但注意:它依然不是const,所以是一个新的 Widget 实例。
✅ 第 3 步:Flutter diff 新旧 Widget 树
- 找到旧的
Text("Hello")Widget → 现在变成新的Text("World") - 它们类型相同(Text),且 key 也都为 null,满足:
Widget.canUpdate(old, new) == true
✅ 所以: Flutter 会复用原来的 Element 和 RenderObject,仅仅调用 Text 的 update()。
✅ 第 4 步:Text 的 updateRenderObject() 被调用
TextWidget 是LeafRenderObjectWidget,它内部绑定的是一个RenderParagraph。- 当 Widget 内容变了(
data != oldWidget.data),会执行:
@override
void updateRenderObject(BuildContext context, RenderParagraph renderObject) {
renderObject.text = buildTextSpan(); // 更新 TextSpan
}
✅ 第 5 步:RenderObject 被标记 dirty → 触发重绘
- 由于
text内容变化,RenderParagraph会重新 layout 和 paint; - 最终新的文字
"World"被渲染到屏幕上。
🎯 总结整个流程:
| 阶段 | 是否变化 | 是否复用 |
|---|---|---|
| Widget 实例 | ✅ 新建 | ❌ 不复用 |
| Element 实例 | ❌ 不变 | ✅ 复用 |
| State(如果有) | ❌ 不变 | ✅ 复用 |
| RenderObject | ❌ 不变 | ✅ 复用(但会更新内容) |
✅ 所以你看到的效果是:
-
文字从
"Hello"→"World"; -
Flutter 并没有销毁和重新创建整个
TextWidget 的 Element 或 RenderObject; -
它只是:
- diff 出
Text的内容变化, - 调用
updateRenderObject更新文字内容, - 触发部分重绘(layout 和 paint)。
- diff 出
🧠 再延伸一句话:
Text(msg)只要不是const,每次 build 都是新的 Widget 实例;但只要类型和 key 没变,Element 和 RenderObject 都可以复用,实现高效更新。