围观 MaterialApp, Navigator, Route, OverlayEntry, Overlay 合伙'直播' Widget

围观 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 {
  @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 {
    List _navigatorObservers;  // 保存了外面传的NavigatorObserver
    
    void initState() {
        _navigatorObservers = List.from(widget.navigatorObservers)
            ..add(_heroController);
    }

    Widget build(BuildContext context) {
        Widget result = WidgetsApp(       // 借用了WidgetsApp并传递所有属性值
           key: GlobalObjectKey(this),
          navigatorKey: widget.navigatorKey,
            navigatorObservers: _navigatorObservers,
            pageRouteBuilder: (RouteSettings settings, WidgetBuilder builder) {  // 初始化了pageRouteBuilder
                return MaterialPageRoute(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 with WidgetsBindingObserver {
    GlobalKey _navigator;
    initState() {
        _navigator = widget.navigatorKey ?? GlobalObjectKey(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 _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()
            : context.findAncestorStateOfType();
        return navigator;
    }
    
    static Future push(BuildContext context, Route route) { // 各种push、pop api都是调用Navigator.of().XXX
    return Navigator.of(context).push(route);
  }
}

class NavigatorState extends State with TickerProviderStateMixin {
    final GlobalKey _overlayKey = GlobalKey();
    final List> _history = >[];
    final Set> _poppedRoutes = >{};

    /// The [FocusScopeNode] for the [FocusScope] that encloses the routes.
    final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'Navigator Scope');

    final List _initialOverlayEntries = [];
    OverlayState get overlay => _overlayKey.currentState;
    
    initState() {
        Route route;
        route ??= _routeNamed(Navigator.defaultRouteName, arguments: null);
        push(route);    // push初始route , 会让route创建overlay entries,并保存在_history里,后面会再分析。
        for (Route route in _history)
            _initialOverlayEntries.addAll(route.overlayEntries);  // 对于'/'route来说,_history也是有元素的。
    }

    // pushNamed、pushReplacement、pushReplacementNamed、popAndPushNamed、pushNamedAndRemoveUntil、pushAndRemoveUntil等逻辑类似push
    Future push(Route 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 result ]) {
        final Route 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 initialEntries;
    OverlayState _overlay;

    static OverlayState of(BuildContext context, { Widget debugRequiredFor }) {
        final OverlayState result = context.findAncestorStateOfType();
        return result;
    }
}

OverlayState extends State with TickerProviderStateMixin {
    final List _entries = [];  // 所有OverlayEntry,通过insertAll、insert、_remove、rearrange操作增删改。

    initState() {
        insertAll(widget.initialEntries);
    }

    Widget build(BuildContext context) {
        final List onstageChildren = [];   // 保存可在屏幕上可见的widget 
        final List offstageChildren = [];  // 保存在屏幕上不可见但保活的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(Navigator.defaultRouteName, arguments: null);
push(route); 
for (Route route in _history)
    _initialOverlayEntries.addAll(route.overlayEntries);

先看看生成的是啥route:

Route _routeNamed(String name, { @required Object arguments, bool allowNull = false }) {
    Route route = widget.onGenerateRoute(settings);
    return route;
}

onGenerateRoute就是_WidgetsAppState里创建Navigator时赋值的方法_onGenerateRoute,这里对于'/'route的就是调用widget.pageRouteBuilder返回route。
    final Route route = widget.pageRouteBuilder( 
        settings,
        pageContentBuilder,  // 对于'/'route就是返回widget.home
    );
    return route;

这里widget.pageRouteBuilder就是_MaterialAppState里传递的,实际就是MaterialPageRoute(settings: settings, builder: builder)。
对于widget.home 这个widget,生成的是MaterialPageRoute。
先列下MaterialPageRoute的父类与主要方法:

class MaterialPageRoute extends PageRoute {
}

abstract class PageRoute extends ModalRoute {
}

abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute {
    @override
    void install(OverlayEntry insertionPoint) {
        super.install(insertionPoint);    // 最后调用超父类OverlayRoute的install
        ...
    }
    
    @override
    Iterable 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 extends OverlayRoute {
}

abstract class OverlayRoute extends Route {
    Iterable createOverlayEntries();
    final List _overlayEntries = [];
    
    @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 {
    final RouteSettings settings;
    List get overlayEntries => const [];
}

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(
    settings: RouteSettings(name: path),
    transitionDuration: Duration(milliseconds: 200),
    pageBuilder: (BuildContext context, Animation animation, Animation 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 with TickerProviderStateMixin {
    final List> _history = >[];
    final GlobalKey _overlayKey = GlobalKey();
    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 with TickerProviderStateMixin {
    void insertAll() {}
}

abstract class OverlayRoute extends Route {
    void install(OverlayEntry insertionPoint) {
        ...
        navigator.overlay?.insertAll(_overlayEntries, above: insertionPoint);
    }
}

你可能感兴趣的:(围观 MaterialApp, Navigator, Route, OverlayEntry, Overlay 合伙'直播' Widget)