Flutter导航器无法正常工作。

35

我有一个应用程序有两个屏幕,我想通过按按钮从第一屏幕推到第二屏幕。

屏幕1

import 'package:flutter/material.dart';
import './view/second_page.dart';

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

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new MainScreen();
  }
}

class MainScreen extends State<MyApp> {

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

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        home: new Scaffold(
            appBar: new AppBar(
                title: new Text("Title")
            ),
            body: new Center(
                child: new FlatButton(child: new Text("Second page"),
                    onPressed: () {
                      Navigator.push(context,
                          new MaterialPageRoute(
                              builder: (context) => new SecondPage()))
                    }
                )
            )
        )
    );
  }
}

屏幕2

import 'package:flutter/material.dart';

class SecondPage extends StatefulWidget {

  @override
  State<StatefulWidget> createState() {
    return new SecondPageState();
  }
}


class SecondPageState extends State<SecondPage> {

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

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Title"),
      ),
      body: new Center(
        child: new Text("Some text"),
      ),
    );
  }
}

推送未发生,我得到了这个:

在处理手势时引发了以下断言:使用不包括Navigator的上下文请求Navigator操作。用于从Navigator推送或弹出路线的上下文必须是Navigator widget的后代小部件的上下文。

另一个异常被抛出:使用不包括Navigator的上下文请求Navigator操作。

出了什么问题?

8个回答

50

把Flutter中的widget想象成一棵树,上下文(context)指向使用build函数构建的任何节点。在您的情况下,您有

MainScreen    <------ context
  --> MaterialApp
   (--> Navigator built within MaterialApp)
      --> Scaffold
        --> App Bar
          --> ...
        --> Center
          --> FlatButton

当您使用上下文查找Navigator时,您正在使用MainScreen的上下文,该上下文不在导航器下。

您可以创建一个新的Stateless或Stateful Widget子类来包含您的Center + FlatButton,因为其中的build函数将指向该级别,或者您可以使用Builder并定义builder回调(其上下文指向Builder)来返回Center + FlatButton。


为什么导航器无法使用MaterialApp的上下文?谢谢。 - Brendon Cheung

26

只需要在主方法中将 MaterialApp 类设置为以下示例即可


 void main() => runApp(MaterialApp(home: FooClass(),));

这对我来说运作良好,希望你也能成功


2
优雅的解决方案。 - Naveed Ahmad
这是唯一对我有效的解决方案。 - suhailvs

25

路由无法找到的主要原因有两个:

1)路由定义在传递给Navigator.of(context)的上下文之下 - 这是@rmtmackenzie解释过的场景

2)路由定义在同级分支上,例如:

Root 

  -> Content (Routes e.g. Home/Profile/Basket/Search)

  -> Navigation (we want to dispatch from here)

如果我们想从 Navigation widget 分配一个路由,我们必须知道 NavigatorState 的引用。拥有全局引用是昂贵的,特别是当你想要在树中移动小部件时。https://docs.flutter.io/flutter/widgets/GlobalKey-class.html。只在没有办法从 Navigator.of(context) 获取它的地方使用它。

要在 MaterialApp 内使用 GlobalKey,请定义 navigatorKey。

final navigatorKey = GlobalKey<NavigatorState>();

Widget build(BuildContext context) => MaterialApp {
    navigatorKey: navigatorKey
    onGenerateRoute : .....
};

现在在应用程序中,您可以通过传递 navigatorKey 来调用任何位置。

navigatorKey.currentState.push(....);

刚刚发布了一篇关于此的文章https://medium.com/@swav.kulinski/flutter-navigating-off-the-charts-e118562a36a5


使用GlobalKey相对较昂贵,如果可能的话应该避免使用 - 虽然您可以使用它,但这并不意味着除非您有特定的用例需要它,否则您应该使用它。我认为,如果您确实需要传递它,您可能可以重构代码以避免使用它。绝对不应该传递它!整个Nativator.of(context)机制旨在避免必须传递东西。 - rmtmckenzie
1
你能详细说明为什么传递引用比遍历树更昂贵吗? - robotoaster
我的前面的评论是想说GlobalKeys是一种相对昂贵的键类型...而且,除非你绝对需要使用它们,否则应该避免使用键。所以我的前面的陈述是误导性的,但我无法返回并编辑它.... 无论如何,Flutter有InheritedWidget或直接传递来从小部件获取数据到子小部件,并使用Widget.of()返回到父小部件。作为一般架构,Flutter鼓励向下传递(不可变)数据和向上传递状态。GlobalKey通过允许状态向下传递来颠覆这一点。 - rmtmckenzie
这很公平 - 有些情况下你需要使用GlobalKey,但如果你发现自己在使用它,你应该考虑是否可以重构以避免使用。尽管从技术上讲...除非你正在做一些特殊的事情(这可能会发生,我也会这样做),通常不应该在导航器的同级分支上放置东西 - 你应该在每个“页面”(即大多数Flutter示例中的pages)中创建子小部件的实例,并让Flutter找出它们之间的转换。但这远远超出了这个问题和答案的范围 =D。 - rmtmckenzie
在会话过期后,从服务类切换到登录屏幕非常有用。 - Giddy Naya
显示剩余2条评论

1

还有一种非常不同的解决方法,如果你使用的是Alarm Manager(Android),并且返回到你的应用程序。如果在导航之前没有打开屏幕,导航器将永远不起作用。虽然这是一种罕见的用法,但我认为这是值得知道的。


1

请确保在同一上下文中提到的路由表:

@override
Widget build(BuildContext context) {
    
    return MaterialApp(
        home: FutureBuilder(
            future: _isUserLoggedIn(),
            builder: (ctx, loginSnapshot) => 
                loginSnapshot.connectionState == ConnectionState.waiting ?
                    SplashScreen() : loginSnapshot.data == true ? AppLandingScreen(): SignUpScreen()
        ),
        routes: {
            AppLandingScreen.routeName: (ctx) => AppLandingScreen(),
        },
    );
}

我遇到这个问题是因为我在不同的构建方法中定义了路由表。

1
对我来说,我在MaterialApp上将navigatorKey设置为了错误的字段。
final navigatorKey = GlobalKey<NavigatorState>();
class MyApp extends StatelessWidget {

  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // key: navigatorKey, <-- wrong 
      navigatorKey: navigatorKey, // <-- correct
    );
  }
}

0

我是一名新手,已经花了两天时间试图解决导航器对象链接到黑屏的问题。

导致这个问题的原因是重复的虚拟数据。以下是两个虚拟数据块:

**Problematic data **- duplicate assets/image:

_buildFoodItem('assets/plate1.png', 'Salmon bowl', '\$24'),
_buildFoodItem('assets/plate2.png', 'Spring bowl', '\$13'),
_buildFoodItem('assets/plate1.png', 'Salmon bowl', '\$24'),
_buildFoodItem('assets/plate5.png', 'Berry bowl', '\$34'),

**Solution **- after eliminating duplicated image argument:
_buildFoodItem('assets/plate1.png', 'Salmon bowl', '\$24'),
_buildFoodItem('assets/plate2.png', 'Spring bowl', '\$13'),
_buildFoodItem('assets/plate6.png', 'Avocado bowl', '\$34'),

希望这能帮助到某个人,,,,,,,


-1
如果导航器不起作用,可能有很多原因,但主要原因是导航器找不到上下文。因此,为了解决这个问题,请尝试将您的小部件包装在Builder内,因为Builder有自己的上下文...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接