2023-06-14 深入理解Flutter中的Navigato
简介:
在Flutter中,导航器(Navigator)是管理应用程序中不同页面之间跳转的关键组件之一。其中的Navigator.of(context)
方法用于获取当前上下文(context)所在的导航器状态(NavigatorState),从而实现页面之间的导航操作。然而,有时候在使用Navigator.of(context)
方法时会遇到异常,本文将解释这个异常的原因并提供解决方法。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: OutlinedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => SecondPage()));
},
child: Text('跳转')),
),
),
);
}
}
异常情况:
当使用Navigator.of(context)
方法时,有时会抛出以下异常信息:
======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
Navigator operation requested with a context that does not include a Navigator.
The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
When the exception was thrown, this was the stack:
该异常信息提示了我们使用Navigator.of(context)
方法的上下文(context)必须是导航器(Navigator)的子级组件的上下文。
解决方法:
为了解决这个异常,我们需要确保调用Navigator.of(context)
方法的上下文(context)是导航器(Navigator)的子级组件。下面是一个示例代码,演示了如何正确使用Navigator.of(context)
方法:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) { //context
return MaterialApp(
home: FirstPage(),
);
}
}
class FirstPage extends StatelessWidget {
const FirstPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: OutlinedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => SecondPage()));
},
child: Text('跳转'),
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text('second page'),
),
);
}
}
现在,在FirstPage
组件中,Navigator.of(context)
方法将会正常工作,因为context
是FirstPage
组件的上下文,并且FirstPage
是作为MaterialApp
的子组件存在的。
探究Navigator.of(context)
源码: 为了更深入地理解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) {
throw FlutterError(
'Navigator operation requested with a context that does not include a Navigator.\n'
'The context used to push or pop routes from the Navigator must be that of a '
'widget that is a descendant of a Navigator widget.',
);
}
return true;
}());
return navigator!;
}
从源码中可以看到,Navigator.of(context)
方法首先检查上下文(context)是否是一个有状态组件(StatefulElement)且其状态(state)是NavigatorState
类型,如果是,那么该上下文的导航器状态就是我们要获取的。接着,如果rootNavigator
参数为true
,则继续查找最近的根级导航器状态;如果rootNavigator
参数为false
,则继续查找最近的导航器状态。最后,如果没有找到导航器状态,则抛出异常。
在上述示例中,Navigator.of(context)
方法的可用性还与MaterialApp
的层级结构有关。下面是MaterialApp
包裹的组件层级结构:
- MaterialApp (_MaterialAppState)
- WidgetsApp (_WidgetsAppState)
- Navigator (NavigatorState)
- WidgetsApp (_WidgetsAppState)
class MaterialApp extends StatefulWidget {
State<MaterialApp> createState() => _MaterialAppState();
Widget _buildWidgetApp(BuildContext context) {
WidgetsApp
State<WidgetsApp> createState() => _WidgetsAppState();
Widget build(BuildContext context) {
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,
)
class Navigator extends StatefulWidget {
NavigatorState createState() => NavigatorState();
class NavigatorState extends State<Navigator> with TickerProviderStateMixin, RestorationMixin {
在这个层级结构中,MaterialApp
是一个StatefulWidget
,它创建了一个_MaterialAppState
的状态。_MaterialAppState
进一步创建了一个WidgetsApp
组件,也是一个StatefulWidget
,并共享相同的状态_WidgetsAppState
。在WidgetsApp
中,又创建了一个Navigator
组件,它的状态是NavigatorState
。
因此,Navigator.of(context)
方法实际上是通过上下文(context)向上查找最近的NavigatorState
状态对象,以便进行页面导航操作。
结论:
在使用Navigator.of(context)
方法时,我们需要确保调用它的上下文(context)是导航器(Navigator)的子级组件,以避免抛出异常。这样,我们就可以在Flutter应用程序中轻松实现页面之间的导航操作。
希望本文对您理解和使用Navigator.of(context)
方法有所帮助!如果您有任何问题或疑问,请随时提问。