掌握 Flutter Widget 重建:2. 当你写 Text(msg) 并改变 msg 的值时,Flutter 内部到底发生了什么?

7 阅读1分钟

💡 示例代码:

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,仅仅调用 Textupdate()

✅ 第 4 步:Text 的 updateRenderObject() 被调用

  • Text Widget 是 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 并没有销毁和重新创建整个 Text Widget 的 Element 或 RenderObject

  • 它只是:

    • diff 出 Text 的内容变化,
    • 调用 updateRenderObject 更新文字内容,
    • 触发部分重绘(layout 和 paint)。

🧠 再延伸一句话:

Text(msg) 只要不是 const,每次 build 都是新的 Widget 实例;但只要类型和 key 没变,Element 和 RenderObject 都可以复用,实现高效更新。