FlutteNavigator入坑手册

Navigator简介

导航,负责管理页面之间的切换,通过内置的栈来保存各个页面的信息,App启动时,MaterialApp会自动创建一个Navigator,它的根视图就是我们配置的MaterialApp对应的Home页面。

 

Navigator主要功能介绍

 

Navigator流程简介

 

 

多个Navigator管理方案

 

 

NavigatorState

Navigator是有状态的,NavigatorState对应导航的状态,是执行页面出战入栈的具体实例,每个Navigator内部创建一个NavigatorState时候,会将当前的state保存在GlobalKey中,它通过Element缓存了当前的state.

RouteFactory

它是一个工厂函数,根据指定的路由配置信息生成一个PageRoute(路由),使用NavigatorContext来创建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

  1. RouteFactory

    • Route创建的工厂函数
  2. RouteBuilder

    • Route创建工厂函数,带BuildContext
  3. RouteListFactory

    • 批量创建Route
  4. RoutePredicate

  5. WillPopCallback

  6. PopPageCallback

  7. RoutePopDisposition

    indexvaluedes
    pop 0 允许返回
    doNotPop 1 不允许返回
    bubble 2 返回事件交给系统返回按钮
  8. Route

    • Route: 构造方法,包含一个RouteSettings,配置路有的名字和传递参数

    • navigator: 管理导航的焦点事件

    • _updateSettings: 更新路有的配置信息,会触发OverlayEntries的重新创建

    • overlayEntries: 路由的遮盖物对象,在切换路由时提供可视化的界面给导航

    • install: 装配初始化该路有的配置信息,当Route被插入到Navigator中的时候调用,创建具体的overlayEntries(遮盖物)对象,它提供给NavigatorOverlay(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,当RouteNavigatorState中移除之后把Route所持有的navigator至为null.

    • isCurrent: 栈顶

    • isFirst: 栈底

    • isActive: 当前可见的route,通常在栈顶,如果有透明的route,它之上,它也会保持active状态,但是不会渲染,由ModalRoute.maintainState保持它的状态

  9. RouteSettings:

    • 包含两个参数name,arguments,分别对应route的名字和传给路由的参数
  10. Page: 继承于RouteSettings,多了一个LocalKey,用于唯一性判定,因为导航允许中会有多个相同路由名字相同

    • createRoute: 根据BuildContext创建一个路由,在NavigatorState内部创建
  11. CustomBuilderPage

    • CustomBuilderPage: 继承于Page再次做了扩展,可以支持传入routeBuilder来创建一个新的Route
  12. NavigatorObserver

    • 导航路有管理的观察者,同场可以用来做埋点,数据分析
    • _navigator
    • didPush
    • didPop
    • didRemove
    • didReplace
    • didStartUserGesture
    • didStopUserGesture
  13. RouteTransitionRecord

    • Route转场记录,用于生成pushpop动画

    • route: 当前记录的route

    • isEntering: route正在进入屏幕,如果标记为true

    • markForPush: 在TransitionDelegate.resolve的时候,如果isEnteringtrue,标记当前的Route将会采用push的方式进行转场.

    • markForAdd: 标记这个Route不需要转场就能添加到Navigator中,也是在TransitionDelegate.resolve方法中执行

    • markForPop: 带动画的pop,与markForPush相反

    • markForComplete: 传入当前route的返回值,完成route并从当前导航移除,想比markForPop,它不带动

    • markForRemove: 同markForComplete不带返回结果

  14. TransitionDelegate

    • TransitionDelegate: 路由的转场代理,决定路由页面如何从进入屏幕和离开屏幕,以及他们对应的页面实体对象page如何从Navigator.pages添加和移除。它只是一个抽象类,提供了一套api来决定路由的转场相关的命令,它的子类必须实现一个resolve的方法来指转场动画或是不使用转场动画
  15. DefaultTransitionDelegate

    • TransitionDelegate的具体实现,通过resolve方法对newPageRouteHistory,locationToExitingPageRoute,pageRouteToPagelessRoutes重排,按顺序生成一个List<RouteTransitionRecord>列表,并标记route不同的转场方式.
  16. 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以斜杠分割,初始化多个路由

  17. _RouteLifecycle

    • 路由的生命周期状态管理
    • staging: 等待转场代理决定该如何去执行这个路由
    • add: 添加路由,通常在onGenerateInitialRouteswidget.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: 检查是否为同一个路由

  18. _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: 处理pushpushReplace,生成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;
  19. NavigatorState

    • 当前NavigatorState
    • _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: 检查historyrouteEntryLifeCycle并执行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有较多数据依赖的场景, 路由的替换操作也是不够友好的

 

posted @ 2020-10-09 01:18  阿甘左  阅读(281)  评论(0编辑  收藏  举报