Flutter showDialog without context using the navigatorKey

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 :)

Leave a comment