Get the Global Context in Flutter - flutter

Is there any way to get the global context of Material App in Flutter. Not the context of particular screen.
I am trying to get the context but it gives me the context of particular screen but I wan the context of MaterialApp.

If above solution does not works please try this solution.
Create the class. Here it named as NavigationService
import 'package:flutter/material.dart';
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
}
Set the navigatorKey property of MaterialApp in the main.dart
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: NavigationService.navigatorKey, // set property
)
}
Great! Now you can use anywhere you want e.g.
print("---print context:
${NavigationService.navigatorKey.currentContext}");

Assign a GlobalKey() to the MaterialApp which you can put in a separate class, let's call it App :
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: App.materialKey, // GlobalKey()
)
}
Now wherever you want to get the context of the MaterialApp, you just have to call :
App.materialKey.currentContext
Here I'm printing MaterialApp context :
print('Material App Context : ${App.materialKey.currentContext}');
OUTPUT :
flutter: Material App Context : MaterialApp-[GlobalKey#4fab4](state: _MaterialAppState#4bb44)

If you use Getx State Management you can use;
GetMaterialApp(
navigatorKey: Get.key,
);
Reach out with;
BuildContext? context = Get.key.currentContext;

Related

How can I make a card clickable and navigate to another page inside TabBarView?

I'm trying to make a card clickable and navigate to another page 'forecastWeather.dart'. But I can't seem to figure out how I can fix the error I'm getting. The error message says
Another exception was thrown: Navigator operation requested with a context that does not include a Navigator.
I'm using a TabBarView with two tabs and one contains a card which should be clickable and navigate to another page 'forecastWeather.dart'. Please help!
In order to get use navigator.push, you must pass the context of your widget and have a MaterialApp somewhere in your tree, you do both, so the code should work, but it doesn't.
The reason is simple, the context you are passing is outdated, you are first creating the context when the build method begins, at that point, there is no material app, then you add a material app, but the context already exists, so the context you are passing when calling Navigator.push(context ... has no MaterialApp.
A possible fix is to use the Builder widget to make a new context after you create the MaterialApp widget:
#override
Widget build(BuildContext context) {
// context has no material app.
return MaterialApp(
home: Builder(
builder: (context) {
// this context does have a material app and is safe to use.
return DefaultTabController( ... );
}
),
);
}
Another fix is to move the code into a new widget with it's own context:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
// this context was created after the material app.
return DefaultTabController(...);
}
}
i suggest you to use get: ^4.6.1 package.
with this package you just need to use this for any navigation:
Get.to(ForecastWeather());
this package have so other features read its document here
[1]: https://pub.dev/packages/get

how do you share data between viewmodels in Flutter Provider state management?

I am new to flutter Provider state management. If each view has its own viewmodel, how do you share data between viewmodels?
Lets say you have a viewmodel with a list in it, in one screen you want to show the list, and in another screen you want to show the list count?
You can place ViewModels on the top of widget tree, before MaterialApp
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: <SingleChildWidget>[
ChangeNotifierProvider<ViewModel1>(create: (_) => ViewModel1()),
ChangeNotifierProvider<ViewModel2>(create: (_) => ViewModel2()),
],
child: MaterialApp( ... );
);
}
}
so you can access both viewmodels everywhere

Exception has occurred. FlutterError (MediaQuery.of() called with a context that does not contain a MediaQuery

Exception has occurred.
FlutterError (MediaQuery.of() called with a context that does not contain a MediaQuery.
No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of(). This can happen because you do not have a WidgetsApp or MaterialApp widget (those widgets introduce a MediaQuery), or it can happen if the context you use comes from a widget above those widgets.
The context used was:
Scaffold)
I got this error can anyone help me out of this?
You need to make sure your app starts with any kind of app: MaterialApp, WidgetsApp or CupertinoApp, that's where the MediaQuery inherited widget is created.
void main() {
runApp(
MaterialApp(
home: MyWidget()
)
)
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
MediaQuery.of(context); // You can access it here.
return Container();
}
}

How can I use MediaQuery to build theme?

started to learn Flutter so I'm thinking if is it possible to create a function that returns a ThemeData object, but inside this function I wanna use MediaQuery.of(context). I mean, I know I can create such a function, but if I use MediaQuery.of(context) inside of it, I have an error
(MediaQuery.of() called with a context that does not contain a MediaQuery) complaining about the missing of MediaQueryProvider. I know I can use MediaQuery in child elements of MaterialApp, but I have a design problem now. Imagine this:
ThemeData getTheme(BuildContext context) {
// I wanna be able to call MediaQuery.of(contex) here
return ThemeData();
}
MaterialApp(
home: home,
// the getTheme() must be able to use MediaQuery. It takes a context and returns a ThemeData object
theme: theme.getTheme(context),
routes: routes,
)
Is there a way to do it? or event better, Should I do it?
Thank you for your help.
A workaround is not using the theme property of MaterialApp but wrapping the descendants with a Theme widget using the builder property. It will have the MaterialApp as an ancestor so you can use MediaQuery there.
MaterialApp(
builder: (context, child) {
return Theme(
data: getTheme(context), // you can use MediaQuery.of(context) in the called function
child: child,
);
},
home: HomeScreen(),
// etc, no theme here
)
Welcome to the brotherhood.
Actually, to call for MediaQuery you will need to wrap your main widget with a MaterialApp() which usually is done in your main.dart file.
Here is an example:
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
Hope it helped!
You can try this.
Create a stateless/ stateful widget for the home of your MaterialApp, and inside that, create your getTheme method.
Example,
homePage extends StatelessWidget{
ThemeData getTheme(BuildContext context) {
// Call MediaQuery.of(contex) here
return ThemeData();
}
#override
//build method
}//homePage ends here.
Now, in your material app, simple call that method.
MaterialApp(
home: homePage(),
theme: homePage().getTheme(context),
routes: routes,
)

Flutter showDialog with navigator key rather than passing context

Currently its very hectic to show dialog from any layer of code in app just because one has to pass context in it. Hence i thought to pass navigatorKey.currentContext (Navigator key is a global key passed to Material app navigatorKey parameter) to show dialog. But i got the 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."
The issue is showDialog calls Navigator.of(context) internally and which looks for the navigator ancestor which ofcourse will return null as the navigator is itself the root. Hence it will not find the navigator as ancestor.
Is there a way we can directly pass the navigator state/context to showDialog function to show the dialog? Or is there a more easy way to show Dialog without passing context to it if we want to show it from bloc?
I found a simple solution:
navigatorKey.currentState.overlay.context
I use this in a redux middleware where I keep navigatorKey, and want to show a dialog globally anywhere in the app everytime I dispatch a specific action.
Since this one is merged:
https://github.com/flutter/flutter/pull/58259
You can use:
navigatorKey.currentContext;
You can make use of InheritedWidget here. Make a InheritedWidget the root for your application which holds a navigator key. Then you can pass any context of child widgets to get the current navigator state.
Example:
InheritedWidget:
// Your InheritedWidget
class NavigatorStateFromKeyOrContext extends InheritedWidget {
const NavigatorStateFromKeyOrContext({
Key key,
#required this.navigatorKey,
#required Widget child,
}) : super(key: key, child: child);
final GlobalKey<NavigatorState> navigatorKey;
static GlobalKey<NavigatorState> getKey(BuildContext context) {
final NavigatorStateFromKeyOrContext provider =
context.inheritFromWidgetOfExactType(NavigatorStateFromKeyOrContext);
return provider.navigatorKey;
}
static NavigatorState of(BuildContext context) {
NavigatorState state;
try {
state = Navigator.of(context);
} catch (e) {
// Assertion error thrown in debug mode, in release mode no errors are thrown
print(e);
}
if (state != null) {
// state can be null when context does not include a Navigator in release mode
return state;
}
final NavigatorStateFromKeyOrContext provider =
context.inheritFromWidgetOfExactType(NavigatorStateFromKeyOrContext);
return provider.navigatorKey?.currentState;
}
#override
bool updateShouldNotify(NavigatorStateFromKeyOrContext oldWidget) {
return navigatorKey != oldWidget.navigatorKey;
}
}
HomeScreen:
// Your home screen
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: NavigatorStateFromKeyOrContext.getKey(context),
home: InitPage(),
);
}
}
The root of the application will look like,
final GlobalKey navigator = GlobalKey<NavigatorState>(debugLabel: 'AppNavigator');
runApp(
NavigatorStateFromKeyOrContext(
navigatorKey: navigator,
child: HomePage(),
),
);
Now from anywhere in the app, pass any context to get the NavigatorState like
NavigatorStateFromKeyOrContext.of(context)
Note: This is one approach I came up with where I used InheritedWidget, there are many other ways to achieve the same, like using Singleton, having a global bloc to provide navigator key, storing the navigator key in a Redux store or any other global state management solutions, etc.
Hope this helps!
Currently, I am showing a dialog by creating a function in my util class which takes the context as a parameter.
static void showAlertDialog(String title, String message, BuildContext context) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text(title),
content: new Text(message),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Using the above function as:
UtilClass. showAlertDialog("Title", "Message", context);