Сценарий

Иногда вы хотите, чтобы пользователи получили больше информации об элементе, представленном на текущем экране, но не хотите, чтобы пользователи переходили на другой экран. Он слишком тяжелый и может отвлекать внимание пользователя.

Вместо перехода к другому экрану сведений для этого можно использовать различные компоненты пользовательского интерфейса: для отображения всплывающего диалогового окна или для перемещения на нижней панели листа. Любой способ хорош; однако, чтобы закрыть диалоговое окно или нижнюю панель листа, необходимо дополнительное взаимодействие с пользователем. Пользователи должны нажать кнопку ОК или явно сдвинуть панель.

Лучшим способом было бы долгое нажатие в Instagram на экране сетки фотографий. В этом случае Instagram использует эту реализацию для отображения фотографии большего размера, позволяя пользователям видеть большую фотографию напрямую, не покидая текущего экрана. Когда палец пользователя покинет экран, большая фотография исчезнет.

Поток поведения будет похож на диаграмму ниже:

Проблема

Как во Flutter добиться такого же поведения? Если вы немного знакомы с кодированием Flutter, нетрудно узнать, что GestureDetector можно использовать для обработки поведения при длительном нажатии с его onLongPress обратным вызовом.

Однако при попытке обработать onLongPressEnd обратный вызов в том же GestureDetector вы обнаружите, что этот обратный вызов никогда не вызывается. Это потому, что когда отображается диалог, GestureDetector прерывается; вы больше не сможете получать события жестов.

Чтобы иметь возможность обрабатывать как onLongPress, так и onLongPressEnd, нам нужно обернуть всплывающий пользовательский интерфейс в другой виджет Flutter - Overlay.

Overlay и OverlayEntry

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



Чтобы использовать Overlay, мы должны заключить виджет, который мы хотим использовать, в OverlayEntry. Записи оверлея вставляются в оверлей с помощью функций OverlayState.insert или OverlayState.insertAll. Чтобы найти ближайший охватывающий оверлей для данного BuildContext, используйте функцию Overlay.of, например,

Overlay.of(context).insert(some_created_overlay_entry);


Реализация

Сначала мы пишем пример приложения флаттера с представлением сетки изображений в нем. Функция _createGridTileWidget(String url) используется для преобразования URL-адреса изображения в виджет, который будет отображаться в виде плитки сетки. В строке 82–89 вы можете увидеть, что GestureDetector создан. В onLongPress() создается всплывающее диалоговое окно, которое вставляется в Overlay, чтобы вы могли видеть, что диалоговое окно отображается.

Чтобы скрыть диалоговое окно, когда палец пользователя покидает экран, в строке 89 диалоговое окно удаляется из Overlay, чтобы его можно было скрыть для пользователя.

Чтобы вести себя как Instagram, некоторые анимации добавляются во всплывающее диалоговое окно. AnimatedDialog написано для обработки части анимации; и он завернут внутрь OverlayEntry. Настоящее содержание содержится внутри _createPopupContent(String url).

Что касается содержания диалогов, в этом нет ничего особенного. Вы можете проверить приведенный ниже код, чтобы узнать, как он устроен.

Демо

А теперь вот результат работы демонстрационного приложения:

Ссылка

Если вы хотите увидеть все коды демонстрационных приложений, вы можете обратиться к проекту Github ниже: