關于 Flutter 中 Mounted 屬性您需要了解的一切
今天,我們來回顧一下 Flutter 中對于保證 widget 交互穩定性起著重要作用的一個屬性。該屬性的名稱是 mounted。
什么是已 mounted 屬性?
Mounted 屬性是 Flutter 框架在 State 類中提供的布爾標志。它指示狀態對象當前是否在小部件樹中。
創建 State 對象之后、調用 initState 之前,框架通過將 State 對象與 BuildContext 關聯來“mounted”該對象。State 對象保持掛載狀態,直到框架調用 dispose,之后框架將不再要求 State 對象再次構建。換句話說,當一個 widget 從 widget 樹中刪除時,其關聯的狀態對象也被刪除,并且 mounted 屬性被設置為 false。
mounted 屬性的目的是什么?
Mounted 屬性的主要目的是幫助您管理狀態并避免與不再屬于小部件樹一部分的小部件交互時可能發生的潛在錯誤。具體來說,mounted 在涉及異步操作或延遲操作的場景中很有用。
何時使用 Mounted 屬性?
我認為應該把第一名讓給異步操作。
當執行異步操作(例如網絡請求、數據庫查詢)時,一旦操作完成,狀態對象可能會被更新。但是,如果在操作完成之前將小部件從小部件樹中刪除,則嘗試更新狀態可能會導致運行時錯誤。在這種情況下,mounted 屬性允許您在進行任何更新之前檢查狀態對象是否仍然處于活動狀態。
String _data = '';
bool _isFetching = false;
@override
void initState() {
super.initState();
_fetchData();
}
void _fetchData() async {
// assuming that _isFetching is a boolean that tracks the loading state
setState(() {
_isFetching = true;
});
try {
final data = await fetchDataFromServer();
if (mounted) {
setState(() {
_data = data;
_isFetching = false;
});
}
} catch (error) {
if (mounted) {
setState(() {
_isFetching = false;
_error = error;
});
}
}
}
異步操作完成后,我們在調用 setState 之前檢查 Mounted 屬性。這確保了小部件仍然是小部件樹的一部分。
為什么這很重要?同樣,如果小部件已從樹中刪除(例如,由于導航離開頁面或小部件被處置),調用 setState 將導致運行時錯誤。在這里,我們的輔助 Mounted 屬性通過確認小部件仍然處于活動狀態并且可以安全更新來防止這種情況發生。
讓我們進入下一個環節:計時器和延遲操作、動畫。
當使用計時器、延遲操作或動畫時,小部件可能會在計時器或操作完成之前被丟棄。如果在調用 setState 之前檢查 mounted 屬性,就能確保只有在部件仍處于活動狀態時才進行更新。
void _startTimer() {
Timer.periodic(Duration(seconds: 1), (timer) {
if (mounted) {
setState(() {
_counter++;
});
}
});
}
在 dispose 方法中,清理任何資源或取消正在進行的操作至關重要。由于當小部件即將從小部件樹中刪除時會調用 dispose,因此最好在執行可能依賴于小部件狀態的操作之前檢查 mounted 屬性是一個很好的做法。
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
另一個是:Navigator。
如果要執行可能被導航中斷的異步操作(如使用 Navigator.push 或 Navigator.pop),請確保在更新狀態前掛載部件。
Future<void> _navigateAndFetchData() async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AnotherPage()),
);
final result = await someAsyncOperation();
if (mounted) {
setState(() {
_data = result;
});
}
}
我想討論的最后一個是:具有異步初始化的有狀態小部件。
對于在 initState 中執行異步操作的有狀態小部件,最好使用 Mounted 屬性來確保僅在小部件處于活動狀態時才發生更新。
@override
void initState() {
super.initState();
_initializeAsync();
}
Future<void> _initializeAsync() async {
final data = await fetchData();
if (mounted) {
setState(() {
_data = data;
});
}
}
好吧好吧,這是最后一篇:長時間運行的操作。
對于長時間運行的任務,例如文件 I/O 或計算,您可以在調用 setState 之前使用 Mounted 屬性來確保 UI 更新是安全的。
Future<void> performLongRunningTask() async {
await longRunningTask();
if (mounted) {
setState(() {
_taskCompleted = true;
});
}
}
為什么要使用 Mounted 屬性?
如果總結我們的討論,我們得到的是:
- 防止運行時錯誤:在不檢查 mounted 的情況下,存在嘗試更新已刪除的小部件的風險,從而導致運行時異常。這在操作完成時間不可預測的異步操作中尤其常見。
- 生命周期管理:mounted 屬性提供了一種管理 widget 生命周期的方法,確保只有在 widget 處于活動狀態時才執行操作。
- 效率:通過避免對非活動部件進行不必要的更新,mounted 屬性有助于有效管理資源,從而提高應用程序的性能。
什么時候 mounted 屬性可能不是必需的?
同步操作:用于不涉及異步任務或后臺操作的純同步操作。然而,確保適當的資源管理并避免在小部件處置期間更新狀態仍然很重要。
簡單小部件:適用于不涉及任何異步邏輯、后臺任務或復雜生命周期管理的簡單小部件。
在 Flutter 3.7 中使用 context.mounted
在 Flutter 3.7 之前,無法檢查 Flutter 中是否安裝了 StatelessWidget。這只能在 State/Stateful 小部件上實現。
但從Flutter 3.7開始,BuildContext 本身就有一個 mounted 屬性。
這使得檢查是否安裝了任何小部件變得很容易,并且您可以從任何地方檢查它,無論它來自 StatefulWidget State 還是來自 Stateless widget,就像這樣:
// inside any widget
@override
Widget build(BuildContext context) {
return ElevatedButton(
child: const Text('Submit'),
onPressed: () async {
await doSomeAsyncWork(); // a method that returns a Future
if (context.mounted) {
Navigator.of(context).pop();
}
},
);
}
我在 StackOverflow 上看到很多關于無論如何都要在任何地方使用 mounted 屬性的必要性的問題。請記住,使用 mounted 屬性不是強制性的,但這是一個很好的做法。在特殊情況下使用的良好實踐。
原文:https://medium.com/@wartelski/everything-you-need-to-know-about-the-mounted-property-in-flutter-b603fdb51cb4