年前由於業務調整開始接觸學習Flutter,自己重寫了業務線的功能型App。其中遇到不少坑,這次主要總結下路由棧以及生命週期的內容,後續再慢慢補充~
什麼是路由
路由(Routes)是什麼?路由是屏幕或應用程序頁面的抽象。
Flutter 使我們能夠優雅地管理路由主要依賴的是 Navigator(導航器)類。這是一個用於管理一組具有某種進出規則的頁面的 Widget,也就是說用它我們能夠實現各個頁面間有規律的切換。而這裡的規則便是在其內部維護的一個“ 路由棧”。
命名路由
在一般應用中,我們用的最多的還是命名路由,它是將應用中需要訪問的每個頁面命名為不重複的字符串,我們便可以通過這個字符串來將該頁面實例推進路由。
例如:
<code>new MaterialApp( home: new Screen1(), routes: <string> { '/screen1': (BuildContext context) => new Screen1(), '/screen2' : (BuildContext context) => new Screen2(), '/screen3' : (BuildContext context) => new Screen3(), '/screen4' : (BuildContext context) => new Screen4() },)/<string>/<code>
路由棧
Navigator維護了一個堆棧,來保存路由跳轉的狀態。
由 screen1 跳轉到 screen2
<code>Navigator.pushNamed(context, '/screen2')/<code>
退出路由 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 在視圖樹中的位置發生了變化,需要先暫時移除後添加。:warning:注意,重寫的時候必須要調用 super.deactivate()。
dispose()
當 State 被永久的從視圖樹中移除,Framework 會調用該函數。在銷燬前觸發,我們可以在這裡進行最終的資源釋放。在調用這個函數之前,總會先調用 deactivate()。:warning:注意,重寫的時候必須要調用 super.dispose()
didUpdateWidget(covariant T oldWidget)
當 widget 的配置發生變化時,會調用這個函數。比如,Hot-reload 的時候就會調用這個函數。這個函數調用後,會調用 build()。
setState()
當我需要更新 State 的視圖時,需要手動調用這個函數,它會觸發 build() 。
一般情況下我們用的最多的是 initState , build , deactivate , dispose , setState 。接下來也主要講 initState 以及 dispose 在路由跳轉過程中的觸發條件。
路由跳轉時的 initState , deactivate , dispose
一開始我們是這麼寫的
<code>@overridevoid initState() { super.initState(); print('------initstate--------');}void deactivate() { super.deactivate(); print('-----deactivate----');}void dispose() { super.dispose(); print('----dispose--------');}/<code>
當我們通過 pushNamed 跳轉到其他路由,發現 deactivate 觸發了,但是 dispose 並沒有觸發,並且再次通過 pushNamed 回到本頁的時候, initstate 也並沒有再次執行。
寫多了 Vue 單頁面應用的同學,應該知道這其實很讓人困擾。我們很多時候需要像 Vue 一樣,在 created 的時候初始化數據,在 beforeDestroy 的時候移除頁面一些監聽事件或者銷燬定時器,防止內存洩露。
讓我們回到生命週期說明那裡:
deactivate() 當 State 被暫時從視圖樹中移除時,會調用這個函數。
dispose() 當 State 被永久的從視圖樹中移除,Framework 會調用該函數。
此時是不是恍然大悟! pushNamed 只是暫時把視圖移除了,並沒有徹底移除,所以導致了一些奇怪的問題(前提是你跟我一樣,業務上需要控制頁面的初始化和銷燬)
那怎麼辦。此時 popAndPushNamed 閃亮登場!用它替代 pushNamed ,你就會發現該觸發的事件都觸發了~
popAndPushNamed 將當前路由彈出並跳轉到新的路由,徹底移除舊路由。
還有類似的方法 pushReplacementNamed , pushNamedAndRemoveUntil 等,就不贅述了。
取消導航Header的返回鍵
<code>AppBar(automaticallyImplyLeading: false)/<code>
閱讀更多 sandag 的文章