37

I call Navigator pushReplacement to show a new view within my flutter app and want to immediately pop up a simple dialog to introduce the page to the user. (I want the user to be able to see the new view in the background)

If I call showDialog within the build method of a widget, and then subsequently return a widget (scaffold) from the build method I get errors stating that flutter is already drawing a widget. I expect I need to listen on a build complete event and then call showDialog.

Guidance on how to do that much appreciated.

Tom
  • 372
  • 1
  • 3
  • 6

4 Answers4

94

You can call the dialog from inside 'initState()' dalaying its appearance after the first frame has been drawn.

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      await showDialog<String>(
        context: context,
        builder: (BuildContext context) => new AlertDialog(
              title: new Text("title"),
              content: new Text("Message"),
              actions: <Widget>[
                new FlatButton(
                  child: new Text("OK"),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
              ],
            ),
      );
    });
  }

The context variable is always available inside the State class. It points to the RenderObject of this widget. The problem is that in initState() the context is not yet created so you have to defer its usage after the first frame has been laid out. Then it is available.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
chemamolins
  • 19,400
  • 5
  • 54
  • 47
  • how did you get the context in initState() ? – Sherin Binu Sep 01 '18 at 05:25
  • 8
    The context variable is always available inside the State class. It points to the RenderObject of this Widget. The problem is that in initState() the context is not yet created so you have to defer its usage after the first frame has been laid out. Then it is available. – chemamolins Sep 01 '18 at 08:08
  • 2
    Is it possible to achieve the same with a stateless widget? – user716468 May 26 '20 at 00:45
9

Another way of doing it is using Timer.run(...)

@override
void initState() {
  super.initState();

  // simply use this
  Timer.run(() {
    showDialog(
      context: context,
      builder: (_) => AlertDialog(title: Text("Dialog title")),
    );
  });
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
2

If anyone after a solution to display dialog based on the widget value update.

For example, let users know the screen they are trying to view, has been deleted.

if (xxx.timestamps.deleted == null) {
  return DetailView(xxx: xxx);
} else {
  return FutureBuilder(
    future: Future.delayed(
      Duration.zero,
      () => showDialog(
          context: context,
          builder: (_) => ActionDialog(
              title: Text('xxx Deleted'),
              content: Text('xxx deleted'),
              confirmActions: [DialogConfirm.Ok])).then(
        (value) => Navigator.pop(context),
      ),
    ),
    builder: (context, _) => DetailView(xxx: xxx),
  );
}

kudos to https://stackoverflow.com/a/64017240/2641128

e-j5
  • 1,759
  • 1
  • 11
  • 23
0

Change your code

Widget build(BuildContext context) {    
    
    Future.delayed(Duration.zero, () => showDialog(context));
    
    return Scaffold();
    }
Muhammad Waleed
  • 2,517
  • 4
  • 27
  • 75