How to dismiss a flutter dialog automatically, and navigate back?

2k views Asked by At

I'm working on a website made in Flutter. I want that after some action is completed successfully, a dialog is displayed, it closes automatically, and it goes back to the previous page. My problem is that I can't make the following error disappear.

Error: Looking up a deactivated widget's ancestor is unsafe.

This is my code.

showDialog<void>(
        context: context,
        barrierDismissible: false, // user must tap button!
        builder: (BuildContext buildContext) {
          Timer(const Duration(milliseconds: 2000), () {
            Navigator.of(buildContext).pop();
            context.router.navigateBack();
          });
          return AlertDialog(
            title: const Text(AppGlobals.invalidFieldsText),
            content: const SingleChildScrollView(
                child: Text(AppGlobals.invalidFieldsDescriptionText)),
          );
        },
      );

If instead of closing the dialog automatically, I use a button to trigger, the error disappears.

showDialog<void>(
        context: context,
        barrierDismissible: false, // user must tap button!
        builder: (BuildContext buildContext) {
          return AlertDialog(
            title: const Text(AppGlobals.invalidFieldsText),
            content: const SingleChildScrollView(
                child: Text(AppGlobals.invalidFieldsDescriptionText)),
            actions: <Widget>[
              TextButton(
                child: const Text(AppGlobals.closeText),
                onPressed: () {
                  Navigator.of(buildContext).pop();
                  context.navigateBack();
                },
              ),
            ],
          );
        },
      );

I tried to find the solution here, here, here, here, and here but I couldn't solve the problem. please help.

3

There are 3 answers

5
gretal On

You might use Future.delayed() method to close the dialog automatically and Navigator.of(context).pop() to go back to the previous page.

// Show dialog
showDialog(
  context: context,
  barrierDismissible: false,
  builder: (BuildContext buildContext) {
    Future.delayed(const Duration(seconds: 2), () {
      Navigator.of(buildContext).pop(); // close the dialog
      Navigator.of(context).pop(); // navigate back to the previous page
    });
    return AlertDialog(
      title: const Text(AppGlobals.invalidFieldsText),
      content: const SingleChildScrollView(
        child: Text(AppGlobals.invalidFieldsDescriptionText),
      ),
    );
  },
);
1
flutterWithChris On

You should use Navigator.popUntil(context, ModalRoute.withName('/')). Replace the '/' with your route name & voila!

Be careful, when you were calling this Navigator.of(buildContext).pop(), that passes the build context of the dialog builder; Not the root app context.

Key difference is using the default lowercase context which always refers to the app's main context. You were also trying to navigate again after your first .pop() which isn't possible because the widget (dialog) will close & will no longer be mounted.

1
krishnaacharyaa On

It is because the widget tree has already been disposed of when the dialogue is supposed to be closed automatically after a predetermined length of time, which prevents Flutter from going back to the previous page.

Hence you can consider using a StatefulWidget to display the dialog and use the dispose function to stop the timer when the widget is removed.

Example:

class MyPage extends StatefulWidget {
  @override
  _MyPageState createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  Timer? _timer;

  void _showDialog() {
    showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        _timer = Timer(const Duration(seconds: 2), () {
          Navigator.of(context).pop();
          Navigator.of(context).pop(); // navigate back
        });
        return AlertDialog(
          title: const Text('Dialog'),
          content: const Text('Dialog content'),
        );
      },
    );
  }

  @override
  void dispose() {
    _timer?.cancel(); // cancel timer when widget is removed
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: ElevatedButton(
          onPressed: _showDialog,
          child: const Text('Show Dialog'),
        ),
      ),
    );
  }
}