前言
年前由於業務調整開始接觸學習Flutter,自己重寫了業務線的功能型App。其中遇到不少坑,這次主要總結下路由棧以及生命週期的內容,後續再慢慢補充~
什麼是路由
路由(Routes)是什麼?路由是螢幕或應用程式頁面的抽象。
Flutter 使我們能夠優雅地管理路由主要依賴的是 Navigator(導航器)類。這是一個用於管理一組具有某種進出規則的頁面的 Widget,也就是說用它我們能夠實現各個頁面間有規律的切換。而這裡的規則便是在其內部維護的一個“ 路由棧”。
命名路由
在一般應用中,我們用的最多的還是命名路由,它是將應用中需要訪問的每個頁面命名為不重複的字串,我們便可以通過這個字串來將該頁面例項推進路由。
例如:
new MaterialApp(
home: new Screen1(),
routes: <String, WidgetBuilder> {
'/screen1': (BuildContext context) => new Screen1(),
'/screen2' : (BuildContext context) => new Screen2(),
'/screen3' : (BuildContext context) => new Screen3(),
'/screen4' : (BuildContext context) => new Screen4()
},
)
複製程式碼
路由棧
Navigator維護了一個堆疊,來儲存路由跳轉的狀態。
由screen1
跳轉到screen2
Navigator.pushNamed(context, '/screen2')
複製程式碼
退出路由pop
Navigator.pop()
切勿用pushNamed
替代pop
,不然就會出現下面的情況了。
生命週期
簡單地聊完路由棧,接下來介紹 StatefulWidget
一系列的生命週期。
上圖就是 State 的生命週期圖。
StatefulWidget.createState()
Framework 通過呼叫 StatefulWidget.createState() 來建立一個 State。
initState()
新建立的 State 會和一個 BuildContext 產生關聯,此時認為 State 已經被安裝好了,initState() 函式將會被呼叫。通常,我們可以重寫這個函式,進行初始化操作。
didChangeDependencies()
在 initState() 呼叫結束後,這個函式會被呼叫。事實上,當 State 物件的依賴關係發生變化時,這個函式總會被 Framework 呼叫。
build()
經過以上步驟,系統認為一個 State 已經準備好了,就會呼叫 build() 來構建檢視。我們需要在這個函式中,返回一個 Widget。
deactivate()
當 State 被暫時從檢視樹中移除時,會呼叫這個函式。頁面切換時,也會呼叫它,因為此時 State 在檢視樹中的位置發生了變化,需要先暫時移除後新增。⚠️注意,重寫的時候必須要呼叫 super.deactivate()。
dispose()
當 State 被永久的從檢視樹中移除,Framework 會呼叫該函式。在銷燬前觸發,我們可以在這裡進行最終的資源釋放。在呼叫這個函式之前,總會先呼叫 deactivate()。⚠️注意,重寫的時候必須要呼叫 super.dispose()
didUpdateWidget(covariant T oldWidget)
當 widget 的配置發生變化時,會呼叫這個函式。比如,Hot-reload 的時候就會呼叫這個函式。這個函式呼叫後,會呼叫 build()。
setState()
當我需要更新 State 的檢視時,需要手動呼叫這個函式,它會觸發 build() 。
一般情況下我們用的最多的是initState
,build
,deactivate
,dispose
,setState
。接下來也主要講initState
以及dispose
在路由跳轉過程中的觸發條件。
路由跳轉時的initState
,deactivate
,dispose
一開始我們是這麼寫的
@override
void initState() {
super.initState();
print('------initstate--------');
}
void deactivate() {
super.deactivate();
print('-----deactivate----');
}
void dispose() {
super.dispose();
print('----dispose--------');
}
複製程式碼
當我們通過pushNamed
跳轉到其他路由,發現deactivate
觸發了,但是dispose
並沒有觸發,並且再次通過pushNamed
回到本頁的時候,initstate
也並沒有再次執行。
寫多了Vue
單頁面應用的同學,應該知道這其實很讓人困擾。我們很多時候需要像Vue
一樣,在created
的時候初始化資料,在beforeDestroy
的時候移除頁面一些監聽事件或者銷燬定時器,防止記憶體洩露。
讓我們回到生命週期說明那裡:
deactivate()
當 State 被暫時從檢視樹中移除時,會呼叫這個函式。
dispose()
當 State 被永久的從檢視樹中移除,Framework 會呼叫該函式。
此時是不是恍然大悟!pushNamed
只是暫時把檢視移除了,並沒有徹底移除,所以導致了一些奇怪的問題(前提是你跟我一樣,業務上需要控制頁面的初始化和銷燬)
那怎麼辦。此時popAndPushNamed
閃亮登場!用它替代pushNamed
,你就會發現該觸發的事件都觸發了~
popAndPushNamed
將當前路由彈出並跳轉到新的路由,徹底移除舊路由。
還有類似的方法pushReplacementNamed
,pushNamedAndRemoveUntil
等,就不贅述了。
取消導航Header的返回鍵
AppBar(automaticallyImplyLeading: false)
複製程式碼
WillPopScope
取消Android物理返回鍵
這個方法也可以取消iOS預設左右滑動切換路由。
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
// Your Code
)
);
}
複製程式碼