showDialog from root widget

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.'),
                    );
                  });
            }),
      ),
    );
  }
}

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,
          ),
        ),
      ),
    );
  }
}

Tags:

Flutter