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

时间:2017-05-16 14:35:52

标签: flutter

我正在尝试在onTap中启动一个新屏幕,但是我收到以下错误:

  

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

我用来导航的代码是:

onTap: () { Navigator.of(context).pushNamed('/settings'); },

我在我的应用中设置了一条路线,如下所示:

routes: <String, WidgetBuilder>{
    '/settings': (BuildContext context) => new SettingsPage(),
},

我尝试使用stocks示例应用程序复制代码。我查看了Navigator和Route文档,但无法弄清楚如何将上下文包含在Navigator中。 onTap中使用的context是从传递给构建方法的参数中引用的:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

SettingsPage是一个类,如下所示:

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),
  );
 }
}

13 个答案:

答案 0 :(得分:53)

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


此错误与目的地无关。发生这种情况是因为您使用的context不包含Navigator实例作为父对象。

然后如何创建Navigator实例?

通常通过在小部件树中插入MaterialAppWidgetApp来完成。尽管您可以直接直接使用Navigator来手动完成此操作,但建议不要这样做。然后,此类小部件的所有子代都可以使用NavigatorState访问Navigator.of(context)

等等,我已经有一个MaterialApp / WidgetApp了!

最有可能是这种情况。但是,当您使用context / MaterialApp的父级WidgetApp时,仍然会发生此错误。

之所以会发生这种情况,是因为当您执行Navigator.of(context)时,它将从与所使用的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, "/"),

我们使用了context中传递给build的{​​{1}}的{​​{1}}。

问题在于,MyApp实际上是MyApp的父级。实例化MaterialApp的是小部件!因此,MaterialApp的{​​{1}}没有MyApp作为父项!

要解决此问题,我们需要使用其他BuildContext

在这种情况下,最简单的解决方案是引入一个新的小部件作为MaterialApp的子级。然后使用该小部件的上下文进行context调用。

有几种方法可以实现这一目标。您可以将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, "/"),
        ),
      );
  }
}

答案 1 :(得分:10)

我在flutter app中设置了这个简单的路由示例:

import 'package:flutter/material.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new MyHomePage(),
      routes: <String, WidgetBuilder>{
        '/settings': (BuildContext context) => new SettingsPage(),
      },
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('TestProject'),
      ),
      body: new Center(
        child: new FlatButton(
          child: const Text('Go to Settings'),
          onPressed: () => Navigator.of(context).pushNamed('/settings')
        )
      )
    );
  }
}

class SettingsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text('SettingsPage'),
        ),
        body: new Center(
            child: new Text('Settings')
        )
    );
  }
}

注意,SettingsPage扩展了StatelessWidget而不是Navigator。我无法重现您的错误。

此示例是否可以帮助您构建应用?如果我可以帮助你,请告诉我。

答案 2 :(得分:4)

就像使用脚手架一样,您可以使用GlobalKey。不需要上下文。

final _navKey = GlobalKey<NavigatorState>();

void _navigateToLogin() {
  _navKey.currentState.popUntil((r) => r.isFirst);
  _navKey.currentState.pushReplacementNamed(LoginRoute.name);
}

@override
Widget build(BuildContext context) {
  return MaterialApp(
    navigatorKey: _navKey,
    ...
  );
}

答案 3 :(得分:3)

完整且经过测试的解决方案:

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:my-app/view/main-view.dart';

class SplashView extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        home: Builder(
          builder: (context) => new _SplashContent(),
        ),
        routes: <String, WidgetBuilder>{
          '/main': (BuildContext context) => new MainView()}
    );
  }
}

class _SplashContent extends StatefulWidget{

  @override
  _SplashContentState createState() => new _SplashContentState();
}

class _SplashContentState extends State<_SplashContent>
    with SingleTickerProviderStateMixin {

  var _iconAnimationController;
  var _iconAnimation;

  startTimeout() async {
    var duration = const Duration(seconds: 3);
    return new Timer(duration, handleTimeout);
  }

  void handleTimeout() {
    Navigator.pushReplacementNamed(context, "/main");
  }

  @override
  void initState() {
    super.initState();

    _iconAnimationController = new AnimationController(
        vsync: this, duration: new Duration(milliseconds: 2000));

    _iconAnimation = new CurvedAnimation(
        parent: _iconAnimationController, curve: Curves.easeIn);
    _iconAnimation.addListener(() => this.setState(() {}));

    _iconAnimationController.forward();

    startTimeout();
  }

  @override
  Widget build(BuildContext context) {
    return new Center(
        child: new Image(
          image: new AssetImage("images/logo.png"),
          width: _iconAnimation.value * 100,
          height: _iconAnimation.value * 100,
        )
    );
  }
}

答案 4 :(得分:3)

您应该在main.dart中重写代码 发件人:

void main() => runApp(MyApp());

TO

void main() {
  runApp(MaterialApp(
  title: 'Your title',
  home: MyApp(),));}

关键是要拥有房屋属性作为您的第一页 这对我有用,我希望它将对以后的人有所帮助

答案 5 :(得分:2)

嗨,我有同样的问题。这是发生在我身上。我发现的解决方案非常简单。我所做的只是一个简单的代码:

void main() {
  runApp(MaterialApp(
    home: YOURAPP() ,
    ),
  );
}

我希望是有用的。

答案 6 :(得分:2)

确保您当前的父窗口小部件与MaterialApp的级别不相同

错误的方式

import re

line = "GeneralBKT_n24_-e_dee_testcaseid_blt12_0001_s3_n4"

for id in re.finditer(r'testcaseid_([^_]+_[^_]+)', line):
    print(id.group(1))

正确的方式

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: () {
                Navigator.push(context,
                    MaterialPageRoute(builder: (context) => ScanScreen()));
              },
              child: const Text('SCAN')),
        )),
      ),
    );
  }
}

答案 7 :(得分:2)

我也遇到了同样的问题,并通过从 MaterialApp 删除 home 并使用 initialRoute 来解决了此问题。

(length(Description) - length(replace(Description,somestring,''))) / length(somestring)

return MaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: '/',
      routes: {
        '/': (context) => MyApp(),
        '/settings': (context) => SettingsPage(),
      },
    );

答案 8 :(得分:0)

根据this comment,如果您的导航器位于Material上下文中,则导航器推入将给出此错误。如果您创建一个新的小部件并将其分配给Material App Home导航器,则它将起作用。

这不起作用

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text("Title"),
        ),
        body: new Center(child: new Text("Click Me")),
        floatingActionButton: new FloatingActionButton(
          child: new Icon(Icons.add),
          backgroundColor: Colors.orange,
          onPressed: () {
            print("Clicked");
            Navigator.push(
              context,
              new MaterialPageRoute(builder: (context) => new AddTaskScreen()),
            );
          },
        ),
      ),
    );
  }
}

这将起作用

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        home: new HomeScreen());
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Title"),
      ),
      body: new Center(child: new Text("Click Me")),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.add),
        backgroundColor: Colors.orange,
        onPressed: () {
          print("Clicked");
          Navigator.push(
            context,
            new MaterialPageRoute(builder: (context) => new AddTaskScreen()),
          );
        },
      ),
    );
  }
}

答案 9 :(得分:0)

当您的屏幕未从其他屏幕导航时,您最初没有访问导航器的权限,因为它尚未实例化。因此,在这种情况下,请使用构建器包装小部件并从中提取上下文。

builder: (context) => Center(
              child: RaisedButton(
                child: Text("Foo"),
                onPressed: () => Navigator.pushNamed(context, "/"),
              ),

答案 10 :(得分:0)

发生这种情况是因为尝试导航的小部件上的上下文仍在使用材质小部件。

该解决方案的简短答案是:

提取小部件

具有导航到新类的功能,因此在调用导航时具有不同的上下文

答案 11 :(得分:0)

您可以使用此插件 https://pub.dev/packages/get/versions/2.0.2

在MaterialApp分配属性navigatorKey:Get.key,

MaterialApp(
      navigatorKey: Get.key,
      initialRoute: "/",
     );

您可以访问Get.toNamed("Your route name");

答案 12 :(得分:0)

更改您的主要功能示例:

`void main() {
  runApp(MaterialApp(
  title: 'Your title',
  home: MyApp(),));}`