FlutteNavigator入坑手册
Navigator简介
导航,负责管理页面之间的切换,通过内置的栈来保存各个页面的信息,App启动时,MaterialApp会自动创建一个Navigator,它的根视图就是我们配置的MaterialApp
对应的Home
页面。
Navigator主要功能介绍
Navigator流程简介
多个Navigator管理方案
NavigatorState
Navigator是有状态的,NavigatorState
对应导航的状态,是执行页面出战入栈的具体实例,每个Navigator
内部创建一个NavigatorState
时候,会将当前的state保存在GlobalKey
中,它通过Element缓存了当前的state.
RouteFactory
它是一个工厂函数,根据指定的路由配置信息生成一个PageRoute
(路由),使用Navigator
的Context
来创建Widget
RouteBuilder
它和RouteFactory
类似,多了一个BuildContext
参数,可以通过指定的Context来初始化PageRoute
RouteListFactory
它也是一个构造PageRoute
的方法,只不过返回的是一个数组,NavigatorState
在初始化时候在onGenerateInitialRoutes
方法创建。
RoutePredicate
它是一个检查路由配置信息的函数,用于确认某两个路由是否为相同
WillPopCallback
它是对系统默认返回按钮事件拦截的回掉,用于对当前NavigatorState
状态确认,是否在栈底,再根据业务逻辑选择返回或是不返回
PopPageCallback
用于删除Pop的页面在Navigator
内的配置信息(page).
RoutePopDisposition
一个枚举值,用于指定当前需要pop
的路由当前在NavigatorState
中的状态,便于对路由进行行为更加精确管理。 包含3个值pop
,doNotPop
,bubble
(pop事件移交给系统导航,通常关闭app),因为pop时需要做前置检查,所以每次pop时都会检查当前Route.willPop
的拦截方法是否做了其他的处理。
Route
在Navigator
和页面切换之间定义了一层抽象的接口(入栈,出栈),它包含了页面的构建信息Builder构造方法,同时也提供了一个List<OverlayEntry>
,页面切换过程中的可视化界面是由很多个OverlayEntry
实现的,Navigator
内部由一个容器Overlay
,在切换页面时不同的时刻插入对应状态的OverlayEntry
,最终呈现出切换页面的过程,它内部还持有了当前的NavigatorState
.
RouteSettings
提供Route
的设置信息,如当前的路有的名字,以及传递给页面的参数,名字不可以参略,但一般建议加上,这对于我们定义问题,以及路由检查非常重要。
Functions
-
RouteFactory
Route
创建的工厂函数
-
RouteBuilder
Route
创建工厂函数,带BuildContext
-
RouteListFactory
- 批量创建
Route
- 批量创建
-
RoutePredicate
-
WillPopCallback
-
PopPageCallback
-
RoutePopDisposition
index value des pop 0 允许返回 doNotPop 1 不允许返回 bubble 2 返回事件交给系统返回按钮 -
Route
-
Route: 构造方法,包含一个
RouteSettings
,配置路有的名字和传递参数 -
navigator: 管理导航的焦点事件
-
_updateSettings: 更新路有的配置信息,会触发
OverlayEntries
的重新创建 -
overlayEntries: 路由的遮盖物对象,在切换路由时提供可视化的界面给导航
-
install: 装配初始化该路有的配置信息,当
Route
被插入到Navigator
中的时候调用,创建具体的overlayEntries
(遮盖物)对象,它提供给Navigator
的Overlay
(rootWidget)构建导航的当前视图。 -
didPush:
Route
加入到Navigator
之后调用,返回一个TickerFuture
对象,当ticker完成才获得导航的焦点事件,这个方法使用于带有转场的push回调,如果路有是直接出现在屏幕不带任何转场效过,则将会调用didAdd
方法来替代它。 -
didReplace: 当路有在
Navigator
中替换后调用 -
willPop: 内部判定当前路由执行pop方法时的pop状态,安全检测
-
willHandlePopInternally: 是否在内部自动处理pop
-
currentResult: 一个默认的缺省值,用于存储当前路由pop时的返回值(只有当poped没有返回值时才会用它)
-
popped: 当前路有从
Navigator
中移除时的future
,可以通过它来监听返回的数据 -
_popCompleter: 内部私有的变量,用于记录和管理pop的future.
-
didPop: 用于向当前
Route
提交一个pop
请求,如果返回为true,这个Route
将会从导航中移除。 -
didComplete: 由
didPop
请求触发,这个方法在pop动画开始之前结束 -
didPopNext: 拦截已经从
Navigator
移除的Route
,当前的Route
已经成功添加 -
didChangeNext: 新的
Route
已经添加到Navigator
的回调 -
didChangePrevious: didPush/didReplace执行成功之后
-
changedInternalState: 当route内部状态发生改变时调用,
willHandlePopInternally
,didPop
,Offstage
触发 -
changedExternalState:
NavigatorState
执行rebuild
时触发 -
dispose: 释放navigator,当
Route
从NavigatorState
中移除之后把Route所持有的navigator
至为null
. -
isCurrent: 栈顶
-
isFirst: 栈底
-
isActive: 当前可见的route,通常在栈顶,如果有透明的
route
,它之上,它也会保持active
状态,但是不会渲染,由ModalRoute.maintainState
保持它的状态
-
-
RouteSettings:
- 包含两个参数
name
,arguments
,分别对应route
的名字和传给路由的参数
- 包含两个参数
-
Page: 继承于
RouteSettings
,多了一个LocalKey
,用于唯一性判定,因为导航允许中会有多个相同路由名字相同- createRoute: 根据
BuildContext
创建一个路由,在NavigatorState
内部创建
- createRoute: 根据
-
CustomBuilderPage
- CustomBuilderPage: 继承于
Page
再次做了扩展,可以支持传入routeBuilder
来创建一个新的Route
- CustomBuilderPage: 继承于
-
NavigatorObserver
- 导航路有管理的观察者,同场可以用来做埋点,数据分析
- _navigator
- didPush
- didPop
- didRemove
- didReplace
- didStartUserGesture
- didStopUserGesture
-
RouteTransitionRecord
-
Route
转场记录,用于生成push
和pop
动画 -
route: 当前记录的
route
-
isEntering:
route
正在进入屏幕,如果标记为true
-
markForPush: 在
TransitionDelegate.resolve
的时候,如果isEntering
为true
,标记当前的Route
将会采用push
的方式进行转场. -
markForAdd: 标记这个
Route
不需要转场就能添加到Navigator
中,也是在TransitionDelegate.resolve
方法中执行 -
markForPop: 带动画的pop,与
markForPush
相反 -
markForComplete: 传入当前route的返回值,完成route并从当前导航移除,想比
markForPop
,它不带动 -
markForRemove: 同
markForComplete
不带返回结果
-
-
TransitionDelegate
- TransitionDelegate: 路由的转场代理,决定路由页面如何从进入屏幕和离开屏幕,以及他们对应的页面实体对象
page
如何从Navigator.pages
添加和移除。它只是一个抽象类,提供了一套api来决定路由的转场相关的命令,它的子类必须实现一个resolve
的方法来指转场动画或是不使用转场动画
- TransitionDelegate: 路由的转场代理,决定路由页面如何从进入屏幕和离开屏幕,以及他们对应的页面实体对象
-
DefaultTransitionDelegate
- 对
TransitionDelegate
的具体实现,通过resolve
方法对newPageRouteHistory
,locationToExitingPageRoute
,pageRouteToPagelessRoutes
重排,按顺序生成一个List<RouteTransitionRecord>
列表,并标记route
不同的转场方式.
- 对
-
Navigator
-
Navigator: 采用堆栈的规则管理导航容器窗口中的页面插入和移除操作,是由一个Overlay的Widget管理的,可以把它理解成为当前导航的可视化的窗口,基于Overaly添加和删除子页面的
Widget
.通过栈来管理Route
,在Route
插入和更新过程中会提供Overlay所需的OverlayEntries
. -
pages: 记录当前的导航所持有的页面
-
onPopPage:
PopPageCallback
,[Route.didPop]
的callback
的监听。 -
transitionDelegate: 控制路有的转场代理
-
initialRoute: 导航显示的第一个路由的名字
-
onGenerateRoute: 创建路有的工厂函数
-
onUnknownRoute: 创建未注册的路有
-
observers: 导航行为的观察者
-
defaultRouteName: 默认的初始化route名字,默认为
/
-
onGenerateInitialRoutes: 初始所有的路由
-
canPop
-
defaultGenerateInitialRoutes: 根据传入的
/routeName1/routeName2
以斜杠分割,初始化多个路由
-
-
_RouteLifecycle
- 路由的生命周期状态管理
- staging: 等待转场代理决定该如何去执行这个路由
- add: 添加路由,通常在
onGenerateInitialRoutes
和widget.pages
初始化时创建,主要是在NavigatorState
内部调用 - adding: 正在添加,准备好了转场
- push: 由push方法触发,功能和add方法相近,外部调用较多.
- pushReplace: 推送一个新的路有并替换当前路有
- pushing: 正在推送中,等待push完成
- replace: 直接替换当前路由
- idle: 导航处于等待状态
- pop: 退出一个页面,将会执行didPop
- remove: 移除一个页面,将会调用
didReplace/didRemove
... - popping: 正在退出,等待执行
finalizeRoute
去释放route - removing: 正在移除一个路由,等待路由动画完成,释放路由资源
- dispose: 立刻释放路由
-
disposed: 路由已经被完全释放
-
如下为
Navigator.dart
对路由生命周期的定义,大致分为10个阶段// The _RouteLifecycle state machine (only goes down): //
// [creation of a _RouteEntry] phase1
// |
// +
// |\
// | \
// | staging。 phase2
// | /
// |/
// +-+----------+--+-------+
// / | | |
// / | | |
// / | | |
// / | | |
// / | | |
// pushReplace push* add* replace* phase3
// \ | | |
// \ | | /
// +--pushing# adding / phase4(animate阶段,非必选)
// \ / /
// \ / /
// idle--+-----+ phase5
// / \
// / \
// pop* remove* phase6
// / \
// / removing# phase7(animate阶段,非必选)
// popping# |
// | |
// [finalizeRoute] | phase8
// \ |
// dispose* phase9
// |
// |
// disposed phase10
// |
// |
// [_RouteEntry garbage collected]
// (terminal state) -
_RouteEntryPredicate: 检查是否为同一个路由
-
_RouteEntry
- 继承于
RouteTransitionRecord
- route: Route
- currentState: _RouteLifecycle
- lastAnnouncedPreviousRoute: Route.didChangePrevious参数
- lastAnnouncedPoppedNextRoute: Route.didPopNext参数
- lastAnnouncedNextRoute: Route.didChangeNext参数
- hasPage:
route.settings is Page;
,route settings参数是否为Page类型 - canUpdateFrom: 当前route为page,且大于phase5(idle.index)
- handleAdd: 执行
route.install
创建overlayEntries
,并将状态设置为adding
- handlePush: 处理
push
和pushReplace
,生成routeFuture
并等待动画完成,如果是replace
方法这直接替换 - handleDidPopNext: pop下一个route
- handlePop: 处理pop事件,执行NavigatorObserver的
didPop
方法,将其状态设置为popping,pop动画是由delegate自动执行的 - handleRemoval: remove -> removing - > observer(didRemove)
- doingPop: pop标志
- didAdd: adding -> idle -> observer(didPush)
- pop: doingPop = true -> pop -> doingPop = false;
- remove: 设置当前祖航太为remove状态
- complete: 页面退出时返回结果回调用,完成后设置为
remove
状态 - finalize: 标记为销毁状态
- dispose: 将路由释放
- willBePresent:将要被展示 [add, idle]
- isPresent: 展示中[add, remove]
- suitableForAnnouncement: [push, removing]
- suitableForTransitionAnimation: [push, removing]
- shouldAnnounceChangeToNext
- isPresentPredicate:
entry.isPresent
- suitableForTransitionAnimationPredicate:
entry.suitableForTransitionAnimation
- willBePresentPredicate:
entry.willBePresent
- isRoutePredicate:
entry.route == route
- isEntering:
currentState == _RouteLifecycle.staging;
- markForPush:
currentState = _RouteLifecycle.push
- markForAdd:
currentState = _RouteLifecycle.add;
- markForPop:
pop<dynamic>(result);
- markForComplete:
complete<dynamic>(result);
- markForRemove:
currentState = _RouteLifecycle.remove;
- 继承于
-
NavigatorState
- 当前
Navigator
的State
- _overlayKey:
GlobalKey
保存窗口的Element - _history: 记录
_RouteEntry
,提供转场动画过程页面的配置信息(widget
) - focusScopeNode: 提供焦点范围
- _debugLocked: 避免重复的入栈出栈操作
- _debugCheckDuplicatedPageKeys
- initState
void initState() { ... //关联observers
for (final NavigatorObserver observer in widget.observers){ ...
if (widget.pages.isNotEmpty) { ... 添加初始化的`pages`
_history.addAll(
widget.pages.map((Page<dynamic> page) => _RouteEntry(
page.createRoute(context),
initialState: _RouteLifecycle.add, //初始化直接添加不需要动画
))
);
} else { ... 初始化default route
initialRoute = initialRoute ?? Navigator.defaultRouteName;
if (initialRoute != null) { ... 添加外部传入的`intialRoute`
_history.addAll(widget.onGenerateInitialRoutes(
_RouteEntry(route, initialState: _RouteLifecycle.add,
...
_flushHistoryUpdates(); //更新`history`中route处于非`disposed`和`staging`状态的route,更新`overlayEntries`,删除或移至`overlay`中更新界面
...- didUpdateWidget:
void didUpdateWidget(Navigator oldWidget) { ... if (oldWidget.observers != widget.observers) { ... //更新Observers
if (oldWidget.pages != widget.pages) { ... //更新pages
_updatePages();
for (final _RouteEntry entry in _history)
entry.route.changedExternalState(); //更新route外部状态
```
- dispose:
```dart
void dispose() { ...
for (final NavigatorObserver observer in widget.observers)
observer._navigator = null;
focusScopeNode.dispose();
for (final _RouteEntry entry in _history)
entry.dispose();
super.dispose(); ...
}- overlay:
_overlayKey.currentState;
- _allRouteOverlayEntries:
_history
->yield* entry.route.overlayEntries;
,获取所有的overlayEntries
- _lastAnnouncedRouteName: 用于传递给Native,是上次的
lastEntry?.route?.settings?.name;
routeName - _debugUpdatingPage: debug标志未
- _updatePages: 更新pages的层级,确保page和history的层级对应
- _flushHistoryUpdates: 检查
history
中routeEntry
的LifeCycle
并执行RouteEntry
相关的方法,执行具体的导航操作,它是具体的执行者.而Navigator
则倾向于管理RouteEntry
栈,和提供窗口容器。 - _flushRouteAnnouncement: 同步更新路由状态,用于传递给native
- _getRouteBefore:
previousRoute
- _getIndexBefore:
- _getRouteAfter:
next RouteEntry
- _routeNamed: 根据route name获取一个新的
Route
- _afterNavigation: 统计导航信息,debug用
- canPop: 检查栈中是否还存在页面,至少有2个,以及是否由系统处理返回键(android back 按钮)
- maybePop: 最后一个路由执行出栈操作
- popUntil: 依次执行pop操作,直到指定的route
- removeRoute: 立刻移除某个route
- removeRouteBelow: 替换某个路由下面的route
- finalizeRoute: 结束路由生命周期,如果正在pop则同步完成
- 当前
Route子类
Route (navigator.dart)
OverlayRoute (routes.dart)
TransitionRoute (routes.dart)
ModalRoute (routes.dart)
PageRoute (pages.dart)
CupertinoPageRoute (route.dart)
MaterialPageRoute (page.dart)
_SearchPageRoute (search.dart)
PageRouteBuilder (pages.dart)
PopupRoute (routes.dart)
_CupertinoModalPopupRoute (route.dart)
_ContextMenuRoute (context_menu.dart)
_ModalBottomSheetRoute (bottom_sheet.dart)
_DropdownRoute (dropdown.dart)
_PopupMenuRoute (popup_menu.dart)
_DialogRoute (routes.dart)
LocalHistoryRoute (routes.dart)
ModalRoute (routes.dart)
PageRoute (pages.dart)
CupertinoPageRoute (route.dart)
MaterialPageRoute (page.dart)
_SearchPageRoute (search.dart)
PageRouteBuilder (pages.dart)
PopupRoute (routes.dart)
_CupertinoModalPopupRoute (route.dart)
_ContextMenuRoute (context_menu.dart)
_ModalBottomSheetRoute (bottom_sheet.dart)
_DropdownRoute (dropdown.dart)
_PopupMenuRoute (popup_menu.dart)
_DialogRoute (routes.dart)
RoutePageBuilder
构建不带动画的路由页面
RouteTransitionsBuilder
构建带动画的路由页面
路由信息记录
RouteTransitionRecord (navigator.dart)
_RouteEntry (navigator.dart)
导航维持了一个history列表,负责管理所有的`_RouteEntry`
页面的实体相关的类
OverlayEntry (overlay.dart)
WidgetBuilder builder; //builder生成的才是我们创建的页面
opaque(bool value)
_OverlayEntryWidget //提供TickMode的设置
- entry._key
- entry
DiagnosticableTree (diagnostics.dart)
Widget (framework.dart)
RenderObjectWidget (framework.dart)
MultiChildRenderObjectWidget (framework.dart)
_Theatre (overlay.dart) //管理overlayEntries层级,不活跃的页面设置为offstage
SchedulerBinding
- schedulerPhase
- persistentCallbacks //移除OverlayEntry时的buildePhase
小结
- Flutter导航的管理目前比较侧重于单个导航管理,对于传统的app需要切换bottom tab的路由管理方式支持不是很友好,如果不同tab切换并跳转, 需要附加很多额外的逻辑.
- 路由的添加和移除对动画也比较局限,比如enable/disable animate功能,也需要自定义去实现
- 对于Route有较多数据依赖的场景, 路由的替换操作也是不够友好的