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

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!'),
),
),
);
}
}
网友评论