flutter 如何关闭特定对话框

xn1cxnb4  于 2023-04-22  发布在  Flutter
关注(0)|答案(5)|浏览(330)

我正在从另一个对话框打开一个对话框,并试图关闭第一个对话框,但它正在关闭最近的对话框。类似的git issue
我试过了

  • ValueKey放在AlertDialog
  • 使用rootNavigator:true while pop
  • context保存到变量中并执行Navigator.of(specifiqContext).pop()

但是它们都不起作用。在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"),
        ),
      ),
    );
  }
}

如何关闭下面的对话框(Dialog 2)而不关闭上面的对话框(Dialog 1)。
我不喜欢关闭两者并重新打开Dialog 1

yacmzcpb

yacmzcpb1#

你需要传递你想要关闭的对话框的上下文(parentContext)并调用:

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

xnifntxz2#

  • 创建一个单独的上下文,并将要关闭的上下文传递给Navigator.pop(yourContextThatYouWishToClose)*
    Navigator.pop(dialogContext);

下面是示例代码。

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);
w6mmgewl

w6mmgewl3#

我看到仍然没有答案,所以我这样做了:

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);
  }
}

你也可以返回myDialogRoute并远程使用它。只要确保在处理上下文之前关闭对话框。

chhqkbe1

chhqkbe14#

您可以在showDialog1中弹出两次,然后立即等待showDialog1

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"),
        ),
      ),
    );
  }
}
vyu0f0g1

vyu0f0g15#

当你使用showDialog时,在引擎盖下发生的事情是在导航器上的一个简单的推动,这将在当前Navigator的顶部添加对话框作为一个新的路由。
Navigator中的所有pop方法都只是从最顶层的路由中弹出,因此这并不容易实现。
一个肮脏的黑客可能是弹出两次,并再次显示第一个对话框一样,在这个例子中,工程在您的dartpad样本

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

在我看来,让一个对话框产生另一个对话框并不是你能为用户提供的最好的UX,但是你可以通过使用检查器来检查哪些路由涉及到:
https://docs.flutter.dev/development/tools/devtools/inspector
在这种情况下,你可以快速检查对话框是否总是在顶部(在这种情况下是树的最新部分),解决这个问题的正确方法应该是创建几个导航器,并决定使用哪个来显示你的对话框,但这会使你的代码变得复杂!

相关问题