Flutter學習篇(五)——路由剖析終篇

少年阿濤 發表於 2019-10-15

導航

前言

路由剖析前篇已經講了Navigator的初始化,路由的對映生成以及觀察訂閱,這個篇章則會側重於路由的形態以及路由的本質。

Flutter學習篇(五)——路由剖析終篇

剖析

我們還是回到NavigatorStatepush方法,

Flutter學習篇(五)——路由剖析終篇

方法雖然不長,資訊量卻不少,簡要來說,有這麼幾點:

  • Route
  • _history
  • return route.popped

做的事情無外乎就是從_history取出oldRoute, 把route加入_history,然後各自呼叫oldRoute,route的一些類似生命週期的方法, 最後返回route.poped。其中有個很特別的地方是呼叫了route.install(),這個我們待會再講。

_history

_history很好理解,它是個List,作為棧儲存每一個路由,push, pop操作分別對應Listadd,removeLast

Route

接著我們看到Route這個類,鋪墊了這麼久,終於要見到路由本尊了,老規矩,先一覽所有👀。

Flutter學習篇(五)——路由剖析終篇
Route的內建方法我們可以簡單分成三類:

  1. 生命週期型別,didPop,didPush等等
  2. 狀態型別,isCurrent,isFirst,isActive
  3. install

前兩類我們就不講了,因為知其名而知其意😎。而install就神祕的很,畢竟它就是路由的核心了,不知道讀者們有沒有發現,其實講到現在,NavigatorRoute的關聯還是很模糊,而install將會揭開這兩者之間的不為人知的祕密(別問我為什麼知道的這麼清楚,因為我事先看過🤣)。

對於install方法,其中有個很重要的類,那就是OverlayEntity, 它提供了Overlay所需要的資訊,如opaque等,而Overlay如其名,是一個懸浮覆蓋的widget,所以說一個好的名字是多麼重要,相信你們也恍然大悟了吧。路由的本質就是就是把我們的頁面包含在Overlay這個widget裡面,然後覆蓋在舊的頁面上去。所以對於彈框來說,也就很好理解了,就是把彈框放入一個透明的Overlay裡面,然後蓋在當前的頁面上面,牛逼🐮!
知道這個關聯之後,我們下一步就是找出RouteOverlayEntity的轉換關係,我們先從平時使用的MaterialPageRoute入手,深扒它的父類,最後在ModalRoute找到了這麼一個方法,

 @override
  Iterable<OverlayEntry> createOverlayEntries() sync* {
    yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier);
    yield OverlayEntry(builder: _buildModalScope, maintainState: maintainState);
  }
複製程式碼

接著我們順藤摸瓜,找到ModalRoute對應的build方法,看到了這麼一個關鍵:

Flutter學習篇(五)——路由剖析終篇

widget.route.buildPage 這不是擺明告訴我們是在這裡建立的頁面嗎,然後我們返回MaterialPageRoute,果不其然

Flutter學習篇(五)——路由剖析終篇
builder是我們傳進去的生成widget的方法,在這裡生成了widget。

所以,綜上,我們的路由頁面通過buildPage作為widget傳遞到ModalRoute,接著在ModalRoute通過createOverlayEntries將路由頁面包裝在Overlay這個覆蓋型的widget。所以install就是這麼一個過程,它將路由頁面對映為Overlay,而Navigator則負責管理路由的進出。

通訊

講完 install,再回過頭看看剛剛的疑問,還有一個地方沒解決的,那就是push之後的返回值route.popped, 究竟為什麼需要這麼一個返回值呢?其實這就是涉及到路由之間的通訊了,當頁面退出時,可以攜帶一些資訊回到上一個頁面。如下:

Flutter學習篇(五)——路由剖析終篇

可以看到,pop方法的引數result傳遞到了didPop,那我們繼續扒一扒didPop

Flutter學習篇(五)——路由剖析終篇

這裡又發現了一個新大陸_popCompleter🙄, 它是個啥呢,官方介紹如下Completer:

  A way to produce Future objects and to complete them later with a value or error.
複製程式碼

它是個可以延後執行的Future物件,那這樣一來就串起來了,路由pop之後,執行了_popCompleter的完成方法,引數自然就通過Future物件傳遞出去,上一個頁面只需要監聽_popCompleter的回撥即可,這時候就輪到push之後的返回值route.popped登場了,沒錯,這個返回值就是_popCompleter的Future物件,如下:

Future<T> get popped => _popCompleter.future;
複製程式碼

所以,梳理一下,當我們push一個路由的時候,我們會拿到下一個路由的 Future物件,當下一個路由pop的時候,Future物件就執行回撥,如下:

  Navigator.of(context).push(MaterialPageRoute(builder:(context) => SettingsPage()).then((value){
                    print(value);
});
複製程式碼

這樣一來,我們就可以監聽到路由傳遞回來的值了。

路由分類

其實可以簡單粗暴地根據opaque將路由劃分為兩類:

  • 透明
  • 不透明

對應的其實就是彈框和頁面,對於頁面級的路由,繼承自PageRoute, 對應的opaque為true,不透明頁面;而彈框繼承自PopupRoute, opaque為false,透明頁面,所以呈現為彈框的形態。

總結

其實這個篇章主要是通過深究push方法,從中窺探路由的本質,對於路由來講,追溯到底,它還是widget,只不過藏得比較深。其實再複雜的東西,一層一層剖開,它仍然是一個系統最基本最核心的個體,這個剖析的過程尚且容易,最難的往往是從0到1,簡單到複雜,這需要極大的智慧去搭建,組裝和落地。

倉庫

點選flutter_demo,檢視完整程式碼。

參考