提问者:小点点

使用不包含导航器的上下文请求的导航器操作


我正在尝试在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),
  );
 }
}

共3个答案

匿名用户

TLDR:将需要访问Navigator的小部件包装成Builder或将该子树提取到类中。并使用新的BuildContext访问Navigator

此错误与目标无关。发生这种情况是因为您使用了不包含Navigator实例的上下文作为父级。

那么如何创建Navigator实例呢?

这通常是通过在小部件树中插入MaterialAppWidgetsApp来完成的。虽然您可以直接使用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, "/"),

我们使用传递给MyAppbuild上下文

问题是,MyApp实际上是MaterialApp的父级。因为它是实例化MaterialApp的小部件!因此MyAppBuildContext没有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')),
      )),
    );
  }
}