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,查看完整代碼。

相關文章
相關標籤/搜索