在Flutter中,路由间的页面跳转使用的是Navigator.push
、Navigator.pop
方法。html
在页面跳转时如何将数据传递过去,目前有两种方法:node
一、目标页面的构造函数显式接收参数。例如跳转过去的是SearchPage
,接收一个字符串参数,则以下所示。bash
Navigator.push(context, new MaterialPageRoute(builder: (BuildContext context) => new SearchPage("传递的参数"))); 复制代码
二、咱们还可使用Route
的RouteSetting
参数进行数据的传递。例如markdown
Navigator.push(context, new MaterialPageRoute(builder: (BuildContext context) => new SearchPage() , settings: RouteSettings(arguments: "传递的参数"))); 复制代码
在页面SearchPage
能够这样获取传递过来的参数:ide
String arg = ModalRoute.of(context).settings.arguments;
复制代码
这里咱们想说一下第二种方式数据传递的原理,让咱们从源码中一窥究竟吧!函数
究竟怎么开始分析源码呢?咱们用打断点的形式,就能一步一步地看到每个方法的执行过程,既然最终的目标页是SearchPage
,那么咱们就将断点打在SearchPage
的build
方法,因为SearchPage
是一个StatefulWidget
,则断点打在了SearchPageState
的build
方法里,以下图所示。 oop
context
是一个
StatefulElement
,依次点开它的
_parent
变量,以下两图所示。
Navigator
的身影。也就是说,使用
Navigator.push
方法跳转到
SearchPage
,则至关于将
SearchPage
挂载在了
Navigator
这个
Widget
下,
SearchPage
至关于
Navigator
的一个子组件。
接下来,咱们将断点打在SearchPage中使用ModalRoute.of
方法这一行,以下图所示。 ui
ModalRoute
的
of
方法。
static ModalRoute<T> of<T extends Object>(BuildContext context) { final _ModalScopeStatus widget = context.inheritFromWidgetOfExactType(_ModalScopeStatus); return widget?.route; } 复制代码
这里的context
就是SearchPageElement
,调用了inheritFromWidgetOfExactType
方法,并将_ModalScopeStatus
做为参数传递过去。this
@override InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) { assert(_debugCheckStateIsActiveForAncestorLookup()); final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType]; if (ancestor != null) { assert(ancestor is InheritedElement); return inheritFromElement(ancestor, aspect: aspect); } _hadUnsatisfiedDependencies = true; return null; } 复制代码
上面的方法逻辑也挺简单的,_inheritedWidgets
是一个Map
,若是不为空则获取_inheritedWidgets
的key
为_ModalScopeStatus
的InheritedElement
对象。那么_inheritedWidgets
是何时被赋值的呢?咱们全局搜索一下,发如今 _updateInheritance
中有对_inheritedWidgets
进行操做。spa
void _updateInheritance() { assert(_active); final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets; if (incomingWidgets != null) _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets); else _inheritedWidgets = HashMap<Type, InheritedElement>(); _inheritedWidgets[widget.runtimeType] = this; } 复制代码
首先它会将父类(_parent
)的_inheritedWidgets
赋值给本身的_inheritedWidgets
,而后对于_inheritedWidgets
的当前运行类型,将本身(this
)进行赋值。
如今让咱们回过头来看一下上面的ModalRoute.of
方法。
static ModalRoute<T> of<T extends Object>(BuildContext context) { final _ModalScopeStatus widget = context.inheritFromWidgetOfExactType(_ModalScopeStatus); return widget?.route; } 复制代码
第一行返回了_ModalScopeStatus
对象。根据上面的结论,咱们知道_inheritedWidgets[_ModalScopeStatus]
就是在_ModalScopeStatus
的_updateInheritance
进行赋值的。
也就是说context.inheritFromWidgetOfExactType
实际上是从下往上寻找_ModalScopeStatus
对象,而后of
方法会返回_ModalScopeStatus
的route
变量。
从Navigator
到SearchPage
的Widget
树能够看到,_ModalScopeStatus
的确存在于上述的Widget
树中,以下图所示。
因而咱们有理由得出以下大推论:
Navigator.push方法自上而下地产生了从Navigator到_ModalScopeStatus到SearchPage的Widget树,而且Navigator将路由信息存储在了_ModalScopeStatus这个Widget中,SearchPage自下而上寻找_ModalScopeStatus并获取其中的路由信息。
为了验证上述推论的正确性,咱们从Navigator
的build
方法出发,一步步地看是否真的有将Route
对象传递到_ModalScopeStatus
对象。
Widget build(BuildContext context) { return Listener( onPointerDown: _handlePointerDown, onPointerUp: _handlePointerUpOrCancel, onPointerCancel: _handlePointerUpOrCancel, child: AbsorbPointer( absorbing: false, // it's mutated directly by _cancelActivePointers above child: FocusScope( node: focusScopeNode, autofocus: true, child: Overlay( key: _overlayKey, initialEntries: _initialOverlayEntries, ), ), ), ); } 复制代码
从上面的build
方法与上面打断点时的Widget
树得知,Navigator
的子Widget
树为Listener->AbsorbPointer->FocusScope->Overlay
,而Overlay
的build
方法以下。
Widget build(BuildContext context) { final List<Widget> onstageChildren = <Widget>[]; final List<Widget> offstageChildren = <Widget>[]; bool onstage = true; for (int i = _entries.length - 1; i >= 0; i -= 1) { final OverlayEntry entry = _entries[i]; if (onstage) { onstageChildren.add(_OverlayEntry(entry)); if (entry.opaque) onstage = false; } else if (entry.maintainState) { offstageChildren.add(TickerMode(enabled: false, child: _OverlayEntry(entry))); } } return _Theatre( onstage: Stack( fit: StackFit.expand, children: onstageChildren.reversed.toList(growable: false), ), offstage: offstageChildren, ); } 复制代码
则Overlay
的子Widget
树为_Theatre->Stack
,而Stack
的children
为onstageChildren
列表,则Stack
的子Widget
为_OverlayEntry
,而_OverlayEntry
的build
方法以下。
Widget build(BuildContext context) { return widget.entry.builder(context); } 复制代码
Navigator
的子
Widget
树得知,
_OverlayEntry
的子
Widget
为
_ModalScope
,也就是说咱们得出第一个小推论:
widget.entry.builder(context)返回的Widget就是_ModalScope。
咱们验证一下对不对,widget.entry
实际上是从OverlayState
的_enties
列表中获取的,而_enties
列表是在Navigator
的build
方法中经过Overlay
传递_initialOverlayEntries
参数初始化的。
child: Overlay(
key: _overlayKey,
initialEntries: _initialOverlayEntries,
),
复制代码
而_initialOverlayEntries
是在Navigator
的initState
初始化的。
void initState() { ...省略 for (Route<dynamic> route in _history) _initialOverlayEntries.addAll(route.overlayEntries); } 复制代码
这里传递进来的是Route
对象的overlayEntries
,而Route
对象的overlayEntries
,而这里的Route
对象实际上是MaterialPageRoute
,因此的overlayEntries
在MaterialPageRoute
的父类OverlayRoute
被重写了,因此它真实的赋值是在OverlayRoute
的install
方法里面,而install
方法在Navigator.push
方法中就被调用了,因此咱们看一下install
方法。
void install(OverlayEntry insertionPoint) {
assert(_overlayEntries.isEmpty);
_overlayEntries.addAll(createOverlayEntries());
navigator.overlay?.insertAll(_overlayEntries, above: insertionPoint);
super.install(insertionPoint);
}
复制代码
_overlayEntries
添加了createOverlayEntries
方法的执行结果。
Iterable<OverlayEntry> createOverlayEntries() sync* {
yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier);
yield OverlayEntry(builder: _buildModalScope, maintainState: maintainState);
}
复制代码
上面方法构建了一个OverlayEntry
对象,其中的builder
参数传了_buildModalScope
,而_buildModalScope
返回的是_ModalScope
对象。
Widget _buildModalScope(BuildContext context) { return _modalScopeCache ??= _ModalScope<T>( key: _scopeKey, route: this, // _ModalScope calls buildTransitions() and buildChild(), defined above ); } 复制代码
到这里,咱们终于验证了第一个小推论的正确性,即_OverlayEntry
的子Widget
就是_ModalScope
,并且这个_ModalScope
还包含了路由信息,就是其中的route
变量。
咱们再来看_ModalScope
的build
方法。
Widget build(BuildContext context) { return _ModalScopeStatus( route: widget.route, ...省略 ); } 复制代码
它返回了_ModalScopeStatus
,并将它的route
变量赋值给了_ModalScopeStatus
的route
变量,咱们上面说过_ModalScope
的route
变量就是MaterialPageRoute
对象。
也就是说只要拿到了_ModalScopeStatus
对象,就能经过它的route
方法获取MaterialPageRoute
对象,并经过其中的setting
变量获取RouteSetting
对象,而RouteSetting
对象的arguments
变量就是路由传递进来的参数了。
看到这里,咱们就不难理解为何在SearchPage
中使用下面这样的方法能获取路由传递进来的数据的缘由了。
String arg = ModalRoute.of(context).settings.arguments;
复制代码
由于经过ModalRoute.of(context)
,SearchPage
自下而上地寻找_ModalScopeStatus
对象,_ModalScopeStatus
对象找到后经过route
变量找到Route
对象,也就是MaterialPageRoute
对象,而后经过访问MaterialPageRoute
对象的setting.arguments
就能拿到Navigator
传递进来的参数了,这就是Navigator
数据传递的原理。
从上述分析中,咱们知道了Flutter中一个重要的功能型组件,它就是InheritedWidget,经过它咱们能够实现跨组件的数据共享。有关更多InheritedWidget的使用方法,能够参考 数据共享(InheritedWidget)