美文网首页Flutter跨平台应用Flutter
Flutter源码学习--Navigator.of

Flutter源码学习--Navigator.of

作者: 慕北人 | 来源:发表于2019-03-02 00:20 被阅读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

    相关文章

      网友评论

        本文标题:Flutter源码学习--Navigator.of

        本文链接:https://www.haomeiwen.com/subject/mtusuqtx.html