美文网首页
2023-04-03 Flutter导航Navigator原理

2023-04-03 Flutter导航Navigator原理

作者: 我是小胡胡分胡 | 来源:发表于2023-04-02 16:46 被阅读0次

    Flutter导航Navigator

    https://developer.aliyun.com/article/918784

    onPressed: () {
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => const SongScreen(song: song),
        ),
      );
    },
    child: Text(song.name),
    

    StatefullWidget:

    • MaterialApp、_MaterialAppState
    • WidgetsApp、_WidgetsAppState
    • Navigator 、NavigatorState
    • Router、_RouterState
      MaterialApp包裹Navigator,通过 Navigator.of(context)方法查找最近的祖先 NavigatorState

    1、 MaterialApp

    MaterialApp

    class MaterialApp extends StatefulWidget {
      @override
      State<MaterialApp> createState() => _MaterialAppState();
    }
    
    

    _MaterialAppState

    Widget build(BuildContext context) {
        Widget result = _buildWidgetApp(context);
        result = Focus(
          canRequestFocus: false,
          onKey: (FocusNode node, RawKeyEvent event) {
            if (event is! RawKeyDownEvent || event.logicalKey != LogicalKeyboardKey.escape)
              return KeyEventResult.ignored;
            return Tooltip.dismissAllToolTips() ? KeyEventResult.handled : KeyEventResult.ignored;
          },
          child: result,
        );
    
    
        return ScrollConfiguration(
          behavior: widget.scrollBehavior ?? const MaterialScrollBehavior(),
          child: HeroControllerScope(
            controller: _heroController,
            child: result,
          ),
        );
      }
    

    _buildWidgetApp

      Widget _buildWidgetApp(BuildContext context) { 
        final Color materialColor = widget.color ?? widget.theme?.primaryColor ?? Colors.blue;
        if (_usesRouter) {
          return WidgetsApp.router(
            key: GlobalObjectKey(this),
            routeInformationProvider: widget.routeInformationProvider, 
    //其他省略
          );
        }
    
        return WidgetsApp(
          key: GlobalObjectKey(this),
          navigatorKey: widget.navigatorKey,
          navigatorObservers: widget.navigatorObservers!,
          pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) {
            return MaterialPageRoute<T>(settings: settings, builder: builder);
          }, 
    //其他省略
        );
      }
    

    创建子widget,类型为 WidgetsApp

    2、WidgetsApp

    WidgetsApp

    class WidgetsApp extends StatefulWidget {
      State<WidgetsApp> createState() => _WidgetsAppState();
    }
    

    _WidgetsAppState

    class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
      @override
      Widget build(BuildContext context) {
        Widget? routing;
        if (_usesRouter) {
             routing = Router<Object>(
            restorationScopeId: 'router',
            routeInformationProvider: _effectiveRouteInformationProvider,
            routeInformationParser: widget.routeInformationParser,
            routerDelegate: widget.routerDelegate!,
            backButtonDispatcher: widget.backButtonDispatcher,
          );
        } else if (_usesNavigator) {
          routing = Navigator(
            restorationScopeId: 'nav',
            key: _navigator,
            initialRoute: _initialRouteName,
            onGenerateRoute: _onGenerateRoute,
            onGenerateInitialRoutes: widget.onGenerateInitialRoutes == null
              ? Navigator.defaultGenerateInitialRoutes
              : (NavigatorState navigator, String initialRouteName) {
                return widget.onGenerateInitialRoutes!(initialRouteName);
              },
            onUnknownRoute: _onUnknownRoute,
            observers: widget.navigatorObservers!,
            reportsRouteUpdateToEngine: true,
          );
          );
        }
    
        Widget result;
        if (widget.builder != null) {
          result = Builder(
            builder: (BuildContext context) {
              return widget.builder!(context, routing);
            },
          );
        } else {
          assert(routing != null);
          result = routing!;
        }
     
        final Widget title;
     
        final Locale appLocale = widget.locale != null
          ? _resolveLocales(<Locale>[widget.locale!], widget.supportedLocales)
          : _locale!;
    
        assert(_debugCheckLocalizations(appLocale));
    
        Widget child = Localizations(
          locale: appLocale,
          delegates: _localizationsDelegates.toList(),
          child: title,
        );
    
        final MediaQueryData? data = MediaQuery.maybeOf(context);
        if (!widget.useInheritedMediaQuery || data == null) {
          child = MediaQuery.fromWindow(
            child: child,
          );
        }
    
        return RootRestorationScope(
          restorationId: widget.restorationScopeId,
          child: SharedAppData(
            child: Shortcuts(
              debugLabel: '<Default WidgetsApp Shortcuts>',
              shortcuts: widget.shortcuts ?? WidgetsApp.defaultShortcuts, 
              child: DefaultTextEditingShortcuts(
                child: Actions(
                  actions: widget.actions ?? WidgetsApp.defaultActions,
                  child: FocusTraversalGroup(
                    policy: ReadingOrderTraversalPolicy(),
                    child: child,
                  ),
                ),
              ),
            ),
          ),
        );
      }
    

    _WidgetsAppState

    3、 Navigator

    Navigator

    class Navigator extends StatefulWidget {
      NavigatorState createState() => NavigatorState();
    }
    

    NavigatorState

      Widget build(BuildContext context) {
    
        return HeroControllerScope.none(
          child: Listener(
            onPointerDown: _handlePointerDown,
            onPointerUp: _handlePointerUpOrCancel,
            onPointerCancel: _handlePointerUpOrCancel,
            child: AbsorbPointer(
              absorbing: false, 
              child: FocusScope(
                node: focusScopeNode,
                autofocus: true,
                child: UnmanagedRestorationScope(
                  bucket: bucket,
                  child: Overlay(
                    key: _overlayKey,
                    initialEntries: overlay == null ?  _allRouteOverlayEntries.toList(growable: false) : const <OverlayEntry>[],
                  ),
                ),
              ),
            ),
          ),
        );
      }
    

    4、Router

    Router也是一个statefullWidget

    
    class Router<T> extends StatefulWidget {
      State<Router<T>> createState() => _RouterState<T>();
    }
    

    _RouterState

      Widget build(BuildContext context) {
        return UnmanagedRestorationScope(
          bucket: bucket,
          child: _RouterScope(
            routeInformationProvider: widget.routeInformationProvider,
            backButtonDispatcher: widget.backButtonDispatcher,
            routeInformationParser: widget.routeInformationParser,
            routerDelegate: widget.routerDelegate,
            routerState: this,
            child: Builder( 
              builder: widget.routerDelegate.build,
            ),
          ),
        );
      }
    

    5、 Navigator.of(context)

    static NavigatorState of(
        BuildContext context, {
        bool rootNavigator = false,
      }) {
     
        NavigatorState? navigator;
        if (context is StatefulElement && context.state is NavigatorState) {
          navigator = context.state as NavigatorState;
        }
        if (rootNavigator) {
          navigator = context.findRootAncestorStateOfType<NavigatorState>() ?? navigator;
        } else {
          navigator = navigator ?? context.findAncestorStateOfType<NavigatorState>();
        }
    
        assert(() {
          if (navigator == null) {
     
          }
          return true;
        }());
        return navigator!;
      }
    

    这段代码实现了一个静态方法 of,用于从一个 BuildContext 中获取对应的 NavigatorState。下面是这段代码的执行流程:

    • 定义一个 NavigatorState 类型的变量 navigator 并初始化为 null。
    • 如果传入的 BuildContext 是一个 StatefulElement,并且它的 state 是一个 NavigatorState,则将 navigator 赋值为该 NavigatorState。
    • 如果 rootNavigator 参数为 true,则通过 context.findRootAncestorStateOfType<NavigatorState>() 方法查找最近的祖先 NavigatorState,并将它赋值给 navigator。
    • 如果 rootNavigator 参数为 false,则通过 context.findAncestorStateOfType<NavigatorState>() 方法查找最近的祖先 NavigatorState,并将它赋值给 navigator。
    • 使用 assert() 方法进行断言,确保 navigator 不为空。如果为空,则抛出 FlutterError 异常。
    • 返回 navigator。

    因此,这段代码的作用是从给定的 BuildContext 中获取对应的 NavigatorState,如果找不到则会抛出异常。同时,可以通过 rootNavigator 参数来指定是否查找根 NavigatorState。

    6、NavigatorState

    NavigatorState 类的handlePush方法
    主要作用是

     void handlePush({ required NavigatorState navigator, required bool isNewFirst, required Route<dynamic>? previous, required Route<dynamic>? previousPresent }) {
        assert(currentState == _RouteLifecycle.push || currentState == _RouteLifecycle.pushReplace || currentState == _RouteLifecycle.replace);
        assert(navigator != null);
        assert(navigator._debugLocked);
        assert(
          route._navigator == null,
          'The pushed route has already been used. When pushing a route, a new '
          'Route object must be provided.',
        );
        final _RouteLifecycle previousState = currentState;
        route._navigator = navigator;
        route.install();
        assert(route.overlayEntries.isNotEmpty);
        if (currentState == _RouteLifecycle.push || currentState == _RouteLifecycle.pushReplace) {
          final TickerFuture routeFuture = route.didPush();
          currentState = _RouteLifecycle.pushing;
          routeFuture.whenCompleteOrCancel(() {
            if (currentState == _RouteLifecycle.pushing) {
              currentState = _RouteLifecycle.idle;
              assert(!navigator._debugLocked);
              assert(() { navigator._debugLocked = true; return true; }());
              navigator._flushHistoryUpdates();
              assert(() { navigator._debugLocked = false; return true; }());
            }
          });
        } else {
          assert(currentState == _RouteLifecycle.replace);
          route.didReplace(previous);
          currentState = _RouteLifecycle.idle;
        }
        if (isNewFirst) {
          route.didChangeNext(null);
        }
    
        if (previousState == _RouteLifecycle.replace || previousState == _RouteLifecycle.pushReplace) {
          navigator._observedRouteAdditions.add(
            _NavigatorReplaceObservation(route, previousPresent),
          );
        } else {
          assert(previousState == _RouteLifecycle.push);
          navigator._observedRouteAdditions.add(
            _NavigatorPushObservation(route, previousPresent),
          );
        }
      }
    

    这段代码实现了 Navigator 对栈进行 push 或者 pushReplace 的操作,主要的实现逻辑如下:

    • 进行断言校验,包括当前状态、navigator 的状态以及被 push 的 route 的状态。
    • 将当前 route 的 _navigator 属性设置为传入的 navigator,并调用 install() 方法。
    • 如果当前状态为 push 或者 pushReplace,则会调用 route 的 didPush() 方法,并将 currentState 设置为 pushing。
    • 如果当前状态为 replace,则会调用 route 的 didReplace() 方法,并将 currentState 设置为 idle。
    • 如果 isNewFirst 为 true,则调用 route 的 didChangeNext() 方法。
    • 根据 previousState 的不同,将对应的 _NavigatorReplaceObservation 或者 _NavigatorPushObservation 对象加入到 navigator 的 _observedRouteAdditions 中。

    总体来说,这段代码主要是实现了 Navigator 对栈进行 push 或者 pushReplace 的操作,并且在操作过程中还会调用 route 中的不同方法,以及在完成操作后会将对应的 observation 对象加入到 navigator 的 _observedRouteAdditions 中。

    最终调用到 setState


    image.png

    7、处理侧滑返回手势

    import 'package:flutter/material.dart';
    
    class MyPage extends StatefulWidget {
      @override
      _MyPageState createState() => _MyPageState();
    }
    
    class _MyPageState extends State<MyPage> {
      @override
      Widget build(BuildContext context) {
        return WillPopScope(
          onWillPop: () async {
            // 当用户从屏幕边缘向右滑动时,onWillPop回调函数会被调用
            if (Navigator.of(context).canPop()) {
              Navigator.of(context).pop();
              return false;
            }
            return true;
          },
          child: Scaffold(
            appBar: AppBar(
              title: Text('My Page'),
            ),
            body: Center(
              child: Text('Hello, World!'),
            ),
          ),
        );
      }
    }
    

    相关文章

      网友评论

          本文标题:2023-04-03 Flutter导航Navigator原理

          本文链接:https://www.haomeiwen.com/subject/qusrddtx.html