0

I've decided to give flutter a try and stumbled upon the following problem:

In my app I would like to have a button in the AppBar that would take the user to another screen. However whenever I attempt to call Navigator in any of the IconButtons' onPressed handlers, I get the following error:

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.

Here's my main.dart (minus the imports):

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var service = new ProjectService();

    return new MaterialApp(
        theme: new ThemeData(
          primaryColor: Colors.deepOrange,
        ),
        home: new Scaffold(
          appBar: new AppBar(
              title: new Text("My App Title"),
              actions: 
              <Widget>[
                new IconButton(
                    icon: new Icon(Icons.add),
                    onPressed: () =>
                        Navigator.of(context).pushNamed("/projects/add")) //<-- Here's where I get the exception
              ],
              elevation: 0.0),
          body: new Center(child: new ProjectList(projects: service.toList())), //<-- Exactly the same code runs fine here
          drawer: new AppDrawer(),
        ),
        routes: <String, WidgetBuilder>{
          '/projects/add': (BuildContext context) => new ProjectAddScreen(),
          '/projects/test': (BuildContext context) => new ProjectAddScreen(),
          '/settings': (BuildContext context) => new AppSettingsScreen(),
          '/about': (BuildContext context) => new AboutScreen()
        });
  }
}

This is the only place where the app instance is constructed and AppBar is the standard AppBar that comes bundled with the flutter/material package.

My flutter doctor output if that's of any relevance

[√] Flutter (Channel master, v0.2.5-pre.41, on Microsoft Windows [Version 10.0.16299.309], locale en-US)
[!] Android toolchain - develop for Android devices (Android SDK 26.0.2)
    X Android license status unknown. // I use the SDK manager from Visual Studio
[!] Android Studio (version 3.0)
    X Unable to determine bundled Java version. //I use the SDK manager from Visual Studio so I have no need in Android Studio
[√] VS Code, 64-bit edition (version 1.21.1)
[√] Connected devices (1 available)
VadimG
  • 509
  • 4
  • 12
  • @rémi-rousselet, thanks for pointing this one out. Conceptually, my question indeed seems like it's a duplicate of that thread, however the code posted there is different. Moreover none of the official guides on flutter that I've seen shows instantiating a `Scaffold` or an `AppBar` via a `Builder` so I did not pay enough attention to it. – VadimG Mar 26 '18 at 06:25

1 Answers1

0

Though as Rémi pointed out in his comment to this question (sorry, I don't know how to properly mention his user), this turns out to be a duplicate of this thread. But because I find the code provided there a bit out of context I think it's worth to mention how I applied the solution provided there in my particular use-case.

I did some debugging and found out that when I'm inside the onPressed handler of the IconButton, the context there is not that of the IconButton or the AppBar. It is that of the MaterialApp. If you ask me it is both logical and weird at the same time, especially if you take into consideration that putting the same code inside body' center works.

But nevertheless, I opened the Stocks sample app and took a look at how they structured the app and simply followed the pattern they used there.

Here's what I ended up with:

main.dart

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

class MyApp extends StatefulWidget {

  @override
  MyAppState createState() {
    return new MyAppState();
  }

}

class MyAppStateextends State<MyApp> {

  ProjectService _projectService;

  @override
  void initState() {
    super.initState();
    _projectService = new ProjectService();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
       title: "My App",
        theme: new ThemeData.light(),
        routes: <String, WidgetBuilder> {
          '/projects/add': (BuildContext context) => new HomeScreen(_projectService),
          '/': (BuildContext context) => new HomeScreen(_projectService)
        }
    );
  }
}

HomeScreen.dart

class HomeScreen extends StatefulWidget {
  final ProjectService projectService;

  const HomeScreen(this.projectService);

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

class HomeScreenState extends State<HomeScreen> {
  void handleProjectUpdate() {
    setState(() => {});
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new ListView(
        children: widget.projectService.all().map<ListTile>((f) {
          return new ListTile(
            title: new Text(f.name),
          );
        }).toList(),
      ),
      appBar: new AppBar(
        actions: <Widget>[
          new IconButton(
              icon: new Icon(Icons.add),
              onPressed: () => Navigator.pushNamed(context, "/projects/add"))
        ],
      ),
    );
  }
}
VadimG
  • 509
  • 4
  • 12