Показать диалог, но без ModalBarrier

У меня есть приложение, работающее в Flutter WEB. Мне нужно несколько экранов, чтобы они открывались как боковое меню.

Я попробовал открыть экраны в виде диалога, и все работает нормально. Но мне нужно открыть диалоговое окно и сохранить все взаимодействия с элементами фонового экрана, пока диалоговое окно открыто. Модальный барьер предотвращает взаимодействие с фоном. Как мне это сделать сейчас? Кто-нибудь может мне помочь?

Пожалуйста, не предлагайте использовать стек! Мне нужно открыть около 40 диалогов/модальных окон, поэтому стек мне не поможет.

Привет, мой пост ниже ответил на твой вопрос?

Texv 29.04.2024 19:02
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
86
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы добиться непрерывного взаимодействия с фоном и избежать 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),
          ],
        ),
      ),
    );
  }
}

Спасибо, это работает. Я очень ценю ваши усилия в этом направлении!

Shrijan Regmi 30.04.2024 15:03

Другие вопросы по теме

Похожие вопросы

Flutter ReorderableListView не работает идеально в прокручиваемом родительском элементе
Во флаттере, как установить местоположение метки для DropdownMenu
Дизайн текстового поля Flutter: как создать текстовое поле с иконкой и радиусом границы
Является ли нулевая поддержка безопасности теперь (более или менее) обязательной для дартса?
W/StorageUtil(9633): ошибка при получении токена проверки приложения; вместо этого используйте токен-заполнитель
Стратегия Flutter Web в Url Launcher
Значение типа «Future<dynamic>» не может быть возвращено из метода «build», поскольку оно имеет тип возвращаемого значения «Виджет»
Читать все SMS с Android
Go_router onExit, как сделать пример из документации нулевым безопасным
Как настроить обратный вызов перенаправления `go_router`, чтобы пользователи могли использовать приложение без входа в систему, но при входе в систему перенаправляться при доступе к странице входа?