У меня есть приложение, работающее в 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,
));
_DialogEntry
— это модель класса, в которой в основном хранится идентификатор диалога dialogId
, а _DialogWidget
— это виджет с состоянием, который мы добавляем для каждого списка, который также предоставляет метод для удаления/удаления нужного диалога.
Обратитесь к этой демонстрации, чтобы понять, как приведенный выше код работает в полноценном рабочем примере (или разверните фрагмент кода ниже):
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),
],
),
),
);
}
}
Спасибо, это работает. Я очень ценю ваши усилия в этом направлении!
Привет, мой пост ниже ответил на твой вопрос?