13

Hello everyone this is a question I have been asking myself for quite sometime. I have also seen some answers to it, but it didn't solve my problem, some similar questions that I found are: Flutter showDialog with navigator key rather than passing context or Navigator operation requested with a context that does not include a Navigator

What I am trying to achieve is to show popup dialog anywhere in my app (so in any page of my app) from the same piece of code (so it's all concentrated). The issue is that from that piece of code I do not have access to the BuildContext, this is because I'm showing this popups based on events that do not come from a user action (like button tap), instead they could be Firestore listeners, or errors that occur deep into my code (so I can show an error message to the user), since I'm so deep into the code, usually I don't have access to the BuildContext.

In the similar questions, I found something that looks like a solution. These solutions use a GlobalKey for the navigator so can access it from anywhere, there are some different options on how to access it anywhere in the app, in my case I opted for a Singleton (my "Repository") where I store this globalKey. Then when the event is fired I use the globalKey to get the context and show a Dialog, but it throws this 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.

@Hemanth Raj mentions that your root Widget needs to be a MaterialApp, I do have a MaterialApp as my root.

Here is an approximation of the structure of the app I have:

void main() async {

  // Here I create the navigatorKey
  GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  // I then init my Singleton with the navigatorKey
  await repository.init(navigatorKey: navigatorKey);

  // Then I pass the navigatorKey down to my App
  runApp(MyApp(debugMode: debugMode, navigatorKey: navigatorKey));
}

class MyApp extends StatefulWidget {
  final navigatorKey;

  const MyApp({Key key, this.navigatorKey})
      : super(key: key);

  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> with WidgetsBindingObserver {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        navigatorKey: widget.navigatorKey,
        title: 'My app',
        home: SplashPage(),
        theme: _buildMyTheme(),
      );
  }
}

Then in my Repository I have a listener for firestore events. These can fire anytime after I am in my home-screen. The idea is wherever I find myself within the app, if the event is fired the popup should appear.

class Repository {

  GlobalKey<NavigatorState> get navigatorKey => _navigatorKey;
  GlobalKey<NavigatorState> _navigatorKey;

  void init({GlobalKey<NavigatorState> navigatorKey}) {
    _navigatorKey = navigatorKey;
  }

  void _listenToEvents() {
    streamWithEvents.listen((event) {
      showDialog(
        context: navigatorKey.currentContext,
        builder: (_) => CustomMessageDialog(message: event.message),
      );
    });
  }
}
Twisha Kotecha
  • 1,082
  • 1
  • 4
  • 18
Migalv
  • 652
  • 6
  • 19
  • If you use MaterialApp.router, look at https://stackoverflow.com/questions/69168726/navigatorkey-not-found-on-materialapp-router – Hadi Albinsaad Apr 28 '23 at 21:40

1 Answers1

10

Use navigatorKey.currentState.overlay.context for showDialog's context.

Shahin Mursalov
  • 625
  • 8
  • 10
  • Thanks a lot for your answer! I'll try this one out, and get back to you. If this works I will put this answer as the correct answer. – Migalv Oct 16 '20 at 10:25