围观 MaterialApp, Navigator, Route, OverlayEntry, Overlay 合伙'直播' Widget
自己写的widget是怎么被展示的?Navigator是怎么切换页面的?Navigator、Route、OverlayEntry都有啥联系?
从一个简单例子通过源码看这些组件怎么合伙‘直播’Widget的。 基于flutter版本1.12.13+hotfix.8。
简单例子:
main.dart
runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: appName,
home: HomePage(),
navigatorObservers: [myNavigatorObserver],
showPerformanceOverlay: true
...
);
}
}
MaterialApp是啥?
material/app.dart
/// An application that uses material design.
class MaterialApp extends StatefulWidget {
@override
_MaterialAppState createState() => _MaterialAppState();
}
class _MaterialAppState extends State<MaterialApp> {
List<NavigatorObserver> _navigatorObservers; // 保存了外面传的NavigatorObserver
void initState() {
_navigatorObservers = List<NavigatorObserver>.from(widget.navigatorObservers)
..add(_heroController);
}
Widget build(BuildContext context) {
Widget result = WidgetsApp( // 借用了WidgetsApp并传递所有属性值
key: GlobalObjectKey(this),
navigatorKey: widget.navigatorKey,
navigatorObservers: _navigatorObservers,
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) { // 初始化了pageRouteBuilder
return MaterialPageRoute<T>(settings: settings, builder: builder);
},
home: widget.home,
routes: widget.routes,
... // 把MaterialApp的参数都传递了
);
return ScrollConfiguration( //
child: result,
);
}
}
就是说 MaterialApp 的内核是 WidgetsApp。
WidgetsApp是啥?
widgets/app.dart
/// wraps a number of widgets that are commonly required for an application.
class WidgetsApp extends StatefulWidget {
}
class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
GlobalKey<NavigatorState> _navigator;
initState() {
_navigator = widget.navigatorKey ?? GlobalObjectKey<NavigatorState>(this); // GlobalObjectKey
WidgetsBinding.instance.addObserver(this); // 注册了自己(WidgetsBindingObserver)监听如didPopRoute、didPushRoute、didChangeLocales
}
Widget build(BuildContext context) {
Widget navigator = Navigator( // 包装了Navigator !
key: _navigator, // key是_navigator
initialRoute: '/' // 就是home属性指代的widget
onGenerateRoute: _onGenerateRoute,
onUnknownRoute: _onUnknownRoute,
observers: widget.navigatorObservers,
);
Widget result = navigator;
// 下面根据widget的属性值装饰result,如builder、textStyle、performanceOverlay、onGenerateTitle等
Widget title = title = Title( //
title: widget.title,
color: widget.color,
child: result
);
return Shortcuts(
shortcuts: _keyMap,
child: Actions(
actions: _actionMap,
child: DefaultFocusTraversal(
policy: ReadingOrderTraversalPolicy(),
child: _MediaQueryFromWindow( // _MediaQueryFromWindowsState with了WidgetsBindingObserver,监听window的属性变化、系统字体大小变化做rebuild
child: Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
child: title,
)
)
)
)
);
}
Route<dynamic> _onGenerateRoute(RouteSettings settings) {
// 检查settings.name是'/'或者widget.routes里的,是就用widget.pageRouteBuilder(WidgetsApp默认是MaterialPageRoute)包装widget.home
// 这个widget生成route,否则用widget.onGenerateRoute生成route。
}
}
就是说 WidgetsApp 里包装了Navigator,传递了初始route名,还监听了window的属性变化、系统字体大小变化来rebuild自己。
Navigator是啥?
widgets/navigator.dart
/// A widget that manages a set of child widgets with a stack discipline.
/// [Navigator.of] operates on the nearest ancestor [Navigator] from the given
/// [BuildContext]. Be sure to provide a [BuildContext] below the intended
/// [Navigator].
class Navigator extends StatefulWidget {
static NavigatorState of( // 通过of()获取离context最近的Navigator对象的NavigatorState,要确保context是Navigator的子孙节点
BuildContext context, {
bool rootNavigator = false,
bool nullOk = false,
}) {
final NavigatorState navigator = rootNavigator
? context.findRootAncestorStateOfType<NavigatorState>()
: context.findAncestorStateOfType<NavigatorState>();
return navigator;
}
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) { // 各种push、pop api都是调用Navigator.of().XXX
return Navigator.of(context).push(route);
}
}
class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
final GlobalKey<OverlayState> _overlayKey = GlobalKey<OverlayState>();
final List<Route<dynamic>> _history = <Route<dynamic>>[];
final Set<Route<dynamic>> _poppedRoutes = <Route<dynamic>>{};
/// The [FocusScopeNode] for the [FocusScope] that encloses the routes.
final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'Navigator Scope');
final List<OverlayEntry> _initialOverlayEntries = <OverlayEntry>[];
OverlayState get overlay => _overlayKey.currentState;
initState() {
Route<Object> route;
route ??= _routeNamed<Object>(Navigator.defaultRouteName, arguments: null);
push(route); // push初始route , 会让route创建overlay entries,并保存在_history里,后面会再分析。
for (Route<dynamic> route in _history)
_initialOverlayEntries.addAll(route.overlayEntries); // 对于'/'route来说,_history也是有元素的。
}
// pushNamed、pushReplacement、pushReplacementNamed、popAndPushNamed、pushNamedAndRemoveUntil、pushAndRemoveUntil等逻辑类似push
Future<T> push<T extends Object>(Route<T> route) { // route可以自定义产生,借助MaterialPageRoute或PageRouteBuilder。
route._navigator = this; // 把NavigatorState绑定到route,在后面install(OverlayEntry)里用到。
route.install(_currentOverlayEntry); // 把route放在_currentOverlayEntry之上,_currentOverlayEntry是最新route里最后那个OverlayEntry。
_history.add(route); // 保存route !
route.didPush(); // 执行 focusScopeNode.requestFocus();
for (NavigatorObserver observer in widget.observers) { // 通过NavigatorObserver通知route pushed
observer.didPush(route, oldRoute);
}
}
bool pop<T extends Object>([ T result ]) {
final Route<dynamic> route = _history.last;
if (route.didPop(result ?? route.currentResult)) {
if (_history.length > 1) {
_history.removeLast(); // 去除顶端route
_history.last.didPopNext(route);
for (NavigatorObserver observer in widget.observers)
observer.didPop(route, _history.last); // 通过NavigatorObserver通知route poped
}
}
}
Widget build(BuildContext context) {
return Listener(
child: AbsorbPointer( // 接收点击事件,还有IgnorePointer
absorbing: false, // 会在navigation之后再设置为true (通过_overlayKey.currentContext?.findAncestorRenderObjectOfType()找到该absorber)
child: FocusScope(
node: focusScopeNode,
autofocus: true,
child: Overlay( // 包装了一个Overley
key: _overlayKey,
initialEntries: _initialOverlayEntries,
),
),
),
);
}
}
Overlay是啥?
/// A [Stack] of entries that can be managed independently.
class Overlay extends StatefulWidget {
final List<OverlayEntry> initialEntries;
OverlayState _overlay;
static OverlayState of(BuildContext context, { Widget debugRequiredFor }) {
final OverlayState result = context.findAncestorStateOfType<OverlayState>();
return result;
}
}
OverlayState extends State<Overlay> with TickerProviderStateMixin {
final List<OverlayEntry> _entries = <OverlayEntry>[]; // 所有OverlayEntry,通过insertAll、insert、_remove、rearrange操作增删改。
initState() {
insertAll(widget.initialEntries);
}
Widget build(BuildContext context) {
final List<Widget> onstageChildren = <Widget>[]; // 保存可在屏幕上可见的widget
final List<Widget> offstageChildren = <Widget>[]; // 保存在屏幕上不可见但保活的widget
bool onstage = true;
for (int i = _entries.length - 1; i >= 0; i -= 1) { // 反向遍历_entries(从新到旧)区分哪些entries是可见,如果哪个声明了不透明,底部的entries就都是不可见的了。
final OverlayEntry entry = _entries[i];
if (onstage) {
onstageChildren.add(_OverlayEntry(entry)); // OverlayEntry不是widget,把它包装成widget _OverlayEntry,会调用entry的builder创建widget。
if (entry.opaque)
onstage = false;
} else if (entry.maintainState) {
offstageChildren.add(TickerMode(enabled: false, child: _OverlayEntry(entry)));
}
}
return _Theatre( // 马上要直播了,widget是OverlayEntry,我们写的真widget在哪呢??
onstage: Stack( // visible
fit: StackFit.expand,
children: onstageChildren.reversed.toList(growable: false), // 按从底层到上层的顺序绘制widget
),
offstage: offstageChildren, // kept alive, and are built, but are not laid out or painted.
);
}
insert(OverlayEntry entry, { OverlayEntry below, OverlayEntry above }) {
entry._overlay = this; // insert时把当前OverlayState关联到OverlayEntry
setState(() { // setState !!
_entries.insert(_insertionIndex(below, above), entry);
});
}
void _remove(OverlayEntry entry) {
if (mounted) {
setState(() {
_entries.remove(entry);
});
}
}
rearrange();
}
class _OverlayEntry extends StatefulWidget {
final OverlayEntry entry;
}
class _OverlayEntryState extends State<_OverlayEntry> {
@override
Widget build(BuildContext context) {
return widget.entry.builder(context); // 调用了OverlayEntry的builder !
}
void _markNeedsBuild() {
setState(() { /* the state that changed is in the builder */ });
}
}
有2个问题:
1、OverlayState是怎么显示MaterialApp里的home参数指定的widget(初始route)呢?
2、_Theatre准备好了,马上要直播了,可widget是OverlayEntry,我们写的真widget在哪呢?
第1个问题,再回看下源码,再看看NavigatorState里push初始route时干了啥。
在initState里先生成route、再push(route)、再遍历_history放到_initialOverlayEntries里:
route ??= _routeNamed<Object>(Navigator.defaultRouteName, arguments: null);
push(route);
for (Route<dynamic> route in _history)
_initialOverlayEntries.addAll(route.overlayEntries);
先看看生成的是啥route:
Route<T> _routeNamed<T>(String name, { @required Object arguments, bool allowNull = false }) {
Route<T> route = widget.onGenerateRoute(settings);
return route;
}
onGenerateRoute就是_WidgetsAppState里创建Navigator时赋值的方法_onGenerateRoute,这里对于'/'route的就是调用widget.pageRouteBuilder返回route。
final Route<dynamic> route = widget.pageRouteBuilder<dynamic>(
settings,
pageContentBuilder, // 对于'/'route就是返回widget.home
);
return route;
这里widget.pageRouteBuilder就是_MaterialAppState里传递的,实际就是MaterialPageRoute<T>(settings: settings, builder: builder)。
对于widget.home 这个widget,生成的是MaterialPageRoute。
先列下MaterialPageRoute的父类与主要方法:
class MaterialPageRoute<T> extends PageRoute<T> {
}
abstract class PageRoute<T> extends ModalRoute<T> {
}
abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T> {
@override
void install(OverlayEntry insertionPoint) {
super.install(insertionPoint); // 最后调用超父类OverlayRoute的install
...
}
@override
Iterable<OverlayEntry> createOverlayEntries() sync* { // 同步生成器,生成2个OverlayEntry并返回
yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier);
yield OverlayEntry(builder: _buildModalScope, maintainState: maintainState);
}
}
OverlayEntry不是widget,参数builder是_buildModalScope会创建widget,里面包含用route.buildPage()返回的widget。这个buildPage在MaterialPageRoute里有实现,就是返回widget.home 。
abstract class TransitionRoute<T> extends OverlayRoute<T> {
}
abstract class OverlayRoute<T> extends Route<T> {
Iterable<OverlayEntry> createOverlayEntries();
final List<OverlayEntry> _overlayEntries = <OverlayEntry>[];
@override
void install(OverlayEntry insertionPoint) {
_overlayEntries.addAll(createOverlayEntries()); // 先创建OverlayEntries在add进_overlayEntries里
navigator.overlay?.insertAll(_overlayEntries, above: insertionPoint); // 把_overlayEntries插入route关联的overlay里。对于'/'route,overlay此时还是null。
super.install(insertionPoint);
}
}
/// An abstraction for an entry managed by a [Navigator].
abstract class Route<T> {
final RouteSettings settings;
List<OverlayEntry> get overlayEntries => const <OverlayEntry>[];
}
push(route)里有这2句:
route.install(_currentOverlayEntry); // 把route插入到_currentOverlayEntry之上。
_history.add(route);
简单总结,对于widget.home这个widget:
- 对应的route是MaterialPageRoute,是MaterialApp的state里自动配置的;
- 在NavigatorState的initState()里,把这个route插入到当前最新的route的最新的overlayentry之上(应该是null)。在插入同时,让route自己创建overlay entries,其中一个overlayEntry提供方法返回widget.home;
- 在NavigatorState的_history里保存了这个route,并遍历_history把所有route里的overlay entry保存到_initialOverlayEntries;
- 在NavigatorState的build()里,创建Overlay并传递了_initialOverlayEntries;
- 在OverlayState的initState()里把_initialOverlayEntries放进_entries里;
- 在OverlayState的build()里遍历_entries,区分要显示的和不用绘制的entry,包装成_OverlayEntry这个widgte给_Theatre去渲染;
第2个问题,前面看到widget其实是被包装在route的其中一个OverlayEntry里,OverlayEntry不是widget,其参数builder会创建widget,里面包含用route.buildPage()返回的widget,即home widget。route.buildPage()的调用时机就是 OverlayState在进行insertAll、insert、_remove等操作时会setState,这会导致子孙节点做build。
以上分析的是MaterialApp了里home参数的widget的绘制路径,其他widget页面可以通过Navigator.of()获得NavigatorState再push、pop一个route,可以是命名方式的route(会用pageRouteBuilder生成route),也可以自己实现生成那个route:
route = PageRouteBuilder<dynamic>(
settings: RouteSettings(name: path),
transitionDuration: Duration(milliseconds: 200),
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return findPage(path, params: params); // 根据path找到对应的widget页面
},
transitionsBuilder: _standardTransitionsBuilder(transition),
);
OverlayEntry是啥?
class OverlayEntry {
final WidgetBuilder builder;
bool _opaque;
bool _maintainState;
OverlayState _overlay; // 直接关联了overlay
final GlobalKey<_OverlayEntryState> _key = GlobalKey<_OverlayEntryState>();
void markNeedsBuild() { // 让OverlayEntry在下一次渲染周期做build,其实是调用了builder属性
_key.currentState?._markNeedsBuild();
}
}
简单总结:
MaterialApp 链式包装了 WidgtesApp 包装了 Navigator 包装了 Overlay ,这些都是widget,
为了方便管理页面,抽象出了Route、OverlayEntry,Navigator管理所有Route,一个route包含多个OverlayEntry,OverlayEntry提供builder对最终的widget做build。
widget页面可以通过Navigator进行push、pop,都是在栈顶操作,操作完会通知各NavigationObserver发生页面切换。
借鉴点:
通过key找到currentContext、currentWidget、currentState,如:
class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
final List<Route<dynamic>> _history = <Route<dynamic>>[];
final GlobalKey<OverlayState> _overlayKey = GlobalKey<OverlayState>();
OverlayState get overlay => _overlayKey.currentState;
@override
Widget build(BuildContext context) {
return Listener(
child: AbsorbPointer(
child: FocusScope(
child: Overlay(
key: _overlayKey
),
),
),
);
}
}
class Overlay extends StatefulWidget {}
class OverlayState extends State<Overlay> with TickerProviderStateMixin {
void insertAll() {}
}
abstract class OverlayRoute<T> extends Route<T> {
void install(OverlayEntry insertionPoint) {
...
navigator.overlay?.insertAll(_overlayEntries, above: insertionPoint);
}
}