When trying to show a dialog box with flutter, it is required to specify the context like so:
Example AlertDialog Box
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("GeeksForGeeks"),
backgroundColor: Colors.green,
),
body: Container(
child: Center(
child: RaisedButton(
onPressed: () {
return showDialog(
context: context, //current context is required
builder: (ctx) => AlertDialog(
title: Text("Alert Dialog Box"),
content: Text("You have raised a Alert Dialog Box"),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text("okay"),
),
],
),
);
},
child: Text("Show alert Dialog box"),
),
),
),
);
}
}
This is fine if we only want to show the dialog box in a widget with the context provided, but what if we want to show a dialog box not in a widget but in a service where there is no context provided? Here is where navigatorKey comes into the play. Basically after setting a global key for navigator (navigatorKey
parameter of MaterialApp
class), its current state becomes accessible from any part of the code. We can then pass the context to the showDialog function. Here is how we can achieve that:
1. First define a global key for navigator and call it navigator_key.dart
import 'package:flutter/material.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
2. Set the navigatorKey inside your MaterialApp
...
import '.../navigator_key.dart';
//optional only if you want a global snackbar as well
import '.../snackbar_key.dart';
...
MaterialApp(
theme: ThemeData(brightness: Brightness.dark, primaryColor: Colors.blueGrey),
scaffoldMessengerKey: snackbarKey, //this is optional, not required for this tutorial, only if you want a global snackbar like a dialog box, see optional part
navigatorKey: navigatorKey,
home: Scaffold(
appBar: AppBar(title: const Text('MaterialApp Theme'),
),
),
)
3. Now that we have defined and set our navigatorKey, we can use it to get the currentContext, create a new file called navigator_global.dart where all our route functions sits
import 'package:flutter/material.dart';
import '.../navigator_key.dart';
class GlobalNavigator {
static showAlertDialog(String text) {
showDialog(
barrierDismissible: false,
context: navigatorKey.currentContext!,
builder: (BuildContext context) {
return AlertDialog(
content: Text(text),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.pop(context, true);
},
child: const Text('OK'),
),
],
);
},
);
}
}
4. Now we can use this alert dialog where the current context doesn't exist like inside a service
import '.../navigator_global.dart';
...
GlobalNavigator.showAlertDialog('Any message');
...
5. You can also do the same thing with a loading box and pop pages using the navigatorKey, since dialogs are just another type of routes like MaterialPageRoute
or CupertinoPageRoute
- they are all derived from the Modal Route
class and their instances are pushed into NavigatorState
. In your navigator_global.dart you can add the following
static pop() {
navigatorKey.currentState?.pop();
}
static popUntil(String routeName) {
navigatorKey.currentState?.popUntil((route) {
return route.settings.name == routeName;
});
}
static showLoadingDialog() {
showDialog(
barrierDismissible: false,
context: navigatorKey.currentContext!,
builder: (BuildContext context) {
return const LoadingWidget();
},
);
}
6. And use it the same way as the alert dialog box
import '.../navigator_global.dart';
...
//show loading
GlobalNavigator.showLoadingDialog();
//after completion of loading, pop the loading page
GlobalNavigator.pop();
...
Optional:
A snackbar uses the ScaffoldMessengerState object, so create a new file called snackbar_key.dart
import 'package:flutter/material.dart';
final GlobalKey<ScaffoldMessengerState> snackbarKey =
GlobalKey<ScaffoldMessengerState>();
And create a snackbar_global.dart file for the implementation
import 'package:flutter/material.dart';
import '.../snackbar_key.dart';
class GlobalSnackBar {
final String message;
const GlobalSnackBar({
required this.message,
});
static show(String message) {
snackbarKey.currentState?.showSnackBar(
SnackBar(
content: Text(message),
action: SnackBarAction(
label: 'OK',
onPressed: () {
// Code to execute.
},
),
),
);
}
}
and use it the same way as the alert dialog box as in step 4
import '.../snackbar_global.dart';
...
GlobalSnackBar.show('Error sending email');
...
Happy coding :)