这是个非常关键但容易被忽略的问题!你问的这个问题可以拆解为:
为什么在
Widget()
(非 const)的情况下,Flutter 仍然能判断“新旧 Widget 是相同的”?它是如何比较 key 的?
我们从源码和机制两个层面来深度解析:
✅ Flutter 判断“相同 Widget”的标准
在 Flutter 的 Element.update()
流程中,判断是否是“同一个 Widget”有两个核心条件:
runtimeType
相同(类型一致)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 结构越深效果越明显 |