192

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), ); } } 
2
  • 1
    this error comes by inserting in your widget tree a MaterialApp or WidgetApp widget. Try adding MaterialApp widget in main method and remaining code in stateless or stateful widget then issue will be resolved. Commented Jun 1, 2022 at 5:00
  • One of the best ways to handle this is described by this answer in another question: stackoverflow.com/a/73433546/5365164 Commented Sep 9, 2023 at 23:47

23 Answers 23

393

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 WidgetsApp. 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/WidgetsApp!

That's most likely the case. But this error can still happen when you use a context that is a parent of MaterialApp/WidgetsApp.

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 finds a Navigator or there's no more widgets to iterate through.

In the first case, everything is fine. In the second, it throws

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 build of MyApp.

The problem is, MyApp is actually a parent of MaterialApp, as it's the widget who instantiates 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, "/"), ), ), ), ); } } 
Sign up to request clarification or add additional context in comments.

3 Comments

@GrumpyRodriguez This answer assumes that you want to use a single widget for home. But this solution does not extend to cases where you use a router, and therefore need to implement some kind of base widget for every route that you have. For example, if you needed to initialize a listener in a common widget that needed to use the Navigator, this wouldn't work because there is no common widget to all routes. The widget gets duplicated, and so does your listener.
Yes I'm having the same problem where I'm using a router. Here is the router code. return MaterialApp.router( builder: (context, child) => Builder( builder: (context) { FToastBuilder(); listenShareMediaFiles(context); return child!; }, theme: lightTheme, routerConfig: router, ); Since "listenShareMediaFiles" opens dialog box it's a routing and I'm getting the same error. How can I fix it? @DannyAllen
when ur using go_routes use ShellRoute to make a common widget which can have common listener final appRouter = GoRouter( initialLocation: RouteNameConstant.splashScreen, routes: [ ShellRoute( builder: (context, state, child) { return AuthNavigatorWidget( child: child); // common wrapper for all screens }, routes: [ ], ), ], ); and in materialApp.router builder: (context, child) => child!,
69

I had the same problem. The solution I´ve found is very simple:

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

2 Comments

This was the easiest solution. To the point!
Shouldn't we only be using the MatrialApp widget one time in the widget tree?
45

Make sure your current parent widget not with same level with MaterialApp

Wrong Way

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: () { //wrong way: use context in same level tree with MaterialApp Navigator.push(context, MaterialPageRoute(builder: (context) => ScanScreen())); }, child: const Text('SCAN')), )), ), ); } } 

Right way

void main() => runApp(MaterialApp( title: "App", home: HomeScreen(), )); class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( centerTitle: true, title: Text('Title'), ), body: Center( child: Padding( padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), child: RaisedButton( onPressed: () { //right way: use context in below level tree with MaterialApp Navigator.push(context, MaterialPageRoute(builder: (context) => ScanScreen())); }, child: const Text('SCAN')), )), ); } } 

5 Comments

Best explanation
how to pass multiple parameters and get/use them on another screen? Kindly suggest. Thanks
@Kamlesh simply pass it by constructor of your widget (Class)
Thanks for your quick reply. Could you please update your answer by adding different types of 2 params? Thanks a lot.
@Kamlesh sorry for late replay, To keep the clear answer directly to main the question above i cant edit it. If you still need help, I can contact you :)
24

Just like with a Scaffold you can use a GlobalKey. It doesn't need context.

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 Comments

This should be the accepted answer. if you want to push new route from MyApp outside the MaterialApp then this is the way..
I got a bug doing this: If no route is provided using home, routes, onGenerateRoute, or onUnknownRoute, a non-null callback for the builder property must be provided, and the other navigator-related properties, navigatorKey, initialRoute, and navigatorObservers, must have their initial values (null, null, and the empty list, respectively).
how to pass multiple parameters and get/use them on another screen? Kindly suggest. Thanks
14

I set up this simple example for routing in a 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') ) ); } } 

Note, that the SettingsPage extends StatelessWidget and not Navigator. I'm not able to reproduce your error.

Does this example help you in building your app? Let me know if I can help you with anything else.

4 Comments

Yes this helped! I had set home: new Scaffold(appBar: new AppBar(title: new Text('GroupUp'),), in the constructor of the MaterialApp rather than creating another Widget. (At least I think that was the issue)
Where did you have your onTap than?
Maybe you're running into github.com/flutter/flutter/issues/15919
how to pass multiple parameters and get/use them on another screen? Kindly suggest. Thanks.
12

You should rewrite your code in main.dart FROM:

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

TO

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

The point is to have the home property to be your first page this worked for me, I hope it will help someone in the future

Comments

5

As per this comment If your navigator is inside Material context navigator push will give this error. if you create a new widget and assign it to the material app home navigator will work.

This won't work

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

This will work

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

1 Comment

how to pass multiple parameters and get/use them on another screen? Kindly suggest. Thanks
4

A complete and tested solution:

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

1 Comment

how to pass multiple parameters and get/use them on another screen? Kindly suggest. Thanks
3

It is Simple

instead using this normal code

`runApp(BasicBankingSystem());` 

wrap it with MaterialApp

runApp(MaterialApp(home: BasicBankingSystem())); 

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
2

I was facing the same problem and solved by removing home from MaterialApp and use initialRoute instead.

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

And

onTap: () => { Navigator.pushNamed(context, "/settings") }, 

Comments

1

It happens because the context on the widget that tries to navigate is still using the material widget.

The short answer for the solution is to :

extract your widget

that has navigation to new class so it has a different context when calling the navigation

Comments

1
Builder( builder: (context) { return TextButton( child: const Text('Bearbeiten'), onPressed:(){ Navigator.push( context, MaterialPageRoute(builder: (context) => const gotothesiteyouwant()), ); }); } ), 

This builder Widget helped me a Lot

Comments

1

In my case this was being caused because of trying to use something context dependant and when the context was not fully loaded (e.g. from init state, app from terminated state, etc.).

Since I was already using GlobalKey, in this case I had to use the context from that global key, in my go_router settings:

final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>(); ... GoRouter( initialLocation: '/home', navigatorKey: rootNavigatorKey, ... 

then from the same key we can retrieve the context in initState (if that's the case for you as well):

final navigatorKeyContext = rootNavigatorKey.currentContext; // can be null if(navigatorKeyContext!=null){ ScaffoldMessenger.of(navigatorKeyContext).showSnackBar(SnackBar(content: Text(content))); } 

This case especially applies with Firebase Cloud Messaging (FCM) and go_router integration (opening the app from a terminated state).

Comments

0

When your screen is not navigated from other screen,you don't initially have access to the navigator,Because it is not instantiated yet.So in that case wrap your widget with builder and extract context from there.This worked for me.

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

Comments

0

You ca use this plugin https://pub.dev/packages/get/versions/2.0.2

in The MaterialApp assign property navigatorKey: Get.key,

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

you can access Get.toNamed("Your route name");

Comments

0

Change your main function example:

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

Comments

0

use this

void main() { runApp(MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()),); } 

instead of this

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

Wrap with materialapp

reproduce code

import 'dart:convert'; import 'package:flutter/material.dart'; void main() { // reproduce code runApp(MyApp()); // working switch // // runApp( // // MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()),); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( height: 100, width: 100, child: ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => IntroPage(Isscar4: true)), ); }, child: RichText( text: TextSpan( text: 'CAR', style: TextStyle( letterSpacing: 3, color: Colors.white, fontWeight: FontWeight.w400), children: [ TextSpan( text: '4', style: TextStyle( fontSize: 25, color: Colors.red, fontWeight: FontWeight.bold)) ], )), ), ), ], ), SizedBox( height: 10, ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( height: 100, width: 100, child: ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => IntroPage(Isscar4: false)), ); }, child: RichText( text: TextSpan( text: 'BIKE', style: TextStyle( letterSpacing: 3, color: Colors.white, fontWeight: FontWeight.w400), children: [ TextSpan( text: '2', style: TextStyle( fontSize: 25, color: Colors.red, fontWeight: FontWeight.bold)) ], )), ), ), ], ) ]))); } MaterialApp Swithwidget(istrue) { return MaterialApp( home: Scaffold( body: IntroPage( Isscar4: istrue, ), ), ); } } class Hi extends StatelessWidget { const Hi({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Container( child: Text("df"), ); } } class IntroPage extends StatelessWidget { final Isscar4; IntroPage({ Key? key, required this.Isscar4, }) : super(key: key); List<Widget> listPagesViewModel = []; List<IntroModel> models = []; @override Widget build(BuildContext context) { List<dynamic> intro = fetchIntroApi(Isscar4); intro.forEach((element) { var element2 = element as Map<String, dynamic>; var cd = IntroModel.fromJson(element2); models.add(cd); }); models.forEach((element) { listPagesViewModel.add(Text("")); }); return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: Container(), )); } List fetchIntroApi(bool bool) { var four = bool; if (four) { var data = '[ {"name_Title": "title name1","description": "description1"}, {"name_Title": "title name2","description": "description2"}, {"name_Title": "title name3","description": "description3"}, {"name_Title": "title name4","description": "description4"} ]'; return json.decode(data); } else { var data = '[ {"name_Title": "title name","description": "description1"}, {"name_Title": "title name2","description": "description2"}, {"name_Title": "title name3","description": "description3"} ]'; return json.decode(data); } } } class IntroModel { String? nameTitle; String? description; IntroModel({this.nameTitle, this.description}); IntroModel.fromJson(Map<String, dynamic> json) { nameTitle = json['name_Title']; description = json['description']; } Map<String, dynamic> toJson() { final Map<String, dynamic> data = new Map<String, dynamic>(); data['name_Title'] = this.nameTitle; data['description'] = this.description; return data; } } 

Comments

0
class Splash extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Splash Screen', theme: ThemeData( primarySwatch: Colors.green, ), home: MyState(), debugShowCheckedModeBanner: false, ); } void main() { runApp(Splash()); } class MyState extends StatefulWidget{ @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyState> { @override void initState() { super.initState(); Timer(Duration(seconds: 3), ()=>Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => Login() ) ) ); } @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center , children: [ Container( child: Image.asset("assets/images/herosplash.png"), ), ], ), ); } } 

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0
Builder( builder: (context) { return TextButton( child: const Text('Bearbeiten'), onPressed:(){ Navigator.push( context, MaterialPageRoute(builder: (context) => const gotothesiteyouwant()), ); }); } ), 

2 Comments

sorry for bad formating .... but this worked fine for me ... thanks @lava
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
0

Here, all you need is to make MaterialApp the parent of your Build. This is because the context that you've used to navigate to a different screen is finding a MaterialApp or a WidgetApp as a parent of the build.

And Since in your case, the situation is the opposite, therefore you need to modify it by either calling a new Stateless widget the parent of is the MaterialApp or by simply using a Builder as home: Builder in MaterialApp.

Hope this would help!

Comments

0

Wrapping the Button Widget inside a Builder Widget fixed my issue. Below is the code snippet for the same.

 Builder( builder: (context) => ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => const SignIn(), ), ); }, child: const Text('Sign in'), ), ), 

Comments

0

This error happen when you want to route page from main.dart and you should cut your code from main.dart and transfer to another page (second page) so after that you have to route which page in second page and call it in the main.dart

Comments

0

This error come when You use Riverpod with autoroute and you use for navigator below code :

Navigator.push( context, MaterialPageRoute( builder: (context) => ShopMainPage(), ), );

above code does not work because context not pass in ShopMainPage()

use below code :

context.pushRoute(ShopMainRoute());

problem fixed in my case .

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.