Issue
I'm trying to start a new screen within an onTap but I get the following error:
Navigator operation requested with a context that does not include a Navigator.
The code I am using to navigate is:
onTap: () { Navigator.of(context).pushNamed('/settings'); },
I have set up a route in my app as follows:
routes: <String, WidgetBuilder>{
'/settings': (BuildContext context) => new SettingsPage(),
},
I've tried to copy the code using the stocks sample application. I've looked at the Navigator and Route documentation and can't figure out how the context can be made to include a Navigator. The context
being used in the onTap is referenced from the parameter passed into the build method:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
SettingsPage is a class as follows:
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),
);
}
}
Solution
TLDR: Wrap the widget which needs to access to Navigator
into a Builder
or extract that sub-tree into a class. And use the new BuildContext
to access Navigator
.
This error is unrelated to the destination. It happens because you used a context
that doesn't contain a Navigator
instance as parent.
How do I create a Navigator instance then ?
This is usually done by inserting in your widget tree a MaterialApp
or WidgetApp
. Although you can do it manually by using Navigator
directly but less recommended. Then, all children of such widget can access NavigatorState
using Navigator.of(context)
.
Wait, I already have a MaterialApp
/WidgetApp
!
That's most likely the case. But this error can still happens when you use a context
that is a parent of MaterialApp
/WidgetApp
.
This happens because when you do Navigator.of(context)
, it will start from the widget associated to the context
used. And then go upward in the widget tree until it either find a Navigator
or there's no more widget.
In the first case, everything is fine. In the second, it throws a
Navigator operation requested with a context that does not include a Navigator.
So, how do I fix it ?
First, let's reproduce this error :
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, "/"),
),
),
);
}
}
This example creates a button that attempts to go to '/' on click but will instead throw an exception.
Notice here that in the
onPressed: () => Navigator.pushNamed(context, "/"),
we used context
passed by to build
of MyApp
.
The problem is, MyApp
is actually a parent of MaterialApp
. As it's the widget who instantiate MaterialApp
! Therefore MyApp
's BuildContext
doesn't have a MaterialApp
as parent!
To solve this problem, we need to use a different context
.
In this situation, the easiest solution is to introduce a new widget as child of MaterialApp
. And then use that widget's context to do the Navigator
call.
There are a few ways to achieve this. You can extract home
into a custom class :
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, "/"),
),
);
}
}
Or you can use 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, "/"),
),
),
),
);
}
}
Answered By - Rémi Rousselet
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.