Flutter学习篇(六)——路由剖析终篇

1,821 阅读5分钟

导航

前言

路由剖析前篇已经讲了Navigator的初始化,路由的映射生成以及观察订阅,这个篇章则会侧重于路由的形态以及路由的本质。

剖析

我们还是回到NavigatorStatepush方法,

方法虽然不长,信息量却不少,简要来说,有这么几点:

  • Route
  • _history
  • return route.popped

做的事情无外乎就是从_history取出oldRoute, 把route加入_history,然后各自调用oldRoute,route的一些类似生命周期的方法, 最后返回route.poped。其中有个很特别的地方是调用了route.install(),这个我们待会再讲。

_history

_history很好理解,它是个List,作为栈存储每一个路由,push, pop操作分别对应Listadd,removeLast

Route

接着我们看到Route这个类,铺垫了这么久,终于要见到路由本尊了,老规矩,先一览所有👀。

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方法,看到了这么一个关键:

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

builder是我们传进去的生成widget的方法,在这里生成了widget。

所以,综上,我们的路由页面通过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,查看完整代码。

参考