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
网友评论