问题:为什么每次刷新都会重新构建 StarWidget,即执行了initState方法
没有包裹Positioned的话,每次刷新不会重新构建 StarWidget
如以下代码:
dart
// 1. Stack 存在很多相同类型的Positioned
Stack(
children: [
...
if(isVip)
Positioned(top:10, left:0, child:Container()),
// 包裹了Positioned, 👆🏻上面还有很多的Positioned
Positioned(
top: 19.px,
left: 0,
right: 0,
child: const Center(
child: StarWidget(maxStar: 6, currentStar: 2),
),
),
],
)
// 2. Stack 存在不同的类型的widget
Stack(
children: [
...
if(isVip)
Positioned(top:10, left:0, child:Container()),
// 没有包裹Positioned, 与👆🏻上面很多的Positioned有了明显的区别
StarWidget(maxStar: 6, currentStar: 2),
],
)
问题根源不在 StarWidget
自身,而是在 Positioned
(或其父)引发了 Widget diffing 失败,导致 subtree 被认为是新节点。
每次 rebuild,Flutter 会尝试 diff Stack.children
中的 Widget。如果没有显式指定 Key
,且子节点顺序或类型有变化,Flutter 会认为这个 Positioned
是新节点,从而导致其 child
被销毁重建。
即使 child
是 const,也没用!一旦 Positioned
被认为是"不同的",它下面的 subtree 都会重新走生命周期。
✅ 为什么不包 Positioned
就不会重建?
当你直接写 Center(child: StarWidget(...))
放在一个不重建的 Widget 树中,Flutter 可以成功 diff 出它是"同一个 Widget",于是就复用了它。
但只要你包了 Positioned
,就进入了一个 diff 更复杂、容易误判的场景 ------ 特别是 Stack
的 children
是 List<Widget>
,没有 Key
会直接匹配失败。
✅ 正确做法(加 Key)
给 Positioned
添加一个明确的 Key
,就能保持一致性,避免 subtree 被误判为新节点:
dart
Stack(
children: [
Positioned(
key: const ValueKey('jackpot'),
top: 19.px,
left: 0,
right: 0,
child: const Center(
child: StarWidget(maxStar: 6, currentStar: 2),
),
),
],
)
或者,如果这个 Positioned
的位置、顺序或状态会变化,建议加 Key
区分。
✅ 更高级的补充
Stack
→children
是一个List<Widget>
,Flutter 的 diff 是按位置+类型做的- 如果你每次
build()
里都返回了新的List
,Flutter 可能每次都认为是不同的 Widget → 导致dispose/initState
✅ 总结
状态 | 是否触发 initState |
---|---|
直接使用 StarWidget |
✅ 不触发 |
包在 Positioned 里(无 Key) |
❌ 会触发(被重建) |
包在 Positioned 里(加 Key) |
✅ 不触发(保持状态) |