我正在尝试在onTap中启动一个新屏幕,但我收到以下错误:
使用不包含导航器的上下文请求的导航器操作。
我用来导航的代码是:
onTap: () { Navigator.of(context).pushNamed('/settings'); },
我在我的应用程序中设置了一条路线,如下所示:
routes: <String, WidgetBuilder>{
'/settings': (BuildContext context) => new SettingsPage(),
},
我尝试使用股票示例应用程序复制代码。我查看了导航器和路由留档,但不知道如何使上下文包含导航器。onTap中使用的context
是从传递给构建方法的参数中引用的:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Settings sPage是一个类,如下所示:
class SettingsPage extends Navigator {
Widget buildAppBar(BuildContext context) {
return new AppBar(
title: const Text('Settings')
);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: buildAppBar(context),
);
}
}
TLDR:将需要访问Navigator
的小部件包装成Builder
或将该子树提取到类中。并使用新的BuildContext
访问Navigator
。
此错误与目标无关。发生这种情况是因为您使用了不包含Navigator
实例的上下文
作为父级。
那么如何创建Navigator实例呢?
这通常是通过在小部件树中插入MaterialApp
或WidgetsApp
来完成的。虽然您可以直接使用Navigator
手动执行此操作,但不太推荐。然后,此类小部件的所有子级都可以使用Navigator. of(context)
访问NavigatorState
。
等等,我已经有一个MaterialApp
/WidgetsApp
!
很可能是这样。但是当您使用上下文
是MaterialApp
/WidgetsApp
的父级时,仍然会发生此错误。
发生这种情况是因为当您执行Navigator. of(context)
时,它将从与所使用的上下文
关联的小部件开始。然后在小部件树中向上移动,直到找到Navigator
或没有更多小部件。
在第一种情况下,一切都很好。在第二种情况下,它抛出了一个
使用不包含导航器的上下文请求的导航器操作。
那么,我该如何修复它?
首先,让我们重现这个错误:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
),
);
}
}
此示例创建一个按钮,该按钮尝试在单击时转到“/”,但会引发异常。
请注意,在
onPressed: () => Navigator.pushNamed(context, "/"),
我们使用传递给MyApp
的build
的上下文
。
问题是,MyApp
实际上是MaterialApp
的父级。因为它是实例化MaterialApp
的小部件!因此MyApp
的BuildContext
没有MaterialApp
作为父级!
为了解决这个问题,我们需要使用不同的上下文
。
在这种情况下,最简单的解决方案是引入一个新小部件作为MaterialApp
的子部件。然后使用该小部件的上下文执行Navigator
调用。
有几种方法可以实现这一点。您可以将home
提取到自定义类中:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHome()
);
}
}
class MyHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
);
}
}
或者您可以使用Builder
:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
),
),
);
}
}
我也有同样的问题。我找到的解决方案非常简单:
void main() {
runApp(
MaterialApp(
home: YourApp(),
),
);
}
确保您当前的父小部件与MaterialApp的级别不同
错误的方式
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Title'),
),
body: Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: RaisedButton(
onPressed: () {
//wrong way: use context in same level tree with MaterialApp
Navigator.push(context,
MaterialPageRoute(builder: (context) => ScanScreen()));
},
child: const Text('SCAN')),
)),
),
);
}
}
正确的方式
void main() => runApp(MaterialApp(
title: "App",
home: HomeScreen(),
));
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Title'),
),
body: Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: RaisedButton(
onPressed: () {
//right way: use context in below level tree with MaterialApp
Navigator.push(context,
MaterialPageRoute(builder: (context) => ScanScreen()));
},
child: const Text('SCAN')),
)),
);
}
}