Вероятно, вам знакомо понятие Overlay, а что насчет OverlayPortal
?
В Flutter можно добавлять "слои" в Overlay
, который находится внутри MaterialApp
, чтобы реализовать такие вещи, как глобальный плавающий компонент или навигационные указатели. Это связано с тем, что Overlay
в Flutter работает как менеджер слоев, имея внутреннюю структуру _Theater
(театр), где каждая «страница маршрута» по умолчанию добавляется в этот театр через OverlayEntry
.
Например, часто используемый Navigator
фактически использует Overlay
для отображения «маршрутных страниц», каждый открытый Route
по умолчанию добавляет OverlayEntry
в Overlay
, чтобы увеличивать количество слоев. Каждый OverlayEntry
независим друг от друга, что является одной из причин того, почему разные Route не влияют друг на друга.
Если вас интересует подробнее, вы можете прочитать старую статью «Расшифровка навигации Flutter и повышение производительности».
Таким образом, ранее мы обычно использовали Overlay
и OverlayEntry
для добавления новых слоев. А что такое OverlayPortal
?Фактически OverlayPortal
также используется для добавления слоев в Overlay
, но он сильно отличается от OverlayEntry
. Самое главное различие заключается в его возможности «работы со состоянием страниц» и «ограниченности количества страниц».Как мы уже говорили, поскольку каждый OverlayEntry
в Overlay
являются равноправными и «не влияют друг на друга», то при вызове нового OverlayEntry
B внутри страницы A, последняя не может напрямую использовать InheritedWidget для передачи различных состояний, так как новый OverlayEntry
B не относится к странице A, а является равноправным OverlayEntry
. Например, ниже Text('Hello')
не может передать состояние «соседнего» Theme
.
А вот OverlayPortal
действует по-другому, он позволяет «сохранять связь состояния с родителем», но при этом «оставаться независимым в структуре слоев», таким образом обеспечивая более простое управление «слоями экрана» внутри страницы, например, плавающими окнами или всплывающими окнами.
Встроенный в Flutter
OverlayPortal
был вдохновлен проектом flutter_portal, и был объединён в прошлом году в flutter/flutter#105335. Приведенный ниже пример кода показывает:- Внутри страницы определяетсяDefaultTextStyle
, который используется для совместного использования модифицированного глобального стиля текста (fontSize: 20
).
Добавлен OverlayPortal
с привязкой к OverlayPortalController
для управления отображением или скрытием.
В методе overlayChildBuilder
возвращается "подсказка", которая может случайным образом появиться в любом месте экрана.
Добавлено дочернее представление, которое отображает обычный текст Text
.
При нажатии на кнопку _tooltipController.toggle
используется для отображения и скрытия "подсказки".```dart
class ClickableTooltipWidgetState extends State {
final OverlayPortalController _tooltipController = OverlayPortalController();
final Random random = Random();
@override Widget build(BuildContext context) { return Container( height: 50, width: 300, decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(10)), child: TextButton( /// Pressing the OverlayPortalController to show and hide onPressed: _tooltipController.toggle, child: DefaultTextStyle( /// Sharing the DefaultTextStyle with a changed fontSize: 20 style: DefaultTextStyle.of(context).style.copyWith(fontSize: 20), /// Using OverlayPortal child: OverlayPortal( controller: _tooltipController, /// Adding layer through overlayChildBuilder overlayChildBuilder: (BuildContext context) { return Positioned( right: random.nextInt(200).toDouble(), bottom: random.nextInt(500).toDouble(), child: const ColoredBox( color: Colors.amberAccent, child: Text('Text that everyone wants to see'), ), ); }, /// Child element within the page child: const Text('Press to show/hide'), ), ), ), ); } }
Как можно заметить, при нажатии на центральную кнопку на экране, "подсказка" может случайным образом появляться и исчезать в любом месте экрана. Это значит, что каждый раз при нажатии на кнопку, подсказка будет появляться в случайном положении на экране.- "Подсказка" имеет свою собственную систему размещения и рисования, которая не зависит от ограничений размещения контейнера `Container` на странице, так как она добавлена в "независимый слой" `Overlay`.
- Стили "подсказки" наследуются от общего стиля `DefaultTextStyle`, поэтому они могут быть синхронизированы со стилем текущей страницы. Приведем ещё один пример: когда отображается текст "Подсказка" через `OverlayPortal`, закрыв страницу, мы видим, что поскольку `OverlayPortal` связан с текущей страницей, он будет "непосредственно уничтожен". Это демонстрирует ограниченность этого компонента:
Не кажется ли вам это "волшебным"? Каким образом `OverlayPortal` может быть связан со своим родителем в плане состояния, но при этом оставаться независимым в аспектах слоев?
Это можно представить следующей схемой: он существует внутри дерева страниц как `_RenderLayoutSurrogateProxyBox`, но также "располагается и рисуется" в глобальном контексте `OverLay` через `_RenderDeferredLayoutBox`:

Каждый `OverlayPortal` имеет два реализованных объекта:
- Каждый `OverlayEntry` страницы хранит список `_sortedTheaterSiblings` типа `LinkedList<_OverlayEntryLocation>`.
- При каждом вызове `OverlayPortal` создается объект `_OverlayEntryLocation`, который является своего рода "слотом", и метод `OverlayPortal#overlayChildBuilder` добавляет этот `_OverlayEntryLocation` в `_sortedTheaterSiblings`.
- В конечном итоге, этот "слот" запускает обновление layout через `_theater._addDeferredChild(child)`.

Еще раз рассмотрим это более подробно: **конечное расположение и рисование `OverlayPortal#overlayChildBuilder` выполняется через внутренний `_Theater` (театр) `Overlay`. Поэтому на этом уровне `OverlayPortal` похож на `OverlayEntry`, хотя сам `Overlay` участвует "косвенно" через "слоты" и остается частью дерева страниц**.

Из точки зрения уровня:- В `Overlay`, `OverlayPortal` обычно располагается после ближайшего ему `OverlayEntry` (обычно это маршрут страницы) и перед следующим `OverlayEntry`, поэтому он может находиться в любом месте текущей страницы, не затмевая следующую страницу. 
- Когда `OverlayEntry` имеет несколько связанных `OverlayPortals`, порядок их рисования определяется последовательностью вызова `overlayPortalController.show`. Из этого можно видеть, что **`OverlayPortal`** в основном предназначен для сценариев использования "глобальных слоев внутри страниц", так как он позволяет связывать состояние со своим родителем, но при этом обеспечивает независимость в структуре слоев. Применение **`OverlayPortal`** вместо **`OverlayEntry`** делает более гибкими различные сценарии отрисовки внутри страниц, такие как слои, руководства, а также реализацию переходных анимаций с помощью локальных слоев:

Конечно, реализация этой анимации — это уже другая история: [«Шейдер для создания эффектной частицной анимации»](https://juejin.cn/post/7435659292868476964)
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )