Flutter Navigator 2.0原理详解
Posted 一叶飘舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter Navigator 2.0原理详解相关的知识,希望对你有一定的参考价值。
Navigator 2.0作为新一代的路由提供了申明式的API,更加符合Flutter的风格。Navigator 2.0向前兼容,新增了一些新的API,使用的方式和Navigator 1.0相比有较大的差别。
本文将详细解析Navigator 2.0的底层逻辑,让大家对它有一个深入的了解,这样在使用上会更加的得心应手。
Navigator 2.0 诞生的背景
Flutter官方团队改造路由主要有几点原因:
- Navigator 1.0 只提供了一些
push()
,pushNamed()
和pop()
等简单的API。实现压入或者弹出多个页面很困难,更难实现对栈内中间页面的移除,交换等操作; - Flutter随着2.0的到来实现了全平台的支持,这样也就新出现一些使用场景,譬如网页修改URL地址等,这些就需要新的API来支持;
- Navigator 2.0满足了嵌套路由的需求场景,这样开发者在使用时就更加的灵活和方便;
- Navigator 2.0提供的是申明式的API,解决了以前路由命令式编程的方式,让编程的风格统一。
Navigator 2.0的API虽然比较的多,但是逻辑还是比较清晰的,我们来一个个的进行介绍。
Page
Page代表页面不可变的的配置信息,代表一个页面,类似于Widget配置信息转换成Element, Page配置的信息会转换成Route。
abstract class Page<T> extends RouteSettings
const Page(
this.key,
String? name,
Object? arguments,
this.restorationId,
) : super(name: name, arguments: arguments);
bool canUpdate(Page<dynamic> other)
return other.runtimeType == runtimeType &&
other.key == key;
@factory
Route<T> createRoute(BuildContext context);
复制代码
createRoute
就是转换成Route的方法;canUpdate
的实现方式和Widget的一样,也是用于diff
算法。
RouteSettings
Page的父类RouteSettings仅仅用来保存name
和arguments
这两个值。
const RouteSettings(
this.name,
this.arguments,
);
复制代码
Route
Route代表一个页面,是Navigator栈中真正管理的内容。
abstract class Route<T>
// 1
RouteSettings get settings => _settings;
NavigatorState? get navigator => _navigator;
// 2
List<OverlayEntry> get overlayEntries => const <OverlayEntry>[];
// 3
void install()
TickerFuture didPush()
...
复制代码
- Route持有了配置对象
page
和管理它的navigator
对象;- Route还持有一个OverlayEntry数组,OverlayEntry放置在类似于Stack的Overlay上,我们写的页面就是放置在一个OverlayEntry上的;
- Route还定义了一些协议方法需要子类覆写,这些方法主要是route的状态变化后收到的回调函数,这些函数调用主要来自于
_RouteEntry
。
方法 | 调用时机 |
---|---|
install | 被插入navigator |
didPush | 动画进入显示 |
didAdd | 直接显示 |
didReplace | 替换旧的route |
didPop | 请求pop页面 |
didComplete | pop完成后 |
didPopNext | 当前route后面的route被pop |
didChangeNext | 当前route后面的route被替换 |
didChangePrevious | 当前route前面的route被替换 |
changedInternalState | 当前route的state变化后 |
changedExternalState | 当前route的navigator 变化后 |
MaterialPage
和 _PageBasedMaterialPageRoute
我们可以直接使用系统给我们提供的Page类,也可以自定义继承自Page的类。我们来看看官方给我们提供的MaterialPage的逻辑。
MaterialPage的Route是_PageBasedMaterialPageRoute
类,它的继承逻辑是:_PageBasedMaterialPageRoute
-> PageRoute
-> ModalRoute
-> TransitionRoute
-> OverlayRoute
+ LocalHistoryRoute
-> Route
。
LocalHistoryRoute
LocalHistoryRoute可以给Route添加一些LocalHistoryEntry。当LocalHistoryEntry不为空时,didPop
方法调用的时候会移除最后一个LocalHistoryEntry,否则Route就要被pop
了。
OverlayRoute
OverlayRoute主要是持有Route对应的OverlayEntry
数组,这个数组是子类在被插入navigator
的时候对其进行赋值的。
abstract class OverlayRoute<T> extends Route<T>
@factory
Iterable<OverlayEntry> createOverlayEntries();
List<OverlayEntry> get overlayEntries => _overlayEntries;
void install()
_overlayEntries.addAll(createOverlayEntries());
super.install();
复制代码
TransitionRoute
TransitionRoute是主要是负责动画部分。
abstract class TransitionRoute<T> extends OverlayRoute<T>
Animation<double>? get animation => _animation;
Animation<double>? get secondaryAnimation => _secondaryAnimation;
void install()
_animation = createAnimation()
..addStatusListener(_handleStatusChanged);
super.install();
TickerFuture didPush()
super.didPush();
return _controller!.forward();
void didAdd()
super.didAdd();
_controller!.value = _controller!.upperBound;
bool didPop(T? result)
_controller!.reverse();
return super.didPop(result);
void didPopNext(Route<dynamic> nextRoute)
_updateSecondaryAnimation(nextRoute);
super.didPopNext(nextRoute);
void didChangeNext(Route<dynamic>? nextRoute)
_updateSecondaryAnimation(nextRoute);
super.didChangeNext(nextRoute);
复制代码
- TransitionRoute有
_animation
和secondaryAnimation
两个动画,前者负责当前Route的push和pop动画,后者负责下一个Route进行push和pop时本身这个Route的动画。_animation
是install
就生成了,secondaryAnimation
可以大部分情况下就是下一个Route的_animation
, 所以didPopNext
和didChangeNext
时需要更新secondaryAnimation
。- 如果不需要动画时调用的是
didAdd
方法,Route是被动调用的这个方法,其实是_RouteEntry
根据(Navigator确定的)状态判断调用的这个方法。
ModalRoute
ModalRoute主要的作用是阻止除最上层的Route之外的Route进行用户交互,其中的知识点也是非常丰富的。
abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T>
Iterable<OverlayEntry> createOverlayEntries() sync*
yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier);
yield _modalScope = OverlayEntry(builder: _buildModalScope, maintainState: maintainState);
复制代码
- ModalRoute生成了两个非常重要的
OverlayEntry
---_modalBarrier
和_modalScope
。_modalBarrier
实现了阻止用户对最上层Route之外的Route进行用户交互的功能;_modalScope
会持有router自身,_modalScope
在构建的时候就会调用router的buildTransitions
和buildChild
方法,参数都包含router的animation
和secondaryAnimation
,也就是TransitionRoute中的两个动画属性;
Widget _buildModalScope(BuildContext context)
return _modalScopeCache ??= Semantics(
sortKey: const OrdinalSortKey(0.0),
child: _ModalScope<T>(
key: _scopeKey,
route: this,
// _ModalScope calls buildTransitions() and buildChild(), defined above
)
);
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation);
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
)
return child;
复制代码
我们接下来看看_ModalScope
的_ModalScopeState
的内容:
class _ModalScopeState<T> extends State<_ModalScope<T>>
late Listenable _listenable;
final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: '$_ModalScopeState Focus Scope');
void initState()
super.initState();
final List<Listenable> animations = <Listenable>[
if (widget.route.animation != null) widget.route.animation!,
if (widget.route.secondaryAnimation != null) widget.route.secondaryAnimation!,
];
_listenable = Listenable.merge(animations);
if (widget.route.isCurrent)
widget.route.navigator!.focusScopeNode.setFirstFocus(focusScopeNode);
复制代码
_listenable
是route的animation
和secondaryAnimation
的组合;focusScopeNode
是焦点,初始化的时候将navigator
的焦点设置为这个焦点,这样就实现了最上层的Route才获取到焦点,屏蔽对其他Route的焦点获取;
Widget build(BuildContext context)
// 1 RestorationScope
return AnimatedBuilder(
animation: widget.route.restorationScopeId,
builder: (BuildContext context, Widget? child)
return RestorationScope(
restorationId: widget.route.restorationScopeId.value,
child: child!,
);
,
// 2 _ModalScopeStatus
child: _ModalScopeStatus(
route: widget.route,
isCurrent: widget.route.isCurrent, // _routeSetState is called if this updates
canPop: widget.route.canPop, // _routeSetState is called if this updates
child: Offstage(
offstage: widget.route.offstage, // _routeSetState is called if this updates
child: PageStorage(
bucket: widget.route._storageBucket, // immutable
child: Builder(
builder: (BuildContext context)
return Actions(
actions: <Type, Action<Intent>>
DismissIntent: _DismissModalAction(context),
,
child: PrimaryScrollController(
controller: primaryScrollController,
child: FocusScope(
node: focusScopeNode, // immutable
// 3 RepaintBoundary
child: RepaintBoundary(
// 4. AnimatedBuilder
child: AnimatedBuilder(
animation: _listenable, // immutable
builder: (BuildContext context, Widget? child)
// 5. buildTransitions
return widget.route.buildTransitions(
context,
widget.route.animation!,
widget.route.secondaryAnimation!,
AnimatedBuilder(
animation: widget.route.navigator?.userGestureInProgressNotifier ?? ValueNotifier<bool>(false),
builder: (BuildContext context, Widget? child)
final bool ignoreEvents = _shouldIgnoreFocusRequest;
focusScopeNode.canRequestFocus = !ignoreEvents;
return IgnorePointer(
ignoring: ignoreEvents,
child: child,
);
,
child: child,
),
);
,
child: _page ??= RepaintBoundary(
key: widget.route._subtreeKey, // immutable
child: Builder(
builder: (BuildContext context)
return widget.route.buildPage(
context,
widget.route.animation!,
widget.route.secondaryAnimation!,
);
,
),
),
),
),
),
),
);
,
),
),
),
),
);
复制代码
_ModalScopeState
的build
方法是设计非常精妙的一个方法:
RestorationScope
负责Route用于恢复数据的作用;_ModalScopeStatus
是InheritedWidget,它保持对Route的引用,所以我们在调用ModalRoute.of(contex)
获取页面传参时,就是通过获取的这个_ModalScopeStatus
,再找到对应的传参。- 中间放置了一个
RepaintBoundary
可以限制重绘的区域,这样可以提高进行动画时绘制的效率;- 最底层的
AnimatedBuilder
这个Widget是核心,这个AnimatedBuilder
的child
是由route.buildPage()
这个方法创建的,其实就是我们Page的child,即开发者写的页面内容;这个AnimatedBuilder
的builder
方法中调用了route.buildTransitions()
,它驱动动画是_listenable
,也就是说animation
和secondaryAnimation
都能驱动它的动画过程。这其实很好理解:当前Route的pop和push和下个Route的pop和push都会触发动画的产生。
PageRoute
PageRoute
主要就是让最上层下面的Route不可见,点击_modalBarrier
不让当前Route从Navigator栈中弹出。
abstract class PageRoute<T> extends ModalRoute<T>
@override
bool get opaque => true;
@override
bool get barrierDismissible => false;
复制代码
_PageBasedMaterialPageRoute
_PageBasedMaterialPageRoute
的作用是覆写了buildPage
方法, 返回的是开发者写的界面;
class _PageBasedMaterialPageRoute<T> extends PageRoute<T> with MaterialRouteTransitionMixin<T>
Widget buildContent(BuildContext context)
return _page.child;
复制代码
官方为我们提供了默认的pop和push动画,它们就在混入的MaterialRouteTransitionMixin
中实现的。MaterialRouteTransitionMixin
会根据不同的平台有不同的实现,ios是左右的动画,android是上下的动画,web也是左右动画。
我们以iOS为例,其最后使用的是CupertinoPageTransition
这个类的方法:
SlideTransition(
position: _secondaryPositionAnimation,
textDirection: textDirection,
transformHitTests: false,
child: SlideTransition(
position: _primaryPositionAnimation,
textDirection: textDirection,
child: DecoratedBoxTransition(
decoration: _primaryShadowAnimation,
child: child,
),
)
复制代码
看到SlideTransition嵌套到一个child上是不是很疑惑?两个动画用在一个Widget上?
先解释下其他参数:
textDirection
决定了滑动的方法,因为有些语言是从右到左排序的;transformHitTests
设置为flase,点击事件的响应位置不受动画的影响;_primaryShadowAnimation
是设置了一个动画中的阴影。
_secondaryPositionAnimation
是从Offset.zero
到Offset(-1.0/3.0, 0.0)
,正常情况下就是从右往左移动1/3的屏幕宽度。
final Animatable<Offset> _kMiddleLeftTween = Tween<Offset>(
begin: Offset.zero,
end: const Offset(-1.0/3.0, 0.0),
);
复制代码
_primaryPositionAnimation
是从Offset(1.0, 0.0)
到Offset.zero
,正常情况下就是从不可见的屏幕右边移动到屏幕最左边,然后占据整个屏幕宽度。
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
);
复制代码
我们接下来解释下pop一个Route时候的动画逻辑, Animation:0->1
- 新加的Route是被
_primaryPositionAnimation
直接驱动的,也就是执行了从右到左的_kRightMiddleTween
动画; _secondaryPositionAnimation
只是被修改了值,我们前面TransitionRoute的介绍中提到过,新加入Route的animation
赋值给了前一个Route的secondaryAnimation
属性。_ModalScopeState
中介绍过secondaryAnimation
也能驱动Route的动画,也就是说前一个Route也能产生一个_kMiddleLeftTween
动画;
概括:
新加的Route通过animation
驱动从屏幕右边移动到左边的动画,animation
赋值给了前一个Route的secondaryAnimation
驱动前一个Route向左移动1/3个屏幕位置。
push的逻辑类似,只是一个反向的动画reverse
。前一个Route在secondaryAnimation
的驱动下右移了1/3屏幕宽度,当前的Route在animation
驱动下移出屏幕。
我们可以点击Flutter DevTools的Slow Animations看看动画的慢放过程:
阶段总结
_RouteEntry
Navigator不是直接操作的Route,而是Route的封装类_RouteEntry
。
_RouteEntry(
this.route,
required _RouteLifecycle initialState,
this.restorationInformation,
)
复制代码
_RouteEntry除了持有route
外,还持有一个_RouteLifecycle
,即路由状态。
函数则主要是修改_RouteLifecycle
状态的函数,譬如markForPush
,markForAdd
,markForPop
,markForRemove
,markForComplete
等。此外还有_RouteLifecycle
被标记后对Route进行操作函数,譬如handlePush
,handleAdd
,handlePop
,remove
等。
Navigator
Navigator(
Key? key,
this.pages = const <Page<dynamic>>[],
// ...
)
复制代码
Navigator
的构造方法中有一个关键的属性pages
,Navigator会将传入的pages
会转换成Routes对应的_RouteEntry
数组。
实现申明式编程的逻辑就是修改这个
pages
中的内容,Navigator会自动实现对应的跳转,返回,替换等操作。Navigator.push
,Navigator.pop
等以前使用的方法就被将不是开发者需要考虑的使用方法了。
我们接下来分析NavigatorState的重要代码。
class NavigatorState extends State<Navigator> with TickerProviderStateMixin, RestorationMixin
List<_RouteEntry> _history = <_RouteEntry>[];
late GlobalKey<OverlayState> _overlayKey;
OverlayState? get overlay => _overlayKey.currentState;
final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'Navigator Scope');
复制代码
_history
就是pages
中每个Page通过createRoute
生成的_RouteEntry
数组;- OverlayState
overlay
代表的就是OverLay,它负责摆放每个Route的overlayEntries
数组;OverLay就相当于一个Stack,专门用于放置OverlayEntry。
NavigatorState的核心方法是didUpdateWidget
方法, 其调用了一个_updatePages()
方法:
void didUpdateWidget(Navigator oldWidget)
_updatePages();
复制代码
_updatePages
方法的主要作用是对pages
进行diff比对,更新_history
数组中每个_routeEntry
的_RouteLifecycle
, 最后调用_flushHistoryUpdates()
方法。
_routeEntry
比对的方法和MultiChildRenderObjectElement
的比对方法是一样的,先前往后比对能复用的元素,然后从后往前比对能复用的元素,然后对剩下的元素进行复用或者新建,不能复用的元素进行销毁。
void _flushHistoryUpdates(bool rearrangeOverlay = true)
final List<_RouteEntry> toBeDisposed = <_RouteEntry>[];
while (index >= 0)
switch (entry!.currentState)
case _RouteLifecycle.push:
case _RouteLifecycle.pushReplace:
case _RouteLifecycle.replace:
entry.handlePush(
navigator: this,
previous: previous?.route,
previousPresent: _getRouteBefore(index - 1, _RouteEntry.isPresentPredicate)?.route,
isNewFirst: next == null,
);
if (entry.currentState == _RouteLifecycle.idle)
continue;
break;
// ...
index -= 1;
next = entry;
entry = previous;
previous = index > 0 ? _history[index - 1] : null;
_flushObserverNotifications();
_flushRouteAnnouncement();
for (final _RouteEntry entry in toBeDisposed)
for (final OverlayEntry overlayEntry in entry.route.overlayEntries)
overlayEntry.remove();
entry.dispose();
if (rearrangeOverlay)
overlay?.rearrange(_allRouteOverlayEntries);
复制代码
- 根据每个
_RouteEntry
的_RouteLifecycle
调用对应的方法,例如如果Route被标记为_RouteLifecycle.push
,则调用handlePush
方法,这样此Route就会调用install
方法插入Navigator的树中,然后进行动画;_flushObserverNotifications
是对每个_NavigatorObservation
监听者进行通知;_flushRouteAnnouncement
主要是对每个Route的前后关系进行梳理更新,secondaryAnimation
的更新就是这个时候进行的;- 将不需要的
_RouteEntry
的overlayEntries
从Overlay上移除,因为不需要再显示了;- 然后将所有的
_RouteEntry
的overlayEntries
更新到Overlay上,代码在build
方法中可以看到添加的逻辑如下。
Widget build(BuildContext context)
return HeroControllerScope.none(
child: 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: UnmanagedRestorationScope(
bucket: bucket,
child: Overlay(
key: _overlayKey,
initialEntries: overlay == null ? _allRouteOverlayEntries.toList(growable: false) : const <OverlayEntry>[],
),
),
),
),
),
);
复制代码
顺便提一下HeroControllerScope是负责进行Hero动画的的Widget,类似于Android中的共享元素动画。
阶段总结
到目前为止,我们通过切换Navigator的page
就能够实现路由切换了,是不是文章就结束了?没有,因为Navigator 2.0是为Flutter 2.0 的全平台而生的,目前还没有解决一些问题,例如编辑浏览器网址,网页返回,安卓物理键返回等功能。
Router
Router(
Key? key,
this.routeInformationProvider,
this.routeInformationParser,
required this.routerDelegate,
this.backButtonDispatcher,
)
final RouteInformationProvider? routeInformationProvider;
final RouteInformationParser<T>? routeInformationParser;
final RouterDelegate<T> routerDelegate;
final BackButtonDispatcher? backButtonDispatcher;
复制代码
我们看到Router有四个属性,RouteInformationProvider
路由信息提供者,RouteInformationParser
路由信息解析者,RouterDelegate
路由信息的处理代理,BackButtonDispatcher
返回处理的分发者。他们四个协同作用,共同实现路由的功能。
RouteInformation
上面说的到路由信息就是指RouteInformation
,包括路由的路径location
和路由对应的状态state
。这里所指的状态就是数据。
class RouteInformation
final String? location;
final Object? state;
复制代码
RouteInformationProvider
RouteInformationProvider
只有一个抽象方法routerReportsNewRouteInformation
,这个方法的作用是根据RouteInformation
进行一些额外的操作。
abstract class RouteInformationProvider extends ValueListenable<RouteInformation?>
void routerReportsNewRouteInformation(RouteInformation routeInformation)
复制代码
系统默认使用的是PlatformRouteInformationProvider
, 它的routerReportsNewRouteInformation
方法中回调了系统路由的更新,例如浏览器就会在History栈中新增一条历史访问记录:
class PlatformRouteInformationProvider extends RouteInformationProvider with WidgetsBindingObserver, ChangeNotifier
void routerReportsNewRouteInformation(RouteInformation routeInformation)
SystemNavigator.routeInformationUpdated(
location: routeInformation.location!,
state: routeInformation.state,
);
_value = routeInformation;
复制代码
RouteInformationParser
这个类的作用是对T页面模型和RouteInformation路由信息进行相互转换:
abstract class RouteInformationParser<T>
Future<T> parseRouteInformation(RouteInformation routeInformation);
RouteInformation? restoreRouteInformation(T configuration) => null;
复制代码
parseRouteInformation
这个方法主要是解析初始路由的时候会使用到,例如 根据RouteInformation(location: "/")
显示启动页面;
restoreRouteInformation
这个方法就是根据T页面模型生成对应的RouteInformation
。
RouterDelegate
RouterDelegate
顾名思义就是代替Router
工作的类,它包括根据T页面模型添加一个页面,pop一个页面,提供构建的内容等。
abstract class RouterDelegate<T> extends Listenable
Future<void> setInitialRoutePath(T configuration)
return setNewRoutePath(configuration);
Future<void> setNewRoutePath(T configuration);
Future<bool> popRoute();
T? get currentConfiguration => null;
Widget build(BuildContext context);
复制代码
可以混入
PopNavigatorRouterDelegateMixin
的popRoute
方法,就不用自己去实现了。
我们从源码角度看看RouteInformationProvider
,RouteInformationParser
和RouterDelegate
他们三者在初始化路由是如何实现的:
class _RouterState<T> extends State<Router<T>>
void initState()
super.initState();
if (widget.routeInformationProvider != null)
_processInitialRoute();
void _processInitialRoute()
_currentRouteInformationParserTransaction = Object();
_currentRouterDelegateTransaction = Object();
_lastSeenLocation = widget.routeInformationProvider!.value!.location;
widget.routeInformationParser!
.parseRouteInformation(widget.routeInformationProvider!.value!)
.then<T>(_verifyRouteInformationParserStillCurrent(_currentRouteInformationParserTransaction, widget))
.then<void>(widget.routerDelegate.setInitialRoutePath)
.then<void>(_verifyRouterDelegatePushStillCurrent(_currentRouterDelegateTransaction, widget))
.then<void>(_rebuild);
复制代码
在_processInitialRoute
方法中我们看到了,routeInformationParser
解析routeInformationProvider
的value
,然后routerDelegate
根据这个解析的结果去调用setNewRoutePath
设置路由。
routeInformationProvider
-> routeInformationParser
-> routerDelegate
-> (setNewRoutePath
)
RouterDelegate
的覆写案例:
class MyRouterDelegate extends RouterDelegate<PageConfiguration>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<PageConfiguration>
final List<Page> _pages = [];
final AppState appState;
final GlobalKey<NavigatorState> navigatorKey;
MyRouterDelegate(this.appState) : navigatorKey = GlobalKey()
appState.addListener(()
notifyListeners();
);
List<MaterialPage> get pages => List.unmodifiable(_pages);
Future<bool> popRoute()
_removePage(_pages.last);
return Future.value(false);
Future<void> setNewRoutePath(PageConfiguration configuration)
if (shouldAddPage)
_pages.clear();
addPage(configuration);
return SynchronousFuture(null);
Widget build(BuildContext context)
return Navigator(
key: navigatorKey,
onPopPage: _onPopPage,
pages: buildPages(),
);
复制代码
MyRouterDelegate
有_pages
属性,这个属性作为Navigator
的pages
;appState
是状态管理的数据,用这个数据去驱动MyRouterDelegate
的观察者也就是Router
即去重构,这样Navigator
也就会重构了。popRoute
将_pages
的最后一个页面删掉,通知Router
即去重构,更新Navigator
;setNewRoutePath
给_pages
添加对应的Page,通知Router
即去重构Navigator
。
BackButtonDispatcher
BackButtonDispatcher
主要就是解决安卓,网页等物理返回的事件。它有两个子类RootBackButtonDispatcher
和ChildBackButtonDispatcher
可以解决Router的嵌套问题。
BackButtonDispatcher
的返回处理可以直接交给RouterDelegate
去处理,例如下面的逻辑:
class MyBackButtonDispatcher extends RootBackButtonDispatcher
final MyRouterDelegate _routerDelegate;
MyBackButtonDispatcher(this._routerDelegate)
: super();
// 3
@override
Future<bool> didPopRoute()
return _routerDelegate.popRoute();
复制代码
最后总结
总结
Navigator 2.0的功能更加强大了,使用方式也变得更加Flutter了。但是变得更复杂了,这样对学习和使用成本造成了很大的困扰,这方面也是很多人认为Navigator 2.0是一个失败的改造的原因。
本文主要从源码角度分析了Navigator 2.0的实现逻辑,原理清楚后写代码应该还是很简单的。
如果你需要Demo,可以参阅下面两篇文章的代码,特别是第一篇文章的代码非常具有参考价值:
Flutter Navigator 2.0 and Deep Links
Learning Flutter’s new navigation and routing system
以上是关于Flutter Navigator 2.0原理详解的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Flutter Navigator 2.0 示例中没有返回按钮?