showDialog 错误:对话框不是从 Flutter 中的 PopupMenuButton 触发的

showDialog bug: dialog isn't triggered from PopupMenuButton in Flutter

我无法让 showDialogPopupMenuButton 一起工作。在下面的示例中,有一个触发对话框显示的按钮和一个弹出菜单也触发对话框显示。

该按钮有效,但在单击 PopupMenu 中的 Alert 文本时,不会发生同样的情况。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text('showDialog bug'), actions: [
          PopupMenuButton(
              itemBuilder: (ctx) => [
                    PopupMenuItem(
                        child: Text('Alert'),
                        onTap: () {
                          showDialog(
                              context: context,
                              builder: (ctx) => AlertDialog(
                                    title: Text('test dialog'),
                                  ));
                        })
                  ])
        ]),
        body: ElevatedButton(
            onPressed: () {
              showDialog(
                  context: context,
                  builder: (ctx) => AlertDialog(
                        title: Text('test dialog'),
                      ));
            },
            child: Text('click me')));
  }
}

然而,当我在其后添加另一个 showDialog 块时,它开始工作。

showDialog(
context: context,
builder: (ctx) => AlertDialog(
      title: Text('test dialog'),
    ));

这不是错误。我记得,PopupMenuItem 的“onTap”回调调用 Navigator.pop 来关闭 Popup。在这种情况下,当您点击 PopupMenuItem 并调用“showDialog”时,对话框将立即弹出,并使 Popup 保持打开状态。

为了克服这个问题,您可以尝试这个解决方案:

PopupMenuItem(
             child: const Text('Alert'),
              onTap: () {
                Future.delayed(
                    const Duration(seconds: 0),
                    () => showDialog(
                          context: context,
                          builder: (context) => const AlertDialog(
                            title: Text('test dialog'),
                          ),
                        ));
              },
            )

有一种更简单的方法可以使用 PopupMenuButton 的实际功能来实现它。

只要给你一个 value PopUpMenuItem 并在 PopupMenuButtononSelected 回调中检查它,如下所示:

PopupMenuButton<String>(
  onSelected: (value) async {
    switch (value) {
      case 'open_dialog':
        return showDialog(...);
      default:
        throw UnimplementedError();
    }
  },
  itemBuilder: (context) => [
    const PopupMenuItem(
      child: Text('Open dialog'),
      value: 'open_dialog',
    ),
  ],
),

这样您将消耗 parent context,而不是 itemBuilder context,它会作为 在点击时弹出。然后决定在收到该值时执行什么,例如,打开对话框。

如果您想让它的类型安全,您也可以使用 enum 而不是 String 作为值。