'How to close specific Dialog

I am opening a dialog from another dialog and trying to close the 1st dialog, but it is closing the recent dialog. Similar kind of git issue.

I've tried

  • putting ValueKey on AlertDialog
  • using rootNavigator:true while pop
  • keeping context into variable and doing Navigator.of(specifiqContext).pop()

But none of them is working. Code to reproduce the issue on dartPad.

class MultiDialogTest extends StatefulWidget {
  const MultiDialogTest({Key? key}) : super(key: key);

  @override
  State<MultiDialogTest> createState() => _MultiDialogTestState();
}

class _MultiDialogTestState extends State<MultiDialogTest> {
  BuildContext? dialog1Context, dialog2Context;

  Future<void> _showDialog1(BuildContext context) async {
    await showDialog(
        context: context,
        barrierDismissible: false,
        builder: (c) {
          dialog1Context = c;
          return AlertDialog(
            key: const ValueKey("dialog 1"),
            title: const Text("Dialog 1"),
            content: ElevatedButton(
              child: const Text("close dialog2"),
              onPressed: () {
                if (dialog2Context != null) {
                  Navigator.of(dialog2Context!,).pop();
                }
              },
            ),
            actions: [
              ElevatedButton(
                child: const Text("close this"),
                onPressed: () {
                  Navigator.of(c, rootNavigator: true).pop();
                },
              ),
            ],
          );
        });

    dialog1Context = null;
  }

  Future<void> _showDialog2(BuildContext context) async {
    await showDialog(
        context: context,
        barrierDismissible: false,
        builder: (c) {
          dialog2Context = c;
          return AlertDialog(
            key: const ValueKey("dialog 2"),
            title: const Text("Dialog 2"),
            actions: [
              ElevatedButton(
                child: const Text("close this"),
                onPressed: () {
                  Navigator.of(c, rootNavigator: true).pop();
                },
              ),
            ],
            content: Column(
              children: [
                ElevatedButton(
                  onPressed: () async {
                    await _showDialog1(context);
                  },
                  child: const Text("Open dialog 1"),
                ),
              ],
            ),
          );
        });
    dialog2Context = null;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            _showDialog2(context);
          },
          child: const Text("show dialog 2"),
        ),
      ),
    );
  }
}

How can I close bellow Dialog(Dialog 2) without closing above(Dialog 1).

I don't like to close both and reopen the Dialog 1.

enter image description here



Solution 1:[1]

Create a separate context and pass the correct context which one you want to close to the Navigator.pop(yourContextThatYouWishToClose)

Navigator.pop(dialogContext);

Here is the example code.

BuildContext dialogContext; // <<----
  showDialog(
    context: context, // <<----
    barrierDismissible: false,
    builder: (BuildContext context) {
      dialogContext = context;
      return Dialog(
        child: new Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            new CircularProgressIndicator(),
            new Text("Loading"),
          ],
        ),
      );
    },
  );

  await _longOperation();
  Navigator.pop(dialogContext);

Solution 2:[2]

You need to pass context of the dialog you want to close (parentContext) and call:

Navigator.pop(parentContext); // close parent 
Navigator.pop(context); // close current 

Solution 3:[3]

What you could do is pop twice in showDialog1 and then await for showDialog1 immediately.

import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: const MultiDialogTest(),
    );
  }
}

 
class MultiDialogTest extends StatefulWidget {
  const MultiDialogTest({Key? key}) : super(key: key);

  @override
  State<MultiDialogTest> createState() => _MultiDialogTestState();
}

class _MultiDialogTestState extends State<MultiDialogTest> {

  Future<void> _showDialog1(BuildContext context) async {
    await showDialog(
        context: context,
        barrierDismissible: false,
        builder: (c) {
          return AlertDialog(
            key: const ValueKey("dialog 1"),
            title: const Text("Dialog 1"),
            content: ElevatedButton(
              child: const Text("close dialog2"),
              onPressed: () async {
                  Navigator.of(context).pop();
                  Navigator.of(context).pop();
                  await _showDialog1(context);
              },
            ),
            actions: [
              ElevatedButton(
                child: const Text("close this"),
                onPressed: () {
                  Navigator.of(c).pop();
                },
              ),
            ],
          );
        });
  }

  Future<void> _showDialog2(BuildContext context) async {
    await showDialog(
        context: context,
        barrierDismissible: false,
        builder: (c) {
          return AlertDialog(
            key: const ValueKey("dialog 2"),
            title: const Text("Dialog 2"),
            actions: [
              ElevatedButton(
                child: const Text("close this"),
                onPressed: () {
                  Navigator.of(c).pop();
                },
              ),
            ],
            content: Column(
              children: [
                ElevatedButton(
                  onPressed: () async {
                    await _showDialog1(context);
                  },
                  child: const Text("Open dialog 1"),
                ),
              ],
            ),
          );
        });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            _showDialog2(context);
          },
          child: const Text("show dialog 2"),
        ),
      ),
    );
  }
}

Solution 4:[4]

When you use showDialog what happens under the hood is a simple push on the Navigator, this will add the Dialog on the top of the current Navigator as a new Route.

All the pop methods in Navigator simply pop from the topmost route so this is not easily feasible.

A dirty hack may be to pop twice and show again the first dialog like in this sample that works in your dartpad sample

onPressed: () {
  if (dialog2Context != null) {
       Navigator.of(dialog2Context!).pop();
       Navigator.of(dialog2Context!).pop();
       _showDialog1(context);
  }
},

In my opinion, having a dialog spawning another dialog its not the best UX you can provide to your user, but you can always check which routes are involved by using the inspector:

https://docs.flutter.dev/development/tools/devtools/inspector

in this case, you can quickly check that the dialog will be always on top (in this case the latest of the tree), the proper way to fix this should be to create several navigators and decide which one to use for showing your dialog, but that will complexity a lot of your code!

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Anandh Krishnan
Solution 2 Qays Dwekat
Solution 3 Kantine
Solution 4