showDialog 错误:对话框不是从 Flutter 中的 PopupMenuButton 触发的
showDialog bug: dialog isn't triggered from PopupMenuButton in Flutter
我无法让 showDialog
与 PopupMenuButton
一起工作。在下面的示例中,有一个触发对话框显示的按钮和一个弹出菜单也触发对话框显示。
该按钮有效,但在单击 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
并在 PopupMenuButton
的 onSelected
回调中检查它,如下所示:
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
作为值。
我无法让 showDialog
与 PopupMenuButton
一起工作。在下面的示例中,有一个触发对话框显示的按钮和一个弹出菜单也触发对话框显示。
该按钮有效,但在单击 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
并在 PopupMenuButton
的 onSelected
回调中检查它,如下所示:
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
作为值。