Widget()(非 const) key 为什么是相同的?

16 阅读2分钟

这是个非常关键但容易被忽略的问题!你问的这个问题可以拆解为:

为什么在 Widget()(非 const)的情况下,Flutter 仍然能判断“新旧 Widget 是相同的”?它是如何比较 key 的?

我们从源码和机制两个层面来深度解析:

Flutter 判断“相同 Widget”的标准

在 Flutter 的 Element.update() 流程中,判断是否是“同一个 Widget”有两个核心条件

  1. runtimeType 相同(类型一致)
  2. key 相同(可选,如果不写 key,会默认相等)
bool canUpdate(Widget oldWidget, Widget newWidget) {
  return oldWidget.runtimeType == newWidget.runtimeType &&
         oldWidget.key == newWidget.key;
}

🔍 那么问题来了:非 const 的 Widget,每次都 new Widget(),实例是不同的,为何 key 还是“相同”?

🧩 关键在于:你根本没显式指定 key,Flutter 默认 key 是 null!

Widget build(BuildContext context) {
  return MyWidget(); // key == null
}

所以:

MyWidget() == MyWidget(); // 不是同一个实例
MyWidget().key == MyWidget().key; // 都是 null,因此 key == key 成立

因此:

只要你没有显式使用 Key,Flutter 默认认为 key 是 null,于是认为“两个 Widget 的 key 相等”。

再配合类型也一样(同是 MyWidget 类型),Flutter 就认为你创建的是“同一个 Widget”,可以复用之前的 Element

🛠️ 示例对比

✅ 示例 1:不使用 key(默认 key 为 null)

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

...

@override
Widget build(BuildContext context) {
  return Column(
    children: [
      MyWidget(), // 每次 build 都创建一个新实例,但 key 是 null
    ],
  );
}

Flutter 检查发现 runtimeType 相同、key 为 null → 复用旧 Element,State 保留。

❌ 示例 2:每次创建不同的 key(强制刷新)

MyWidget(key: UniqueKey())

或者

MyWidget(key: ValueKey(DateTime.now()))

每次构建 key 都不同 → Flutter 认为是不同的 Widget → 销毁旧 Element 和 State,重新创建。

🧠 小结:非 const Widget 为什么 key 相等?

情况key是否相等是否重建 Element / State
Widget()null✅ 相等❌ 不重建
const Widget()null(相同内存)❌ 不重建
Widget(key: UniqueKey())每次不同✅ 重建
Widget(key: ValueKey(id))id 相同❌ 不重建

✅ 最佳实践建议

场景建议
不关心状态重建不用加 key,让 Flutter 自行复用
想强制 Widget 和 State 重建使用 UniqueKey() 或变更的 ValueKey()
列表中使用相同类型的 Widget强烈建议加 Key(避免重排时状态错位)
想利用 const 提升性能参数必须为编译期常量,Widget 结构越深效果越明显