InheritedWidget becomes null on Navigator route change - flutter

I'm using an InheritedWidget to keep track of auth state. It works fine on main app. But as soon as I go to another page via a route change, the components in the new route cannot find the inherited widget.
Ideally, I would like all pages/routes to share the same auth state.
The following minimal example gives a NULL pointer error.
import 'package:flutter/material.dart';
void main() => runApp(EntryPoint());
class TestContext extends InheritedWidget {
const TestContext({
Key key,
Widget child,
}) : super(key: key, child: child);
final String hello = "foobar";
static TestContext of(BuildContext context) {
return context.inheritFromWidgetOfExactType(TestContext) as TestContext;
}
#override
bool updateShouldNotify(TestContext oldWidget) => true;
}
class EntryPoint extends StatelessWidget {
/// Needed for dialog
final navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
title: 'TODO',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: TestContext(
child: Scaffold(
body: Center(
child: Builder(
builder: (context) => FlatButton(
child: Text('Launch'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginForm()),
);
}))))));
}
}
class LoginForm extends StatefulWidget {
#override
LoginFormState createState() {
return LoginFormState();
}
}
class LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Scaffold(
body: Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter your JIRA website URL'),
keyboardType: TextInputType.url,
textInputAction: TextInputAction.next,
style: TextStyle(
fontSize: 28,
initialValue: TestContext.of(context).hello,
),
],
)),
));
}
}
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building LoginForm(dirty, state: LoginFormState#be3a1):
The getter 'hello' was called on null.
Receiver: null
Tried calling: hello
User-created ancestor of the error-causing widget was
MaterialApp
package:flutter_greeting_screen/main.dart:63
When the exception was thrown, this was the stack
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 LoginFormState.build
package:flutter_greeting_screen/main.dart:144

When you use Navigator.of(context).push(...) or Navigator.push(context, ...) or Navigator.of(context).pushNamed(...) or Navigator.pushNamed(context, ...), the widget pushed is not a child of the widget that call Navigator.push (and its variants), this widget is a child of the closest instance of Navigator that encloses the given context, in your case the Navigator is created by the MaterialApp, so if you want to provide TestContext to all routes, the InheritedWidget must be a parent of the Navigator, in your case must be a parent of MaterialApp.
class EntryPoint extends StatelessWidget {
/// Needed for dialog
final navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return TestContext(
child: MaterialApp(
navigatorKey: navigatorKey,
title: 'TODO',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Builder(
builder: (context) => FlatButton(
child: Text('Launch'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginForm()),
);
}))))),
);
}
}
I also recommend that you look at Provider widget of the provider package, which is a widget with syntax sugar for InheritedWidget.
A generic implementation of InheritedWidget. It allows to expose any kind of object, without having to manually write an InheritedWidget ourselves.
Provider<String>.value(
value: 'Hello World',
child: MaterialApp(
home: Home(),
)
)
Provider<Auth>(
builder: (context) => Auth(),
dispose: (context, auth) => auth.dispose(),
child: MaterialApp(
home: Home(),
)
)

Related

how can i fix : Navigator operation requested with a context that does not include a Navigator

I´m new in flutter and i´m trying to create a navigation to another page called Registro();
I'm trying with this solutionthat i found in a post here in stackoverflow.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(home: Login());
}
}
class Login extends StatelessWidget {
const Login({Key? key}) : super(key: key);
static const String _title = 'Sample App';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
bottomNavigationBar: BottomAppBar(
elevation: 3,
child: Row(
children: <Widget>[
const Text('¿No tienes una cuenta?'),
TextButton(
child: const Text(
'Registrate',
style: TextStyle(fontSize: 20),
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => new Registro()),
);
},
)
],
mainAxisAlignment: MainAxisAlignment.center,
),
),
),
);
}
}
Also i tried this solution -> and doesnt works :c
Navigator operation requested with a context that does not include a Navigator
If you want to use Navigator.push(context, yourPageRoute), (which does a Navigator.of(context)), context needs to be able to access a Navigator placed above in the widget tree.
Usually, this Navigator is the root one that is being built in MaterialApp.
I your case you have
Widget build(BuildContext context) {
return MaterialApp( // <- The Navigator is inside this widget.
// ...
TextButton(
onPressed: () {
Navigator.of(context);
// ...
},
),
);
}
But the context you are using comes from the build method of MyApp, which is not below the MaterialApp widget you are using.
So you need to use the context that comes from below the MaterialApp in the widget tree.
How to solve it
You could use a Builder widget to be able to access the context from its builder method:
Widget build(BuildContext context) {
return MaterialApp( // <- The Navigator is inside this widget.
// ...
Builder(
builder: (context) {
return TextButton(
onPressed: () {
Navigator.of(context); // <- Now the context comes from Builder which is below MaterialApp and you should be able to access its Navigator.
// ...
},
),
},
),
);
}

Flutter navigating MaterialApp children

I'm fairly new to flutter and dart, I set up my app like so:
class MyExpensesApp extends StatefulWidget {
const MyExpensesApp({Key? key}) : super(key: key);
#override
State<MyExpensesApp> createState() => _MyExpensesAppState();
}
class _MyExpensesAppState extends State<MyExpensesApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'title',
home: Directionality(
textDirection: TextDirection.rtl,
child: HomePage(),
),
);
}
}
and i have a FloatingAction button inside the HomePage which navigates to a different page when clicked
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
....
),
floatingActionButton: FloatingActionButton(
onPressed: () => {
Navigator.push(
context, MaterialPageRoute(builder: (context) => ExpensePage()))
},
tooltip: "add expense",
child: Icon(Icons.add),
),
);
}
}
but when navigation to the ExpensePage screen, it is not RTL, which means it is not a child of Directionality widget?
Am i just replacing the whole MaterialApp with ExpensePage?
How can i only replace the Directionality Child when navigating?
I don't need any intricate routing, as i only have those 2 pages.
To put a Directionality widget above the Navigator (and hence above every page), you can use the builder property in MaterialApp.
return MaterialApp(
title: 'title',
home: HomePage(),
builder: (context, child) => Directionality(
textDirection: TextDirection.rtl,
child: child!,
),
);
For the child page, you can use Scaffold as the root parent.
Then try with the same routing.

Flutter Navigator not working with named routes

I'm trying to use the Navigator to navigate between named routes.
Despite the fact that a question exists here of similar nature, I did not find the answers to be particularly helpful. Additionally when I tried to implement some of the proposed solutions none of them worked.
The relevant error is:
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.
I tried removing and adding the providers and that doesnt seem to be the cause of the problem, I have tested that access to the Provider.of<T>(context) interface works as expected.
I also tried implementing the Builder widget but that was also ineffective.
For clarification, Naigator.of(context).pushNamed('/home') appears within the build method of LandingPage for debugging purposes.
Code:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [Provider<LiraAnchor>(create: (_) => LiraAnchor())],
child: MaterialApp(
theme: ThemeData(
primaryColor: LiraColours.highlightGreen,
highlightColor: LiraColours.highlightGreen,
accentColor: LiraColours.highlightGreen,
cursorColor: LiraColours.highlightGreen,
indicatorColor: LiraColours.highlightGreen,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
debugShowCheckedModeBanner: false,
routes: {'/home': (ctx) => HomePage(), '/topup': (ctx) => TopUpPage()},
builder: (ctx, _) {
return LandingPage();
},
),
);
}
}
class LandingPage extends StatefulWidget {
LandingPage({Key key}) : super(key: key);
#override
_LandingPageState createState() => _LandingPageState();
}
class _LandingPageState extends State<LandingPage> {
final PageController _pageController = PageController();
#override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final double width = mediaQuery.size.width;
final double height = mediaQuery.size.height;
final List<Widget> onboarding = [
SplashPage(width: width, height: height, pageController: _pageController),
SignInPage(height: height, width: width)
];
Navigator.of(context).pushNamed('/home');
return Scaffold(
backgroundColor: Color(0xFFECF0F3),
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: IntrinsicHeight(
child: SingleChildScrollView(
child: Container(
width: width,
height: height,
child: PageView.builder(
controller: this._pageController,
itemCount: onboarding.length,
itemBuilder: (ctx, index) {
return onboarding[index];
}),
),
),
));
}),
);
}
}
replace this line
routes: {'/home': (ctx) => HomePage(), '/topup': (ctx) => TopUpPage()},
with
initialRoute: RoutesLinks.main,
onGenerateRoute: RoutesProvider.provideRoutes,
and add this file
class RoutesProvider {
static Route<dynamic> provideRoutes(RouteSettings settings) {
// Getting arguments passed, in while calling Navigator.pushNamed
final arguments = settings.arguments;
switch (settings.name) {
case RoutesLinks.main:
return MaterialPageRoute(builder: (_) => MainPage());
case RoutesLinks.home:
return MaterialPageRoute(builder: (_) => HomeScreen());
default:
// If there is no such named route in the switch statement, e.g. /third
return _errorRoute();
}
}
static Route<dynamic> _errorRoute() {
return MaterialPageRoute(builder: (_) {
return Scaffold(
appBar: AppBar(
title: Text('Error'),
),
body: Center(
child: Text('ERROR'),
),
);
});
}
}
class RoutesLinks {
static const main = '/';
static const home = '/home';
}

showDialog from root widget

I want to show a dialog from root widget (the one that created MaterialApp) I have a NavigatorState instance, but showDialog requires context that would return Navigator.of(context).
It looks like I need to provide context from a route, but I can't do this, because the root widget does not have it.
EDIT: I have found a workaround: I can push fake route that is only there to showDialog and then pop that route when dialog finishes. Not pretty but works.
I fixed the problem by using navigatorKey.currentState.overlay.context. Here is example:
class GlobalDialogApp extends StatefulWidget {
#override
_GlobalDialogAppState createState() => _GlobalDialogAppState();
}
class _GlobalDialogAppState extends State<GlobalDialogApp> {
final navigatorKey = GlobalKey<NavigatorState>();
void show() {
final context = navigatorKey.currentState.overlay.context;
final dialog = AlertDialog(
content: Text('Test'),
);
showDialog(context: context, builder: (x) => dialog);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
home: Scaffold(
body: Center(
child: RaisedButton(
child: Text('Show alert'),
onPressed: show,
),
),
),
);
}
}
tl;dr: If you want to call showDialog from your root widget, extrude your code into another widget (e.g. a StatelessWidget), and call showDialog there.
Anyway, in the following I'm going to assume you are running into this issue:
flutter: No MaterialLocalizations found.
flutter: MyApp widgets require MaterialLocalizations to be provided by a Localizations widget ancestor.
flutter: Localizations are used to generate many different messages, labels,and abbreviations which are used by the material library.
As said before, showDialog can only be called in a BuildContext whose ancestor has a MaterialApp. Therefore you can't directly call showDialogif you have a structure like this:
- MaterialApp
- Scaffold
- Button // call show Dialog here
In a code example this would result in code like this, throwing the error given above:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
home: Scaffold(
body: Center(
child: RaisedButton(
child: Text('Show dialog!'),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
child: Text('Dialog.'),
);
});
}),
),
),
);
}
}
To solve this error from occuring you can create a new Widget, which has its own BuildContext. The modified structure would look like this:
- MaterialApp
- Home
- Home // your own (Stateless)Widget
- Button // call show Dialog here
Modifying the code example to the structure given above, results in the code snippet below. showDialogcan be called without throwing the error.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
home: Home()
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text('Show dialog!'),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
child: Text('Dialog.'),
);
});
}),
),
);
}
}
They changed the way the navigator overlay works.
This is the working solution for us as the accepted one isn't anymore.
// If you want to use the context for anything.
final context = navigatorKey.currentState.overlay.context;
// How to insert the dialog into the display queue.
navigatorKey.currentState.overlay.insert(anyDialog);
If you already have a context object, you can get root material app's context by
final rootContext = context.findRootAncestorStateOfType<NavigatorState>().context
and passing this to showDialog or showModalBottomSheet context argument.
Since showDialog is used for showing a material dialog It can be used for showing dialogs inside a MaterialApp widget only. It can not be used to show dialog outside it.
If it helps anyone else, inject the navigator key into a dialog widget like so.
class MyApp extends StatelessWidget {
MyApp({Key key});
final navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
onGenerateRoute: Router.generateRoute,
// ...
builder: (context, routeChild) {
return Material(
child: InviteRequestModal(
navigatorKey: navigatorKey,
child: routeChild,
),
);
},
);
}
Then in the Widget that requires the modal, you can use it as mentioned above.
class InviteRequestModal extends StatelessWidget {
final Widget child;
final GlobalKey<NavigatorState> navigatorKey;
InviteRequestModal({
Key key,
this.child,
this.navigatorKey,
}) : super(key: key);
void _showInviteRequest(InviteRequest invite) {
final context = navigatorKey.currentState.overlay.context;
showDialog(
context: context,
builder: (_) {
// Your dialog content
return Container();
}
);
}
#override
Widget build(BuildContext context) {
return BlocListener<InviteContactsBloc, InviteContactsState>(
listenWhen: (previous, current) => current is InviteRequestLoaded,
listener: (_, state) {
if (state is InviteRequestLoaded) {
_showInviteRequest(state.invite);
}
},
child: child,
);
}
}
The answer just that simple, when you are providing MaterialApp to the tree it was providing but at the immediate bottom you are the context which obtained before providing MaterialApp to the tree. To resolve the issue you need to create a new context which will have the MaterialApp properties. For that wrap a Builder above the home and vola it is working...!
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Builder(builder: (context) {
return HomePage(
child: Center(
child: TextButton(
onPressed: () async {
await showDialog(
context: context,
builder: (context) => Dialog(
child: Container(
color: Colors.green,
height: 50,
width: 100,
child: Text("Hi, I am a dialog"),
),
),
);
},
child: Text("Tap me"),
),
),
);
}),
);
}
For those wanting to see how to do this in a multiple widget/route/file scenario, I used it with InheritedWidget and an extension on BuildContext.
main.dart
import 'package:flutter/material.dart';
import 'package:myapp/home_screen.dart';
import 'package:myapp/app_navkey.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return AppNavKey(
navigatorKey: navigatorKey,
child: MaterialApp(
navigatorKey: navigatorKey,
theme: ThemeData(),
home: Scaffold(
body: HomeScreen(),
),
),
);
}
}
home_screen.dart
import 'package:myapp/extensions.dart';
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final overlayContext = context.navigationKey().currentState.overlay.context;
return Center(
child: TextButton(
child: Text('Show dialog!'),
onPressed: () {
showDialog(
context: overlayContext, // use app level navigation context overlay
builder: (BuildContext context) {
return Dialog(
child: Text('Dialog.'),
);
});
},
),
);
}
}
app_navkey.dart
import 'package:flutter/widgets.dart';
class AppNavKey extends InheritedWidget {
final Widget child;
final GlobalKey<NavigatorState> navigatorKey;
AppNavKey({
Key key,
#required this.child,
#required this.navigatorKey,
}) : super(key: key, child: child);
static GlobalKey<NavigatorState> of(BuildContext context) {
final ctx = context.dependOnInheritedWidgetOfExactType<AppNavKey>();
if (ctx == null) throw Exception('Could not find ancestor of type AppNavProvider');
return ctx.navigatorKey;
}
#override
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
}
extensions.dart
import 'package:flutter/widgets.dart';
import 'package:myapp/app_navkey.dart';
extension SwitchTabContext on BuildContext {
/// Get app level NavigatorState key.
/// ```dart
/// context.navigationKey();
/// ```
GlobalKey<NavigatorState> navigationKey() => AppNavKey.of(this);
}

Navigator operation requested with a context that does not include a Navigator

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),
);
}
}
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 happens 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 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, "/"),
),
),
),
);
}
}
Hy guys, i have the same problem. This is occur for me. The solution what i found is very simple. Only what i did is in a simple code:
void main() {
runApp(MaterialApp(
home: YOURAPP() ,
),
);
}
I hope was useful.
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')),
)),
);
}
}
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,
...
);
}
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.
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
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,
)
);
}
}
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()),
);
},
),
);
}
}
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")
},
It is Simple
instead using this normal code
`runApp(BasicBankingSystem());`
wrap it with MaterialApp
runApp(MaterialApp(home: BasicBankingSystem()));
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
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, "/"),
),
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");
Change your main function example:
void main() {
runApp(
MaterialApp(
title: 'Your title',
home: MyApp(),
)
);
}
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;
}
}
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"),
),
],
),
);
}
}
Builder(
builder: (context) {
return TextButton(
child: const Text('Bearbeiten'),
onPressed:(){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const gotothesiteyouwant()),
);
});
}
),
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!