skip to Main Content

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

Answers


  1. 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),
          ),
        );
      },
    );
    
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
  3. 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'),
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search