Flutter跨平台应用Flutter

Flutter源码学习--Navigator.of

2019-03-02  本文已影响0人  慕北人

Flutter源码学习--Navigator

一、Navigator.of(context)

在使用Navigator.of(context)的时候,文档中会提到:The state from the closest instance of this class that encloses the given context.一脸懵逼,这是啥意思?我们先看看这个方法的源码压压惊:

  static NavigatorState of(
    BuildContext context, {
      bool rootNavigator = false,
      bool nullOk = false,
    }) {
    final NavigatorState navigator = rootNavigator
        ? context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>())
        : context.ancestorStateOfType(const TypeMatcher<NavigatorState>());
    assert(() {
      if (navigator == null && !nullOk) {
        throw FlutterError(
          'Navigator operation requested with a context that does not include a Navigator.\n'
          'The context used to push or pop routes from the Navigator must be that of a '
          'widget that is a descendant of a Navigator widget.'
        );
      }
      return true;
    }());
    return navigator;
  }  

根据注释可以知道,如果rootNavigator为true的话,返回的是the state from the furthest instance of this class is given instead

经过跟踪,发现这两个方法的实现在Element这个类中:

  @override
  State rootAncestorStateOfType(TypeMatcher matcher) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    Element ancestor = _parent;
    StatefulElement statefulAncestor;
    while (ancestor != null) {
      if (ancestor is StatefulElement && matcher.check(ancestor.state))
        statefulAncestor = ancestor;
      ancestor = ancestor._parent;
    }
    return statefulAncestor?.state;
  }  

可以看到,有一个while循环会找遍Widget树的所有元素,而且最终的结果是最初的NavigatorState(因为matcher参数为NavigatorState);这就引出了下一个问题,最初的NavigatorState是哪里创建的呢?文档中说MatrialApp和WidgetsApp,由于我们常用的是MatrialApp,所以看看它的源码:

@override
  Widget build(BuildContext context) {
    final ThemeData theme = widget.theme ?? ThemeData.fallback();
    Widget result = AnimatedTheme(
      data: theme,
      isMaterialAppTheme: true,
      child: WidgetsApp(
        key: GlobalObjectKey(this),
        navigatorKey: widget.navigatorKey,
        navigatorObservers: _navigatorObservers,
        // TODO(dnfield): when https://github.com/dart-lang/sdk/issues/34572 is resolved
        // this can use type arguments again
        pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
          MaterialPageRoute<dynamic>(settings: settings, builder: builder),
        home: widget.home,
        routes: widget.routes,
        initialRoute: widget.initialRoute,
        onGenerateRoute: widget.onGenerateRoute,
        onUnknownRoute: widget.onUnknownRoute,
        builder: widget.builder,
        title: widget.title,
        onGenerateTitle: widget.onGenerateTitle,
        textStyle: _errorTextStyle,
        // blue is the primary color of the default theme
        color: widget.color ?? theme?.primaryColor ?? Colors.blue,
        locale: widget.locale,
        localizationsDelegates: _localizationsDelegates,
        localeResolutionCallback: widget.localeResolutionCallback,
        localeListResolutionCallback: widget.localeListResolutionCallback,
        supportedLocales: widget.supportedLocales,
        showPerformanceOverlay: widget.showPerformanceOverlay,
        checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages,
        checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
        showSemanticsDebugger: widget.showSemanticsDebugger,
        debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner,
        inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) {
          return FloatingActionButton(
            child: const Icon(Icons.search),
            onPressed: onPressed,
            mini: true,
          );
        },
      )
    );  

上面的是MatrialAppState的build方法,可见其child是WidgetsApp,看来我们又绕了回来,不得不去看WidgetsApp的源码了:

@override
  Widget build(BuildContext context) {
    Widget navigator;
    if (_navigator != null) {
      navigator = Navigator(
        key: _navigator,
        // If ui.window.defaultRouteName isn't '/', we should assume it was set
        // intentionally via `setInitialRoute`, and should override whatever
        // is in [widget.initialRoute].
        initialRoute: ui.window.defaultRouteName != Navigator.defaultRouteName
            ? ui.window.defaultRouteName
            : widget.initialRoute ?? ui.window.defaultRouteName,
        onGenerateRoute: _onGenerateRoute,
        onUnknownRoute: _onUnknownRoute,
        observers: widget.navigatorObservers,
      );
    }

    ....
  }  

可以看到,就是这里创建了Navigator对象(其State自然就是NavigatorState),这下就搞懂了NavigatorState是在哪里初始化的了。

二、pushName(String)

二话不说,直接上源码:

Future<T> pushNamed<T extends Object>(String routeName) {
    return push<T>(_routeNamed<T>(routeName));
  }  

首先来看看_routeNamed<T>(routeName)是干啥的。

Route<T> _routeNamed<T>(String name, { bool allowNull = false }) {
    ...
    final RouteSettings settings = RouteSettings(
      name: name,
      isInitialRoute: _history.isEmpty,
    );
    Route<T> route = widget.onGenerateRoute(settings);
    if (route == null && !allowNull) {
      assert(() {
        if (widget.onUnknownRoute == null) {
          throw FlutterError(
            'If a Navigator has no onUnknownRoute, then its onGenerateRoute must never return null.\n'
            'When trying to build the route "$name", onGenerateRoute returned null, but there was no '
            'onUnknownRoute callback specified.\n'
            'The Navigator was:\n'
            '  $this'
          );
        }
        return true;
      }());
      route = widget.onUnknownRoute(settings);
      assert(() {
        if (route == null) {
          throw FlutterError(
            'A Navigator\'s onUnknownRoute returned null.\n'
            'When trying to build the route "$name", both onGenerateRoute and onUnknownRoute returned '
            'null. The onUnknownRoute callback should never return null.\n'
            'The Navigator was:\n'
            '  $this'
          );
        }
        return true;
      }());
    }
    return route;
  }  

正常情况下,我们的Route都应该是Route<T> route = widget.onGenerateRoute(settings);这句话生成的,现在就来看看onGenerateRoute方法的实现,注意我们上面说了Navigator最初的对象是由WidgetsApp创建的,而在创建的时候传入了onGenerateRoute这个参数,所以其方法实现是在WidgetsApp中的。

Route<dynamic> _onGenerateRoute(RouteSettings settings) {
    final String name = settings.name;
    final WidgetBuilder pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null
        ? (BuildContext context) => widget.home
        : widget.routes[name];

    if (pageContentBuilder != null) {
      assert(widget.pageRouteBuilder != null,
        'The default onGenerateRoute handler for WidgetsApp must have a '
        'pageRouteBuilder set if the home or routes properties are set.');
      final Route<dynamic> route = widget.pageRouteBuilder(
        settings,
        pageContentBuilder,
      );
      assert(route != null,
        'The pageRouteBuilder for WidgetsApp must return a valid non-null Route.');
      return route;
    }
    if (widget.onGenerateRoute != null)
      return widget.onGenerateRoute(settings);
    return null;
  }

正常情况下,final WidgetBuilder pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null ? (BuildContext context) => widget.home : widget.routes[name];得到的pageContentBuilder是不为空的(MatrialApp的routes参数的map中的value就是一个WidgetBuilder),所以最终得到的Route应该是final Route<dynamic> route = widget.pageRouteBuilder(settings, pageContentBuilder, );这句生成的,而WidgetsApp初始化的时候MatrialApp传递的该参数为:

pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) =>
      MaterialPageRoute<dynamic>(settings: settings, builder: builder),  

所以我们得到的Route对象是一个MatrialPageRoute

最终的push方法:

Future<T> push<T extends Object>(Route<T> route) {
    assert(!_debugLocked);
    assert(() { _debugLocked = true; return true; }());
    assert(route != null);
    assert(route._navigator == null);
    final Route<dynamic> oldRoute = _history.isNotEmpty ? _history.last : null;
    route._navigator = this;
    route.install(_currentOverlayEntry);
    _history.add(route);
    route.didPush();
    route.didChangeNext(null);
    if (oldRoute != null) {
      oldRoute.didChangeNext(route);
      route.didChangePrevious(oldRoute);
    }
    for (NavigatorObserver observer in widget.observers)
      observer.didPush(route, oldRoute);
    assert(() { _debugLocked = false; return true; }());
    _afterNavigation();
    return route.popped;
  }  

三、总结

pushName的参数的一定要是定义在距离该context最近的MatrialApp中才有效,如果想要访问远处的MatrialApp中的NavigatorState,那么Navigator.of的第二个参数请设置为true

上一篇下一篇

猜你喜欢

热点阅读