#flutter #dart #dialog #flutter-web
#трепетание #dart #диалоговое окно #flutter-web
Вопрос:
Я работаю над приложением flutter, используя несколько диалоговых окон для нескольких целей. В нашем коде есть несколько случаев, когда пользователь может открыть диалоговое окно. Внутри этого диалогового окна есть несколько кнопок, которые также открывают другое диалоговое окно. В результате получается 2 диалоговых окна друг над другом и очень темный фоновый экран. Что мы хотели бы сделать, так это отображать только одно диалоговое окно одновременно. Как мы можем этого добиться?
Вот простой код, иллюстрирующий нашу проблему:
class MyScreen extends StatelessWidget {
@override
build(BuildContext context) {
return FlatButton(
child: Text('Button'),
onPressed: () async {
final resultDialog = await showDialog<ResultDialog1>(
context: context,
builder: (BuildContext context) => MyFirstDialog(),
);
// Do some stuff with the result, so this part of the tree cannot be destroyed
},
);
}
}
class MyFirstDialog extends StatelessWidget {
@override
build(BuildContext context) {
return FlatButton(
child: Text('Button in first dialog'),
onPressed: () async {
final resultDialog = await showDialog<ResultDialog2>(
context: context,
builder: (BuildContext context) => MySecondDialog(), // <- This will appear on top of the first dialog
);
// Do some stuff with the result, so this part of the tree cannot be destroyed
},
);
}
}
class MySecondDialog extends StatelessWidget {
@override
build(BuildContext context) {
return Text('Second Dialog');
}
}
Ответ №1:
позвольте мне дать вам виджет для этого
class MultiDialog extends StatefulWidget {
final Widget child;
const MultiDialog({Key key, this.child}) : super(key: key);
@override
_MultiDialogState createState() => _MultiDialogState();
static void addDialog(
{@required BuildContext context, @required Widget dialog}) {
assert(context != null, "the context cannot be null");
assert(dialog != null, "the dialog cannot be null");
context.findAncestorStateOfType<_MultiDialogState>()._addDialog(dialog);
}
static void remove({@required BuildContext context}) {
assert(context != null, "the context cannot be null");
context.findAncestorStateOfType<_MultiDialogState>()._remove();
}
}
class _MultiDialogState extends State<MultiDialog> {
final _allDialogs = <Widget>[];
void _addDialog<T>(Widget dialog) {
assert(dialog != null, "The dialog cannot be null");
setState(() {
_allDialogs.add(dialog);
});
}
void _remove() {
if (_allDialogs.isEmpty) {
print("No dialogs to remove");
return;
}
setState(() {
_allDialogs.removeLast();
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
widget.child,
if (_allDialogs.isNotEmpty) _allDialogs.last,
],
);
}
}
и когда вы хотите добавить диалоговое окно, просто вызовите
MultiDialog.addDialog(
context: context,
dialog: AlertDialog(),
);
вызов Navigator.pop
для удаления диалогового окна, если существует другое диалоговое окно, которое вы нажали, оно будет показано, вы можете дополнительно отобразить их все с результатами, PS: этот код не тестировался, дайте мне знать в комментариях, если это сработает для вас
вызов MultiDialog.remove(context:context)
, чтобы открыть видимое диалоговое окно и вернуть предыдущее диалоговое окно,
и если вы получаете сообщение об ошибке, что addDialog вызывается для null, это потому, что работает flutter, после MultiDialog
использования a Builder
для введения нового context
вызова use it showDialog
, PS: ПРИВЕДЕННЫЙ ВЫШЕ КОД ПРОТЕСТИРОВАН
Комментарии:
1. Мне не удалось заставить его работать. У меня ошибка
TypeError: Cannot read property 'Symbol(_addDialog)' of null
, и я думаю, это связано с тем, что виджет в диалоговом окне «Показать» не использует контекст, как предыдущий экран (и, следовательно, нетMultiDialog
) Также я чувствую, что этот код не закроет предыдущее диалоговое окно при добавлении нового, верно?2. да, я переработал предыдущий, проверьте новый рабочий код
Ответ №2:
я создал поток из событий, которые вызывают всплывающее диалоговое окно, и использовал rx darts exhaust map для ожидания результата (я уже использовал rxdart)
dialogEventStream
.exhaustMap((_) => maybeShowDialog().asStream())
.listen((_) {});
Future<bool> maybeShowOfflineModeDialog() async {
final isOfflineModeEnabled = await _sharedPreferencesService.isOfflineModeEnabled();
if (!isOfflineModeEnabled) {
final isLoginOffline = await _navigationService.showDialog(NoConnectionDialog());
if (isLoginOffline == true) {
await _sharedPreferencesService.setIsOfflineModeEnabled(isOfflineModeEnabled: true);
return await _navigationService.pushReplacement(AppShellOffline.routeName) ?? true;
} else {
return false;
}
}
return false;
}
что-то вроде этого