How to close specific Dialog
Asked Answered
P

5

6

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

Poky answered 17/2, 2022 at 9:7 Comment(6)
i am afraid you cannot do thatKimura
@Kimura Can you provide something that explain the situation? I am trying to understand flutter.Poky
basically showDialog uses NavigatorState.push method under the hood and as far i know you cannot remove the route from the middle of the navigator stackKimura
the only way you could do it is to use NavigatorState.removeRoute method but you would need to know what Route to removeKimura
Seems complicated, While looking for this I found : NavigatorState doesn't expose an API for getting the path of the current route,Poky
thats why you would need to create some class like DialogHelper that has method like showDialog that keeps track of shown dialogs and deismissDialog that uses NavigatorState.removeRoute under the hoodKimura
J
9

I see that there's still no answer so here's what I did:

void showMyDialog(BuildContext context, Widget dialogContent) async {
  var myDialogRoute = DialogRoute(
    context: context,
    builder: (BuildContext context) {
      return Dialog(child: dialogContent);
    },
  );

  /// push the dialog route
  Navigator.of(context).push(myDialogRoute);

  /// do your work here
  await Future.delayed(2.seconds);

  /// ensure the route is still active to avoid removing a route that doesn't
  /// exist causing Navigator error
  if (myDialogRoute.isActive) {
    /// whenever you want, remove the specific route even if other dialogs
    /// are spawned above or whatever
    Navigator.of(context).removeRoute(myDialogRoute);
  }
}

you can also return the myDialogRoute and use it remotely. Just make sure you dismiss the dialog before disposing the context.

Jecho answered 20/4, 2023 at 10:8 Comment(0)
D
2

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);
Dahle answered 17/2, 2022 at 9:26 Comment(3)
I think I've passed the correct context. Can you include the full widget?Poky
Thanks but it doesnt answer the questionPoky
This may only work if you have different navigator resorting to different context Since we are in the same Navigator (provided from MaterialApp) in this case it won't change, the pop will always remove the topmost independently from the context you pass as parameterMillimeter
R
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 
Remunerative answered 17/2, 2022 at 9:34 Comment(3)
Can you include the full widget?Poky
Since we are in the same Navigator (provided from MaterialApp) in this case it won't change, the pop will always remove the topmost independently from the context you pass as parameterMillimeter
Thanks and yes, that's the issue I like to overcome without closing and reopening the dialog.Poky
N
-1

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"),
        ),
      ),
    );
  }
}
Norge answered 17/2, 2022 at 9:36 Comment(0)
M
-1

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

Millimeter answered 17/2, 2022 at 13:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.