33

I'm trying to use the showDialog(context, builder) to display a greeting message when the user navigates to a certain page.

I tried this by calling the showDialog in the initState method of a stateful widget on that page. While it does work, it appears I don't have access to the actual context.

Is there a way in Flutter to access the context in the initState() method?

If not, is there another way to achieve this behaviour in a better way?

@override
void initState() {
  super.initState();
  new Future.delayed(Duration.zero, () {
    showDialog(context: context, 
    builder: (BuildContext context) {
      return new Container(child: new Text('foo'));
    });
  });
}
TommyF
  • 6,660
  • 8
  • 37
  • 61
  • You have access to `context` in the `initState` method. And the code you linked actually work – Rémi Rousselet Aug 09 '18 at 13:52
  • "While it does work, it appears I don't have access to the actual context.".. How are you telling this ? – Dinesh Balasubramanian Aug 09 '18 at 14:37
  • Maybe I misinterpreted the problem, my Widgets didn't have the Theme applied and looked horrific with this code, maybe the context is there but I have to get the Theme for the context or something? I'll check that out when I get back home. – TommyF Aug 09 '18 at 15:41
  • Minor point, but you probably don't need the `Container`. – Duncan Jones Sep 23 '18 at 08:10

6 Answers6

45

For newcomers, you have access to context in initState as others said. The only problem here in initState build function is not executed yet. So this context is kind of empty.

To solve this, you can use SchedulerBinding:

SchedulerBinding.instance.addPostFrameCallback((_) => doSomethingNextFrame(context));

addPostFrameCallback: Schedule a callback for the end of this frame.

Future.delayed(Duration.zero) will also work. But I think SchedulerBinding looks cleaner.

Sait Banazili
  • 1,263
  • 2
  • 12
  • 27
15

While this is most certainly not the smoothest way, but you can make a function that displays the dialog after a short duration, when everything is built already. It would look something like this:

void initState() {
    super.initState();
    _showDialog();
  }

_showDialog() async {
    await Future.delayed(Duration(milliseconds: 50));
    showDialog(
        context: context,
        builder: (BuildContext context) {
          return new Container(child: new Text('foo'));
        });
  }
leodriesch
  • 5,308
  • 5
  • 30
  • 52
  • @SebastianRoth I'm not entirely sure about that, but I've read that async calls or Future's invoked in the initState() method get delayed until after the initState() call. The Dart VM/Flutter engine does that. So you could even leave out the Future.delayed() call, just speculating tho, not tested. – leodriesch May 23 '19 at 17:40
13
@override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      await showDialog<String>(
        context: context,
        builder: (BuildContext context) => new showDialog(
           context: context, 
           builder: (BuildContext context) {
              return new Container(child: new Text('foo'));
          });
      );
    });
  }
Codedman
  • 233
  • 2
  • 10
2

You have the proper context. But you should use some Dialog widgets to get the proper dialog.

showDialog(context: context, builder: (context) {
   return new SimpleDialog(children: <Widget>[new Center(child: new Container(child: new Text('foo')))]);
});

you can find more dialog widgets here and here

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Dinesh Balasubramanian
  • 20,532
  • 7
  • 64
  • 57
2

You can use after_layout for more readable:

xxx with AfterLayoutMixin

@override
void initState() {
  super.initState();
  // don't need write code here
}

@override
void afterFirstLayout(BuildContext context) {
  // write code here
  _showDialog();
}

It's source code is very simple:

mixin AfterLayoutMixin<T extends StatefulWidget> on State<T> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.endOfFrame.then(
      (_) {
        if (mounted) afterFirstLayout(context);
      },
    );
  }

  FutureOr<void> afterFirstLayout(BuildContext context);
}

So you can use WidgetsBinding.instance.endOfFrame.then directly without import after_layout if you want.

无夜之星辰
  • 5,426
  • 4
  • 25
  • 48
-1

This work using a key in your method build widget.

First create the key:

  final GlobalKey<NavigatorState> key =
  new GlobalKey<NavigatorState>();

After we bind with our widget:

  @override
  Widget build(BuildContext context) {
    return Scaffold(key:key);
  }

Finally we use the key calling .currentContext parameter.

    @override
      void initState() {
        super.initState();
        SchedulerBinding.instance.addPostFrameCallback((_) {
            // your method where use the context
            // Example navigate:
            showDialog(key.currentContext); 
        });
   }

Happy coding.

Pedro Molina
  • 1,101
  • 12
  • 12