导航
- Flutter学习篇(一)—— Dialog的简单使用
- Flutter学习篇(二)—— Drawer和水纹按压效果
- Flutter学习篇(三)—— MobX的使用和原理
- Flutter学习篇(四)—— 尺寸解惑
- Flutter学习篇(五)——路由剖析前篇
前言
在路由剖析前篇已经讲了Navigator的初始化,路由的映射生成以及观察订阅,这个篇章则会侧重于路由的形态以及路由的本质。

剖析
我们还是回到NavigatorState的push方法,

方法虽然不长,信息量却不少,简要来说,有这么几点:
- Route
- _history
- return route.popped
做的事情无外乎就是从_history取出oldRoute, 把route加入_history,然后各自调用oldRoute,route的一些类似生命周期的方法, 最后返回route.poped。其中有个很特别的地方是调用了route.install(),这个我们待会再讲。
_history
_history很好理解,它是个List,作为栈存储每一个路由,push, pop操作分别对应List的add,removeLast。
Route
接着我们看到Route这个类,铺垫了这么久,终于要见到路由本尊了,老规矩,先一览所有👀。

Route的内置方法我们可以简单分成三类:
- 生命周期类型,
didPop,didPush等等 - 状态类型,
isCurrent,isFirst,isActive等 - install
前两类我们就不讲了,因为知其名而知其意😎。而install就神秘的很,毕竟它就是路由的核心了,不知道读者们有没有发现,其实讲到现在,Navigator和Route的关联还是很模糊,而install将会揭开这两者之间的不为人知的秘密(别问我为什么知道的这么清楚,因为我事先看过🤣)。
对于install方法,其中有个很重要的类,那就是OverlayEntity, 它提供了Overlay所需要的信息,如opaque等,而Overlay如其名,是一个悬浮覆盖的widget,所以说一个好的名字是多么重要,相信你们也恍然大悟了吧。路由的本质就是就是把我们的页面包含在Overlay这个widget里面,然后覆盖在旧的页面上去。所以对于弹框来说,也就很好理解了,就是把弹框放入一个透明的Overlay里面,然后盖在当前的页面上面,牛逼🐮!
知道这个关联之后,我们下一步就是找出Route到OverlayEntity的转换关系,我们先从平时使用的MaterialPageRoute入手,深扒它的父类,最后在ModalRoute找到了这么一个方法,
@override
Iterable<OverlayEntry> createOverlayEntries() sync* {
yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier);
yield OverlayEntry(builder: _buildModalScope, maintainState: maintainState);
}
接着我们顺藤摸瓜,找到ModalRoute对应的build方法,看到了这么一个关键:

widget.route.buildPage 这不是摆明告诉我们是在这里创建的页面吗,然后我们返回MaterialPageRoute,果不其然

所以,综上,我们的路由页面通过buildPage作为widget传递到ModalRoute,接着在ModalRoute通过createOverlayEntries将路由页面包装在Overlay这个覆盖型的widget。所以install就是这么一个过程,它将路由页面映射为Overlay,而Navigator则负责管理路由的进出。
通信
讲完 install,再回过头看看刚刚的疑问,还有一个地方没解决的,那就是push之后的返回值route.popped, 究竟为什么需要这么一个返回值呢?其实这就是涉及到路由之间的通信了,当页面退出时,可以携带一些信息回到上一个页面。如下:

可以看到,pop方法的参数result传递到了didPop,那我们继续扒一扒didPop,

这里又发现了一个新大陆_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,查看完整代码。