Flutter — показывать только одно диалоговое окно одновременно

#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;
  }
 

что-то вроде этого