Flutter学习篇(五)——路由剖析前篇

导航

前言

路由, 是前端页面永远绕不过去的一个坎。那么对于万物皆widget的Flutter,路由又会以怎么样的形式存在呢?🔎前端

剖析

咱们先从调用入口开始跟踪,git

Navigator.of(context).pushNamed("xxx")github

那咱们就直接进入Navigator这个文件,文件的结构以下:数组

涉及到几个类,分别 RoutePopDisposition,Route,RouteSettings.NavigatorObserver,Navigator,NavigatorState 这几个类就是咱们了解路由的基础了,咱们先看到Navigator, 好家伙,Navigator 果真又是一个Widget😏,接着利用Android Studio找出调用它的地方,以下:markdown

咱们先到app.dart一探究竟, 发现这里是对Navigator作了初始化工做 ,app

不过,仔细瞧瞧,发现这里又有一个上面提到的类—— NavigatorObserver, 那先过去看看咯。less

/// An interface for observing the behavior of a [Navigator].
class NavigatorObserver {
  /// The navigator that the observer is observing, if any.
  NavigatorState get navigator => _navigator;
  NavigatorState _navigator;

  /// The [Navigator] pushed `route`.
  ///
  /// The route immediately below that one, and thus the previously active
  /// route, is `previousRoute`.
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) { }

  /// The [Navigator] popped `route`.
  ///
  /// The route immediately below that one, and thus the newly active
  /// route, is `previousRoute`.
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) { }

  /// The [Navigator] removed `route`.
  ///
  /// If only one route is being removed, then the route immediately below
  /// that one, if any, is `previousRoute`.
  ///
  /// If multiple routes are being removed, then the route below the
  /// bottommost route being removed, if any, is `previousRoute`, and this
  /// method will be called once for each removed route, from the topmost route
  /// to the bottommost route.
  void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) { }

  /// The [Navigator] replaced `oldRoute` with `newRoute`.
  void didReplace({ Route<dynamic> newRoute, Route<dynamic> oldRoute }) { }

  /// The [Navigator]'s route `route` is being moved by a user gesture.
  ///
  /// For example, this is called when an iOS back gesture starts.
  ///
  /// Paired with a call to [didStopUserGesture] when the route is no longer
  /// being manipulated via user gesture.
  ///
  /// If present, the route immediately below `route` is `previousRoute`.
  /// Though the gesture may not necessarily conclude at `previousRoute` if
  /// the gesture is canceled. In that case, [didStopUserGesture] is still
  /// called but a follow-up [didPop] is not.
  void didStartUserGesture(Route<dynamic> route, Route<dynamic> previousRoute) { }

  /// User gesture is no longer controlling the [Navigator].
  ///
  /// Paired with an earlier call to [didStartUserGesture].
  void didStopUserGesture() { }
}

复制代码

NavigatorObserver是一个Navigator行为的观察者,能够监听到push,remove,pop等行为,它能够在MaterialApp对属性navigatorObservers进行设置,以下:ide

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Demo Home Page'),
      navigatorObservers: [
        MyObserver()
      ],
      routes: {
        "second": (context) => SecondPage(),
      },
    );
  }
}

复制代码

接着咱们借助Android Studio查看Observer的调用, 如下是其中一个调用:post

咱们能够注意到,Navigator在push的时候调用了Observer的didPush, 第一个参数是即将push的route,第二个参数则是当前的路由页面,这样一来,咱们就能够经过全局的Observer监听到整个App的路由变化。
看到这里,我本身内心有两个疑惑🤔,第一个就是为何全局的Observer要设计为数组的形式,第二个就是咱们要怎么在当前页面监听本身的路由状态变化。学习

要解决这两个疑惑,须要从NavigatorObserver入手,再次借助Android Studio(AS真是个好东西😅),不怎么费劲就找到一个继承自NavigatorObserver的类RouteObserver, 下面是它的描述:

A [Navigator] observer that notifies [RouteAware]s of changes to the state of their [Route].

此外还有一些相关的方法:

看到subscribe, unsubscribe了,实锤无疑了🤓,看来这个就是咱们在找的东西了,能够监听当前route的状态改变。 解决了第二个疑惑,其实也就顺带解决了第一个疑惑,既然有这种观察路由的须要,那么意味着须要多个观察者,那Observer设计为数组的形式也就很合理了。
下面是简单的用法:

final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home:  MainPage(),
      navigatorObservers: [
        routeObserver,
        MyObserver()
      ],
      routes: {
        "设置": (context) => SettingsPage(),
      },
    );
  }
}

复制代码
class _SettingsPageState extends State<SettingsPage> with RouteAware{
  final settingsStore = SettingsStore();

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    settingsStore.getPrefsData();
    routeObserver.subscribe(this, ModalRoute.of(context));
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void didPop() {
    super.didPop();
  }

  @override
  void didPush() {
    super.didPush();
  }
}

复制代码

用法也很简单,就是增长了一个全局的观察者,而后在使用的地方进行订阅和注销,这里又发现了一个新的类RouteAware, 天啊😢,好多类,不过嘛,其实就是个简单的接口定义😎:

接着回过头去看到一开始的Navigator.of(context).pushNamed("xxx"), 发现其实是调用了NavigatorStatepushNamed方法,而对于pushNamed方法,又牵涉到name映射到route的关系,咱们来看看这一段的实现:

其实逻辑很简单,就是调用onGenerateRoute生成route, 而且提供了兜底操做,容许使用onUnknownRoute继续生成route。而上面这两个方法其实也是在MaterialApp这个widget进行设置的,咱们照例进行了设置:

Route _onGenerateRoute(RouteSettings routeSettings) {
    return null;
  }

  Route _onUnknownRoute(RouteSettings routeSettings) {
    return MaterialPageRoute(settings:routeSettings, builder: (context) => UnKnowRoutePage());
  }
复制代码

除了这两个属性以外,还提供了一个可供配置的routes,提供name到route映射的map,以下:

{
  routes: <String, WidgetBuilder>{"设置": (context) => SettingsPage()},
}
复制代码

紧接着,咱们找到看到WidgetsApp这个widget,找到里面的_onGenerateRoute方法,

到这里就豁然开朗了,优先取出routes的路由,若是取不到,就调用onGenerateRoute生成路由。

前篇差很少就到这里吧,中篇才是大头,各类Route满天飞😪。

总结

这个剖析前篇其实就是讲了Navigator的初始化,还有NavigatorObserver的应用,经过它能够监听本身路由的状态变化,而且研究了路由的生成配置原理以及兜底方案,固然讲的更多的还有Android Stduio的使用,经过AS在定位代码,查找代码引用确实有奇效。

参考

Flutter笔记--Flutter页面嵌入Android Activity中
Flutter生命周期和Navigator、Route监听

仓库

点击flutter_demo,查看完整代码。

相关文章
相关标签/搜索