13
votes

In flutter, we want to overlay a dialog above the widget.

We were able to display the dialog after pushing the button.

However, we want to display that dialog at the timing of displaying the widget that likes loading dialog.

We implemented as follows.

import 'package:flutter/material.dart';

class XxxxxWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // [NG]We want to show dialog on Container widget.
    // showMyDialog(context);
    return Container(
      child: FlatButton(
        child: Text('Show'),
        onPressed: () {
          // [OK]We can show dialog.
          showMyDialog(context);
        },
      ),
    );
  }

  void showMyDialog(BuildContext context) {
    showDialog<bool>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          content: const Text(
            'Message',
          ),
          actions: <Widget>[
            FlatButton(
              child: const Text('OK'),
              onPressed: () {
                Navigator.of(context).pop(true);
              },
            ),
          ],
        );
      },
    );
  }
}

When we use [NG] code, the following error occurs.

I/flutter (28618): When the exception was thrown, this was the stack:
I/flutter (28618): #0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3436:11)
I/flutter (28618): #1      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3462:6)
I/flutter (28618): #2      State.setState (package:flutter/src/widgets/framework.dart:1141:14)
I/flutter (28618): #3      OverlayState.insertAll (package:flutter/src/widgets/overlay.dart:301:5)
I/flutter (28618): #4      OverlayRoute.install (package:flutter/src/widgets/routes.dart:40:24)
I/flutter (28618): #5      TransitionRoute.install (package:flutter/src/widgets/routes.dart:182:11)
I/flutter (28618): #6      ModalRoute.install (package:flutter/src/widgets/routes.dart:740:11)
I/flutter (28618): #7      NavigatorState.push (package:flutter/src/widgets/navigator.dart:1443:11)
I/flutter (28618): #8      showDialog (package:flutter/src/material/dialog.dart:642:53)
I/flutter (28618): #9      XxxxxWidget.showMyDialog (package:xxxxx/Widgets/xxxxx_widget.dart:20:5)
I/flutter (28618): #10     XxxxxWidget.build (package:xxxxx/Widgets/xxxxx_widget.dart:7:5)
[abridgement]

We also tried FutureBuilder but could not solve this problem.

How should we solve this problem?

$ flutter doctor -v
[✓] Flutter (Channel beta, v0.5.1, on Mac OS X 10.13.6 17G65, locale ja)
    • Flutter version 0.5.1 at /Applications/flutter
    • Framework revision c7ea3ca377 (3 months ago), 2018-05-29 21:07:33 +0200
    • Engine revision 1ed25ca7b7
    • Dart version 2.0.0-dev.58.0.flutter-f981f09760

[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
    • Android SDK at /Users/xxxxx/src/android-sdks
    • Android NDK at /Users/xxxxx/src/android-sdks/ndk-bundle
    • Platform android-27, build-tools 27.0.3
    • ANDROID_HOME = /Users/xxxxx/src/android-sdks
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01)
    • All Android licenses accepted.

[✓] iOS toolchain - develop for iOS devices (Xcode 9.4.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 9.4.1, Build version 9F2000
    • ios-deploy 1.9.2
    • CocoaPods version 1.5.3

[✓] Android Studio (version 3.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 27.1.1
    • Dart plugin version 173.4700
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b01)

[!] VS Code (version 1.25.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected devices (1 available)
    • ASUS Z017DA • XXXXXXXXXXXXXXX • android-arm64 • Android 8.0.0 (API 26)

! Doctor found issues in 1 category.

We already installed Flutter extension. But flutter doctor said not installed. That is not important for this question.

4
"• Flutter extension not installed; install from" is a known issue in this Flutter version and safe to ignore.Günter Zöchbauer
Thank you for your reply. We already know about that. :)Ganessa
We are using the beta branch. It's a little old. Should we use a different version or master branch to release the application?Ganessa

4 Answers

39
votes

We have to display the dialog once done with building the widget. You can use Future.delayed function like below(I tested, it is working).

class XxxxxWidget extends StatelessWidget {

@override
Widget build(BuildContext context) {
// [NG]We want to show dialog on Container widget.

 Future.delayed(Duration.zero, () => showMyDialog(context)); // import 'dart:async';
 return Container(
  child: FlatButton(.... //same as question

Explaination:

As Dart is based on single-threaded event loop, when we create an async tasks, it will put those events in the end of the event queue and continue it's current execution. Please refer below example for more details,

void main() {
  print("first");
  Future(() => print("second"));
  print("third");
  Future(() => print("forth"));

}

Output will be

first
third
second
forth

It is very similar to

DispatchQueue.main.async {
 print("Async1") //printJob
}

Once done with building the widget display the dialog. Check out my answer for similar problem.

7
votes

some of this is ambiguous, but you can show an alert dialog in a widgets init method like this

Future showDialog() async {

 WidgetsBinding.instance.addPostFrameCallback((_) async {
  await showDialog<String>(
    context: context,
    builder: (BuildContext context) => AlertDialog(
          title: const Text('Title'),
          content: const Text('Content')
          actions: <Widget>[
            FlatButton(
              child: const Text('OK'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        ),
  );
});
}

this will wait for the first frame to be drawn before showing the dialog so you can call it from init

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

   showDialog();
}
0
votes

The issue you have its normal on Flutter so let's see the code.

I guess the problem is in the constructor for the Widget, need to be like this :

showMyDialog(BuildContext context) {
 showDialog(
  context: context,
  builder: (BuildContext context){
  return new AlertDialog(
    content: Text(
        'Message Here',
      ),
      actions: <Widget>[
        FlatButton(
          child: const Text('OK'),
          onPressed: () {
            Navigator.of(context).pop(true);
          },
        ),
      ],
   );
  }
 );
}

Try this

0
votes

'The Flutter Way' of doing this would look like this (assume you need to do this when rendering your main screen):

class MyWidgetWithDialogOnStartup extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: FutureBuilder(
        future: Future.delayed(Duration.zero, () => showMyDialog(context)),
        builder: (context, snapshot) {
          return WhateverMyWidgetReallyIs();
        },
      )
    );
  }
}

showMyDialog(context) wraps the native showDialog call.