StatefulWidget 須要藉助於 State 對象,在特定的階段來處理用戶的交互或其內部數據的變化,並體如今 UI 上。這個特定的階段,就涵蓋來一個組件從加載到卸載的全過程,即生命週期。Flutter 中的 Widget 也存在生命週期,而且經過 State 來體現。安全
而 App 則是一個特殊的 Widget。除了須要處理視圖顯示的各個階段(即視圖的生命週期)以外,還須要應對應用從啓動到退出所經歷的各個狀態(App 的生命週期)。app
State 的生命週期,指的是在用戶參與的狀況下,其關聯的 Widget 所經歷的,從建立到顯示再到更新最後到中止,直至銷燬等各個過程階段。State 的生命週期能夠分爲 3 個階段:建立(插入視圖樹)、更新(在視圖樹中存在)、銷燬(從視圖樹中移除)。async
State 初始化時會依次執行:構造方法 -> initState -> disChangeDependencies -> build,隨後完成頁面渲染。ide
初始化過程當中每一個方法的意義以下:函數
Widget 的狀態更新,主要由 3 個方法觸發:setState、didchangeDependencies 與 didUpdateWidget.oop
如上三個方法對應的調用場景:post
組件銷燬比較簡單。好比組件被移除,或是頁面銷燬的時候,系統會調用 deactivate 和 dispose 這兩個方法,來移除或銷燬組件。ui
具體調用機制以下:this
從功能、調用時機和調用次數的緯度總結這些方法以下;spa
方法名 | 功能 | 調用時機 | 調用次數 |
---|---|---|---|
構造方法 | 接收父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 從啓動到退出的全過程。
在原生 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 這三個。
案例分享:在 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 的生命週期回調作相應的處理以外,有時候還須要在組件渲染以後作一些與顯示安全相關的操做。
在 iOS 開發中,能夠經過 dispatch_async(dispatch_get_main_queue(),^{...}) 方法,讓操做在下一個 RunLoop 執行;在 Android 開發中,能夠經過 View.post() 插入消息隊列,來保證在組件渲染後進行相關操做。
在 Flutter 中實現一樣的需求會更簡單: 依然使用萬能的 WidgetsBinding 來實現。
WidgetsBinding 提供了單次 Frame 繪製回調,以及實時 Frame 繪製回調兩種機制,來分別知足不一樣的需求:
WidgetsBinding.instance.addPostFrameCallback((_){ print(" 單次 Frame 繪製回調"); // 只回調一次 });
WidgetsBinding.instance.addPersistentFrameCallback((_){ print(" 實時 Frame 繪製回調"); // 每幀都回調 });