Search code examples
flutterdartdialogflutter-navigation

Flutter context error after multiple pops on dialogs


I have a function called from a button in one of my menu pages that builds an AlertDialog passing a context. The dialog contains a button that calls a function (called testFunction) that:

  • first disposes the current dialog using the passed context;
  • then creates a new loading dialog;
  • then calls an async function which, when done, disposes the current loading dialog and creates a new final dialog.

But it gives me this error when I try to build the loading dialog on the third step:

E/flutter ( 2550): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 2550): At this point the state of the widget's element tree is no longer stable.
E/flutter ( 2550): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.

The function called from the menu button:

static void buildDeckPurchaseDialog(BuildContext context) {
    showDialog(context: context, builder: (BuildContext context) {
      return AlertDialog(
        content: SizedBox(
          width: 80,
          height: 130,
          child: Center(
            MenuAnimatedButton(
              width: 110,
              height: 50,
              function: () => testFunction(context), // Executed on button tap
            ),
          ),
        ),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
        backgroundColor: kBoxColor,
      );
    });
  }

testFunction() called from the button in the dialog built from the previous function:

Future<dynamic> testFunction(BuildContext context) async {
  try {
    // Disposing the previous dialog
    Navigator.of(context).pop();
    // Showing loading dialog
    CustomDialogs.buildLoadingDialog(context, "Processing purchase...");

    // Making the async request
    return await FirebaseFunctions.instance.httpsCallable('test').call({
      'test': 1,
    }).then((value) {
      // Disposing the loading dialog
      Navigator.of(context).pop(); // <- ERROR HERE
      // Building the last dialog (which is not shown)
      CustomDialogs.buildSimpleDialog("End of function", context);
    }).onError((error, stackTrace) => Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const ErrorScreen())));
  } on FirebaseFunctionsException {
    Navigator.push(context, MaterialPageRoute(builder: (context) => const ErrorScreen()));
  }
}

I think I should use didChangeDependencies() method but I don't know how.


Solution

  • What I was doing wrong was giving the context passed to the buildDeckPurchaseDialog function the same name as the context created by the showDialog function builder (builder: (BuildContext context)). This way, testFunction(context) took the builder context as an argument and not the passed context.

    Then write the function like this:

    static void buildDeckPurchaseDialog(BuildContext passedContext) {
        showDialog(context: passedContext, builder: (BuildContext context) {
          return AlertDialog(
            content: SizedBox(
              width: 80,
              height: 130,
              child: Center(
                MenuAnimatedButton(
                  width: 110,
                  height: 50,
                  function: () => testFunction(passedContext), // Executed on button tap
                ),
              ),
            ),
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
            backgroundColor: kBoxColor,
          );
        });
      }