OverlayEntries 和 Routes 的重建最佳化
OverlayEntries 現在僅在明確的狀態變更時才會重建。
摘要
#
這項最佳化提升了路由(Route)轉場的效能,
但可能會暴露出你應用程式中遺漏呼叫 setState 的情況。
背景說明
#
在此變更之前,當有新的不透明條目(opaque entry)被加入到 OverlayEntry 之上,
或是從其上方移除時,OverlayEntry 會進行重建。
這些重建其實是多餘的,因為它們並不是由受影響的 OverlayEntry 狀態變更所觸發。
這個破壞性變更(breaking change)最佳化了我們處理 OverlayEntry 新增與移除的方式,
並移除了不必要的重建,以提升效能。
由於 Navigator 內部會將每個 Route 放入 OverlayEntry,
因此這項變更同樣適用於 Route 轉場:
如果有不透明的 Route 被推到另一個 Route 之上,
或是從其上方移除,則在該不透明 Route 下方的 Route
將不再進行不必要的重建。
變更說明
#
在大多數情況下,這項變更不需要你修改任何程式碼。
然而,如果你的應用程式錯誤地依賴於這些隱式重建,
你可能會遇到問題,此時可以透過將任何狀態變更包裹在 setState 呼叫中來解決。
此外,這項變更也略微調整了元件 (Widget) 樹的結構:
在此變更之前,OverlayEntry 會被包裹在 Stack 元件中。
現在明確的 Stack 元件已從元件階層中移除。
遷移指南
#
如果你在升級至包含此變更的 Flutter 版本後遇到問題,
請檢查你的程式碼是否遺漏了 setState 的呼叫。
在下方的範例中,將 Navigator.pushNamed 的回傳值指派給 buttonLabel
其實是隱式地修改了狀態,因此應該將其包裹在明確的 setState 呼叫中。
遷移前的程式碼:
class FooState extends State<Foo> {
String buttonLabel = 'Click Me';
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
// Illegal state modification that should be wrapped in setState.
buttonLabel = await Navigator.pushNamed(context, '/bar');
},
child: Text(buttonLabel),
);
}
}
遷移後的程式碼:
class FooState extends State<Foo> {
String buttonLabel = 'Click Me';
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
final newLabel = await Navigator.pushNamed(context, '/bar');
setState(() {
buttonLabel = newLabel;
});
},
child: Text(buttonLabel),
);
}
}
時程
#
合併於版本:1.16.3
在穩定版釋出:1.17
參考資料
#API 文件:
相關議題:
相關 PR:
Unless stated otherwise, the documentation on this site reflects Flutter 3.44.0. Page last updated on 2026-06-14. View source or report an issue.