19
votes

I have app with two screens, and I want to make push from 1st to second screen by pressing button.

Screen 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()))
                    }
                )
            )
        )
    );
  }
}

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

Push not happening and I got this

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.

Another exception was thrown: Navigator operation requested with a context that does not include a Navigator.

What is wrong?

7

7 Answers

39
votes

Think of the widgets in Flutter as a tree, with the context pointing to whichever node is being built with the build function. In your case, you have

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

So when you're using the context to find the Navigator, you're using a context for the MainScreen which isn't under the navigator.

You can either make a new Stateless or Stateful Widget subclass to contain your Center + FlatButton, as the build function within those will point at that level instead, or you can use a Builder and define the builder callback (which has a context pointing at the Builder) to return the Center + FlatButton.

19
votes

There are two main reasons why the route cannot be found.

1) The Route is defined below the context passed to Navigator.of(context) - scenario which @rmtmackenzie has explained

2) The Route is defined on the sibling branch e.g.

Root 

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

  -> Navigation (we want to dispatch from here)

If we want to dispatch a route from the Navigation widget, we have to know the reference to the NavigatorState. Having a global reference is expensive, especially when you intend to move widget around the tree. https://docs.flutter.io/flutter/widgets/GlobalKey-class.html. Use it only where there is no way to get it from Navigator.of(context).

To use a GlobalKey inside the MaterialApp define navigatorKey

final navigatorKey = GlobalKey<NavigatorState>();

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

Now anywhere in the app where you pass the navigatorKey you can now invoke

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

Just posted about it https://medium.com/@swav.kulinski/flutter-navigating-off-the-charts-e118562a36a5

17
votes

Just make the MaterialApp class in main method as this example

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

it works fine for me, I hope it will work with you

1
votes

There is an another very different work around about this issue, If you are using Alarm Manager (Android), and open back to your Application. If you haven't turned on the screen before navigation, the navigator will never work. Although this is a rare usage, I think It should be a worth to know.

1
votes

Make sure route table mentioned in same context:

  @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(),
      },

    );



  }

I faced this issue because i define route table in different build method.

0
votes

Am a newbie and have spent two days trying to get over the Navigtor objet linking to a black a screen.

The issue causing this was dublicated dummy data. Find Bellow the two dummny data blocks:

**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'),

I hope this helps someone,,,,,,,

-1
votes

If the navigator is not working, it can be due to many reasons but the major one is that the navigator not finds the context. So, to solve this issue try to wrap your widget inside Builder because the builder has its own context...