我有一个在 flutter WEB 中运行的应用程序。我需要打开几个屏幕,例如侧面菜单。
我尝试将屏幕作为对话框打开,效果很好。 但我需要打开一个对话框,并且在对话框打开时仍然与背景屏幕中的元素进行所有交互。模态屏障防止与背景的相互作用。我现在该怎么做?有人可以帮我吗?
请不要建议使用堆栈!我有大约 40 个对话框/模式要打开,所以堆栈对我没有帮助。
为了实现与后台的持续交互并避免 ModalBarrier,您首先需要在常规构建方法之外构建
dialog
,使其独立于小部件树。
您可以通过使用
Overlay
小部件来实现此目的,该小部件允许您将任何小部件覆盖在应用程序内的另一个小部件之上。
还要在 Overlay
处定义 app level
小部件,使其可全局访问并独立于正在构建的特定类或页面。这还将使您能够在对话框仍然打开的情况下导航不同的页面。
由于
Overlay
小部件是独立的,因此需要使用 markNeedsBuild
进行重建以允许布局构建器,因此我们调用 _overlayEntry?.markNeedsBuild();
来触发重建。
您可以在我的回答中找到一个简单的实现:在从一个屏幕移动到另一个屏幕时,颤动中是否有任何方法可以保留对话框?
(I would also appreciate some credit on there)
但是,在您的情况下,由于您说您有 40 多个对话框,因此编码更具挑战性。 要有效管理多个对话框,您需要使用
list of overlays
并能够在关闭时调用正确的对话框。我通过将列表与标识符计数器结合使用来实现这一点
static List<_DialogEntry> _dialogEntries = [];
static int _dialogIdCounter = 0;
int dialogId = _dialogIdCounter++;
计数器确保每个对话框都是唯一标识的,这样您就可以避免有关
markNeedsBuild
的某些错误。
然后您可以使用 OverlayEntry 构造每个对话框:
OverlayEntry(builder: (context) {
return _DialogWidget(
dialogId: dialogId,
title: title,
onDispose: () {
_dialogEntries.removeWhere((entry) => entry.dialogId == dialogId);
},
);
});
_dialogEntries.add(_DialogEntry(
dialogId: dialogId,
overlayEntry: newOverlayEntry,
));
_DialogWidget
是主要存储对话框标识符dialogId
的类模型,并提供了删除/处置右侧对话框的方法。
请参阅此演示以了解上述代码在完整工作示例中的工作原理(或展开下面的代码片段):
https://dartpad.dev/?id=67693f986403359a98081f130829ddcb
另请注意,在演示中,我注释掉了
Positioned
小部件,它可用于将每个对话框放置在不同的位置。您需要对其进行进一步的调整并尝试以达到您想要的对话位置,我的只是一个基本示例。
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
void main() {
runApp(MyApp());
}
class OverlayDialog {
static List<_DialogEntry> _dialogEntries = [];
static int _dialogIdCounter = 0;
static void show(BuildContext context, String title) {
int dialogId = _dialogIdCounter++;
OverlayEntry newOverlayEntry = OverlayEntry(builder: (context) {
// double position = dialogId * 50.0;
return
//Positioned(
// top: position,
// child:
_DialogWidget(
dialogId: dialogId,
title: title,
onDispose: () {
_dialogEntries.removeWhere((entry) => entry.dialogId == dialogId);
},
// ),
);
});
_dialogEntries.add(_DialogEntry(
dialogId: dialogId,
overlayEntry: newOverlayEntry,
));
Overlay.of(context).insert(newOverlayEntry);
}
static void close(int dialogId) {
final entry =
_dialogEntries.firstWhereOrNull((entry) => entry.dialogId == dialogId);
entry?.overlayEntry.remove();
}
}
class _DialogEntry {
final int dialogId;
final OverlayEntry overlayEntry;
_DialogEntry({required this.dialogId, required this.overlayEntry});
}
class _DialogWidget extends StatefulWidget {
final int dialogId;
final String title;
final VoidCallback onDispose;
_DialogWidget(
{required this.dialogId, required this.title, required this.onDispose});
@override
_DialogWidgetState createState() => _DialogWidgetState();
}
class _DialogWidgetState extends State<_DialogWidget> {
@override
Widget build(BuildContext context) {
return Center(
child: Dialog(
backgroundColor: Colors.grey,
child: Container(
constraints: BoxConstraints(
minHeight: 80.0,
maxHeight: MediaQuery.of(context).size.height,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(widget.title),
Text('content'),
ElevatedButton(
onPressed: () {
OverlayDialog.close(widget.dialogId);
},
child: const Text('Close Dialog'),
),
],
),
),
),
);
}
@override
void dispose() {
widget.onDispose();
super.dispose();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
home: ScreenA(),
);
}
}
class ScreenA extends StatelessWidget {
void openDialogFunction(
{required BuildContext context, required String title}) {
OverlayDialog.show(context, title);
}
Widget kEButton(
{required BuildContext context,
required String title,
bool showDialog = true}) {
return Padding(
padding: const EdgeInsets.all(3),
child: ElevatedButton(
onPressed: !showDialog
? () {
print(title);
}
: () {
openDialogFunction(context: context, title: title);
},
child: Text(title),
));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Overlay Dialog Demo'),
),
body: Center(
child: Column(
children: [
kEButton(context: context, title: 'Dialog 1'),
kEButton(context: context, title: 'Dialog 2'),
kEButton(context: context, title: 'Dialog 3'),
kEButton(context: context, title: 'Print test', showDialog: false),
],
),
),
);
}
}