StatefulWidget 需要借助于 State 物件,在特定的階段來處理用戶的互動或其內部資料的變化,并體現在 UI 上,這個特定的階段,就涵蓋來一個組件從加載到卸載的全程序,即生命周期,Flutter 中的 Widget 也存在生命周期,并且通過 State 來體現,
而 App 則是一個特殊的 Widget,除了需要處理視圖顯示的各個階段(即視圖的生命周期)之外,還需要應對應用從啟動到退出所經歷的各個狀態(App 的生命周期),
State 生命周期
State 的生命周期,指的是在用戶參與的情況下,其關聯的 Widget 所經歷的,從創建到顯示再到更新最后到停止,直至銷毀等各個程序階段,State 的生命周期可以分為 3 個階段:創建(插入視圖樹)、更新(在視圖樹中存在)、銷毀(從視圖樹中移除),
創建
State 初始化時會依次執行:構造方法 -> initState -> disChangeDependencies -> build,隨后完成頁面渲染,
初始化程序中每個方法的意義如下:
- 構造方法是 State 生命周期的起點,Flutter 會通過呼叫 StatefulWidget.createState() 來創建一個 State,可以通過構造方法,來接收父 Widget 傳遞的初始化 UI 配置資料,這些配置資料,決定了 Widget 最初的呈現效果,
- initState,會在 State 物件被插入視圖樹的時候呼叫,這個函式在 State 的生命周期中只會被呼叫一次,所以可以在這里做一些初始化作業,比如為狀態變數設定默認值,
- didChangeDependencies 則來專門處理 State 物件依賴關系變化,會在 initState() 呼叫結束后,被 Flutter 呼叫,
- build,作用是構建視圖,經過以上步驟,Framework 認為 State 已經準備好了,于是呼叫 build,可以在這個函式中,根據父 Widget 傳遞過來的初始化配置資料,以及 State 的當前狀態,創建一個 Widget 然后回傳,
更新
Widget 的狀態更新,主要由 3 個方法觸發:setState、didchangeDependencies 與 didUpdateWidget.
如上三個方法對應的呼叫場景:
- setState:當狀態資料發生變化時,通過呼叫這個方法告訴 Flutter:“這兒的資料變了,請使用更新后的資料重建 UI!”,
- didChangeDependencies:State 物件的依賴關系發生變化后,Flutter 會回呼這個方法,隨后觸發組建構建,典型的場景是,系統語言 Locale 或應用主題改變時,系統會通知 State 執行 didChangeDependencies 回呼方法,
- didUpdateWidget:當 Widget 的配置發生變化時,比如,父 Widget 觸發重建(即父 Widget 的狀態發生變化時),熱多載時,系統會呼叫這個函式
一旦這三個方法被呼叫,Flutter 隨后就會銷毀老 Widget,并呼叫 build 方法重建 widget,
銷毀
組件銷毀比較簡單,比如組件被移除,或是頁面銷毀的時候,系統會呼叫 deactivate 和 dispose 這兩個方法,來移除或銷毀組件,
具體呼叫機制如下:
- 當組件的可見狀態發生變化時,deactivate 函式會被呼叫,這時 State 會被暫時從視圖樹中移除,值得注意的是,頁面切換時,由于 State 物件在視圖樹中的位置發生了變化,需要先暫時移除后再重新添加,重新觸發組件構建,因此這個函式也會被呼叫,
- 當 Sate 被永久地從視圖樹中移除時,Flutter 會呼叫 dispose 函式,而一旦到這個階段,組件就要被銷毀了,所以我們可以在這里進行最終的資源釋放、移除監聽、清理環境,等等,
從功能、呼叫時機和呼叫次數的緯度總結這些方法如下;
| 方法名 | 功能 | 呼叫時機 | 呼叫次數 |
|---|---|---|---|
| 構造方法 | 接收父Widget傳遞的初始化UI配置資料 | 創建State時 | 1 |
| initState | 與渲染相關的初始化作業 | 在State被插入視圖樹時 | 1 |
| didChangeDependencies | 處理State隊形依賴關系變化 | initState后及State物件依賴關系變化時 | >= 1 |
| build | 構建視圖 | State準備好資料需要渲染時 | >= 1 |
| setState | 觸發視圖重建 | 需要重繪UI時 | >= 1 |
| didUpdateWidget | 處理Widget的配置變化 | 父Widget setState觸發子Widget重建時 | >= 1 |
| deactivate | 組件被移除 | 組件不可視 | >= 1 |
| dispose | 組件被銷毀 | 組件被永久移除 | 1 |
App 生命周期
視圖的生命周期,定義了視圖的加載到構建的全程序,其回呼機制能夠確保我們可以根據視圖的狀態選擇合適的時機做恰當的事情,App 的生命周期,則定義了 App 從啟動到退出的全程序,
在原生 Android、iOS 開發中,有時需要在對應的 App 生命周期事件中做相應處理,比如 App 從后臺進入前臺、從前臺退到后臺,或是在 UI 繪制完成后做一些處理,
在原生開發中,可以通過重寫 Activity、ViewController 生命周期回呼方法,或是注冊應用程式的相關通知,來監聽 App 的生命周期并做相應的處理,而在 Flutter 中,我們可以利用 WidgetsBindingObserver 類,來實作同樣的需求,
WidgetsBindingObserver 中具體回呼函式有如下一些方法:
abstract class WidgetsBindingObserver {
// 頁面 pop
Future<bool> didPopRoute() => Future<bool>.value(false);
// 頁面 push
Future<bool> didPushRoute(String route) => Future<bool>.value(false);
// 系統視窗相關改變回呼,如旋轉
void didChangeMetrics() { }
// 文本縮放系數變化
void didChangeTextScaleFactor() { }
// 系統亮度變化
void didChangePlatformBrightness() { }
// 本地化語言變化
void didChangeLocales(List<Locale> locale) { }
//App 生命周期變化
void didChangeAppLifecycleState(AppLifecycleState state) { }
// 記憶體警告回呼
void didHaveMemoryPressure() { }
//Accessibility 相關特性回呼
void didChangeAccessibilityFeatures() {}
}
其他回呼相對簡單,可以查看官方檔案
生命周期回呼
didChangeAppLifecycleState 回呼函式中,有一個引數型別為 AppLifecycleState 的列舉類,這個列舉類是 Flutter 對 App 生命周期狀態的封裝,它的常用狀態包括 resumed、inactive、paused 這三個,
- resumed:可見的,并能回應用戶的輸入;
- inactive:處在不活動狀態,無法處理用戶回應;
- paused:不可見并不能回應用戶的輸入,但是在后臺繼續活動中,
案例分享:在 initState 時注冊來監聽器,在 didChangeAppLifecycleState 回呼方法中列印來當前的 App 狀態,最后在 dispose 時把監聽器移除:
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
...
@override
@mustCallSuper
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this); // 注冊監聽器
}
@override
@mustCallSuper
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this); // 移除監聽器
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
print("$state");
if (state == AppLifecycleState.resumed) {
// do sth
}
}
}
嘗試這切換一下前、后臺,觀察控制臺輸出的 App 狀態,可以發現:
- 從后臺切入前臺,控制臺列印的 App 生命周期變化如下:AppLifecycleState.paused -> AppLifecycleState.inactive -> AppLifecycleState.resumed;
- 從前臺退回后臺,控制臺列印的 App 生命周期變化則變成了:AppLifecycleState.resumed -> AppLifecycleState.inactive -> AppLifecycleState.paused
幀繪制回呼
除了需要監聽 App 的生命周期回呼做相應的處理之外,有時候還需要在組件渲染之后做一些與顯示安全相關的操作,
在 iOS 開發中,可以通過 dispatch_async(dispatch_get_main_queue(),^{...}) 方法,讓操作在下一個 RunLoop 執行;在 Android 開發中,可以通過 View.post() 插入訊息佇列,來保證在組件渲染后進行相關操作,
在 Flutter 中實作同樣的需求會更簡單: 依然使用萬能的 WidgetsBinding 來實作,
WidgetsBinding 提供了單次 Frame 繪制回呼,以及實時 Frame 繪制回呼兩種機制,來分別滿足不同的需求:
- 單次 Frame 繪制回呼,通過 addPostFrameCallback 實作,它會在當前 Frame 繪制完成后進行回呼,并只會回呼一次,如果要再次監聽則需要再設定一次,
WidgetsBinding.instance.addPostFrameCallback((_){
print(" 單次 Frame 繪制回呼"); // 只回呼一次
});
- 實時 Frame 繪制回呼,則通過 addPersistentFrameCallback 實作,這個函式會在每次繪制 Frame 結束后進行回呼,可以用作 FPS 檢測,
WidgetsBinding.instance.addPersistentFrameCallback((_){
print(" 實時 Frame 繪制回呼"); // 每幀都回呼
});
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/15115.html
標籤:iOS
上一篇:Method Swizzling
下一篇:【原】MAC安裝Flutter
