前言
上一篇 Flutter路由管理和頁面參數(shù)的傳遞(獲取&返回) 文章中我們講述了這么用代碼實現(xiàn) Flutter
中頁面參數(shù)的傳遞,這一篇我們用源碼分析一下 Navigator
為什么可以進行頁面參數(shù)傳遞。
從頁面跳轉(zhuǎn)入口的代碼進行分析:
Navigator.of(context).pushNamed('/route1');
Navigator 的獲取
Navigator
對應(yīng)的 State
是 NavigatorState
,所以實際上我們需要獲取的是 NavigatorState
。
class Navigator extends StatefulWidget {
/******部分代碼省略*****/
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;
}
}
我們從源看到 NavigatorState
的獲取實際是獲取的 context.ancestorStateOfType
。
abstract class Element extends DiagnosticableTree implements BuildContext {
/******部分代碼省略*****/
@override
State ancestorStateOfType(TypeMatcher matcher) {
assert(_debugCheckStateIsActiveForAncestorLookup());
Element ancestor = _parent;
while (ancestor != null) {
//從當(dāng)前的Element節(jié)點一直向上尋找到匹配的StatefulElement
if (ancestor is StatefulElement && matcher.check(ancestor.state))
break;
ancestor = ancestor._parent;
}
final StatefulElement statefulAncestor = ancestor;
//返回匹配的StatefulElement的state
return statefulAncestor?.state;
}
}
循環(huán)遍歷向上尋找 Navigato
r 的 state
,這里就是 NavigatorState
。
Navigator的生成
Navigator
的 Widget
是是什么時候添加到視圖樹中的呢?我們從 Flutter
應(yīng)用程序的入口開始一步一步跟進代碼的執(zhí)行:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(...);
}
}
MaterialApp
傳入 routes
和 onGenerateRoute
等參數(shù),MaterialApp
的 state
是 _MaterialAppState
它構(gòu)建的是 WidgetsApp
類型的 Widget
,同時 routes
和 onGenerateRoute
等參數(shù)也進行了透傳。
class MaterialApp extends StatefulWidget {
const MaterialApp({
Key key,
this.navigatorKey,
this.home,
this.routes = const <String, WidgetBuilder>{},
this.initialRoute,
this.onGenerateRoute,
this.onUnknownRoute,
/******部分代碼省略*****/
})
/******部分代碼省略*****/
@override
_MaterialAppState createState() => _MaterialAppState();
}
class _MaterialAppState extends State<MaterialApp> {
/******部分代碼省略*****/
@override
Widget build(BuildContext context) {
Widget result = WidgetsApp(
key: GlobalObjectKey(this),
navigatorKey: widget.navigatorKey,
navigatorObservers: _navigatorObservers,
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) =>
MaterialPageRoute<T>(settings: settings, builder: builder),
home: widget.home,
routes: widget.routes,
initialRoute: widget.initialRoute,
onGenerateRoute: widget.onGenerateRoute,
onUnknownRoute: widget.onUnknownRoute,
/******部分代碼省略*****/
);
}
我們再看看 WidgetsApp
對應(yīng)的 State
的 _WidgetsAppState
。在_WidgetsAppState
的 Widget build(BuildContext context)
方法中我們找到了管理路由的 Navigator
的構(gòu)造時機。
class WidgetsApp extends StatefulWidget {
WidgetsApp({ // can't be const because the asserts use methods on Iterable :-(
Key key,
this.navigatorKey,
this.onGenerateRoute,
this.onUnknownRoute,
this.navigatorObservers = const <NavigatorObserver>[],
this.initialRoute,
this.pageRouteBuilder,
this.home,
this.routes = const <String, WidgetBuilder>{},
/******部分代碼省略*****/
);
@override
_WidgetsAppState createState() => _WidgetsAppState();
}
class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
Widget navigator;
if (_navigator != null) {
navigator = Navigator(
key: _navigator,
// If window.defaultRouteName isn't '/', we should assume it was set
// intentionally via `setInitialRoute`, and should override whatever
// is in [widget.initialRoute].
initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
? WidgetsBinding.instance.window.defaultRouteName
: widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
onGenerateRoute: _onGenerateRoute,
onUnknownRoute: _onUnknownRoute,
observers: widget.navigatorObservers,
);
}
Widget result;
if (widget.builder != null) {
result = Builder(
builder: (BuildContext context) {
return widget.builder(context, navigator);
},
);
} else {
assert(navigator != null);
result = navigator;
}
/******部分代碼省略*****/
/**上面經(jīng)過多次的操作之后,navigator變?yōu)閞esult的某個子孫節(jié)點上的child**/
Widget title;
if (widget.onGenerateTitle != null) {
title = Builder(
// This Builder exists to provide a context below the Localizations widget.
// The onGenerateTitle callback can refer to Localizations via its context
// parameter.
builder: (BuildContext context) {
final String title = widget.onGenerateTitle(context);
assert(title != null, 'onGenerateTitle must return a non-null String');
return Title(
title: title,
color: widget.color,
child: result,
);
},
);
} else {
title = Title(
title: widget.title,
color: widget.color,
child: result,
);
}
/******部分代碼省略*****/
/**上面經(jīng)過多次的操作之后,result變?yōu)閠itle的某個子孫節(jié)點上的child**/
return MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
child: Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
//將title作為child視圖,也就是說navigator變?yōu)槠渲械哪硞€子孫節(jié)點視圖
child: title,
),
);
}
}
在構(gòu)建的 MediaQuery 就存在我們需要的 Navigator
。
這張圖是程序運行時候使用(DevTools)進行的頁面元素分析,也證明了 Navigator
是在頁面的 Widget
元素路徑上的。
pushNamed方法解析
@optionalTypeArgs
static Future<T> pushNamed<T extends Object>(
BuildContext context,
String routeName, {
Object arguments,
}) {
return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
}
@optionalTypeArgs
Future<T> pushNamed<T extends Object>(
String routeName, {
Object arguments,
}) {
return push<T>(_routeNamed<T>(routeName, arguments: arguments));
}
Route<T> _routeNamed<T>(String name, { @required Object arguments, bool allowNull = false }) {
assert(!_debugLocked);
assert(name != null);
final RouteSettings settings = RouteSettings(
name: name,
isInitialRoute: _history.isEmpty,
arguments: arguments,
);
Route<T> route = widget.onGenerateRoute(settings);
if (route == null && !allowNull) {
assert(() {
if (widget.onUnknownRoute == null) {
throw FlutterError(...);
}
return true;
}());
route = widget.onUnknownRoute(settings);
assert(() {
if (route == null) {
throw FlutterError(...);
}
return true;
}());
}
return route;
}
我們看到是調(diào)用了 widget.onGenerateRoute(settings)
生成路由, 這里的 onGenerateRoute
在 Navigator
在構(gòu)造的時候傳入的 onGenerateRoute
。
onGenerateRoute
Navigator
在構(gòu)造的時候如果我們細心就會發(fā)現(xiàn) onGenerateRoute
現(xiàn)在改為了 _onGenerateRoute
。
也就是 _WidgetsAppState
的 _onGenerateRoute
方法實現(xiàn):
Route<dynamic> _onGenerateRoute(RouteSettings settings) {
final String name = settings.name;
//從widget注冊的路由中獲取name對應(yīng)的WidgetBuilder
final WidgetBuilder pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null
? (BuildContext context) => widget.home
: widget.routes[name];
//如果pageContentBuilder不為空,那么和RouteSettings一起執(zhí)行widget.pageRouteBuilder構(gòu)造一個route
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<dynamic>(
settings,
pageContentBuilder,
);
assert(route != null,
'The pageRouteBuilder for WidgetsApp must return a valid non-null Route.');
return route;
}
//如果pageContentBuilder為空,那么執(zhí)行widget.onGenerateRoute的方法
if (widget.onGenerateRoute != null)
return widget.onGenerateRoute(settings);
return null;
}
widget.pageRouteBuilder
的方法,我們在生成 WidgetsApp
可以看到是:
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) =>
MaterialPageRoute<T>(settings: settings, builder: builder)
所以最終我們通過在 MaterialApp
注冊 routes
生成了一個 MaterialPageRoute
用來進行頁面跳轉(zhuǎn)。
最后如果 routes
為空的話,我們執(zhí)行 widget.onGenerateRoute
。這個解釋了在 Flutter路由管理和頁面參數(shù)的傳遞(獲取&返回) 這篇文章末尾說的 onGenerateRoute
方式進行的參數(shù)傳遞,必須不能進行 routers
的注冊。
文章到這里就全部講述完啦,若有其他需要交流的可以留言哦!!
想閱讀作者的更多文章,可以查看我 個人博客 和公共號: