To Be An Artist Engineer.

0%

FlutterBoost路由简析

一、单Navigator模式

首先看下NavigatorState build方法的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@override
Widget build(BuildContext context) {
assert(!_debugLocked);
assert(_history.isNotEmpty);
// Hides the HeroControllerScope for the widget subtree so that the other
// nested navigator underneath will not pick up the hero controller above
// this level.
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>[],
),
),
),
),
),
);
}
}

可以发现是靠Overlay来实现路由管理功能的。

以Navigator.push为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@optionalTypeArgs
Future<T?> push<T extends Object?>(Route<T> route) {
_pushEntry(_RouteEntry(route, initialState: _RouteLifecycle.push));
return route.popped;
}

void _pushEntry(_RouteEntry entry) {
....
_history.add(entry);
_flushHistoryUpdates();
....
}

void _flushHistroyUpdates({bool rearrangeOverlay = true}){
entry.handlePush(
navigator: this,
previous: previous?.route,
previousPresent: _getRouteBefore(index - 1, _RouteEntry.isPresentPredicate)?.route,
isNewFirst: next == null,
);
if (rearrangeOverlay) {
overlay?.rearrange(_allRouteOverlayEntries);
}
}

void handlePush({ required NavigatorState navigator, required bool isNewFirst, required Route<dynamic>? previous, required Route<dynamic>? previousPresent }) {
....
route.install();
....
}

具体调用过程如下:

Navigator创建_RouteEntry并添加到_history列表里

遍历_history列表取出新添加的Entry并调用Route的install方法,创建OverlayEntry实例

最后,调用OverlayStaterearrange方法把新创建的OverlayEntry插入到Overlay里,完成路由页面的添加

Navigator其他api方法都是类似的原理,本质上都是通过管理_RouteEntry列表来间接实现对Overlay的管理

二、FlutterBoost的多Navigator模式

FlutterBoost里的Widget结构为:

BoostContainerManager

​ |- Overlay

​ |- BoostNavigator

​ |- BoostNavigator

BoostNavigator是自定义的Navigator实例,每个原生容器(Activity、ViewController)都对应一个BoostNavigator实例并且被作为一个OverlayEntry插入到BoostContainerManager下的Overlay里。

下面是一些具体细节的剖析:

  1. 传递给MaterialApp的routes,onGenerateRoute参数怎么在这个模式下起作用呢?

    在MaterialApp里有一个builder参数,它是一个函数,其中第一个Widget child参数就是Navigator,它的onGenerateRoute参数是通过MaterialApp里的routes,home和onGenerateRoute等参数初始化的。

    BoostContainerManager持有了这个Navigator实例,但是没有把它插入Widget Tree,而是用来生产BoostNavigator,从而保证诸如Navigator.pushNamed等官方api能正常使用。

  2. FlutterBoost是怎么管理PopupRoute的?

    在showDialog等方法中默认会有一个useRootNavigator的参数,顾名思义它会优先使用Widget树中最顶层的Navigator。而在FlutterBoost中RootNavigator被人为移除了,普通页面拿到的Navigator其实就是BoostNavigator实例,这样在单个原生容器的上下文中PageRoute和PopupRoute的管理就统一了起来。如果不这样做会有什么后果呢:

    showDialog:使用RootNavigator

    popDialog:必须要使用Navigator.of(context,rootNavigator:true),如果使用Navigator.of(context)那拿到的是BoostNavigator实例,是无法移除对话框的。