1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Flutter-vp.md 12 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Небольшие хитрости Flutter: реализация OverlayPortal с ограниченностью и совместимостью страниц

Вероятно, вам знакомо понятие 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` связан с текущей страницей, он будет "непосредственно уничтожен". Это демонстрирует ограниченность этого компонента:![image](http://img.cdn.guoshuyu.cn/20241107_vp/image5.gif)

Не кажется ли вам это "волшебным"? Каким образом `OverlayPortal` может быть связан со своим родителем в плане состояния, но при этом оставаться независимым в аспектах слоев?

Это можно представить следующей схемой: он существует внутри дерева страниц как `_RenderLayoutSurrogateProxyBox`, но также "располагается и рисуется" в глобальном контексте `OverLay` через `_RenderDeferredLayoutBox`:

![image](http://img.cdn.guoshuyu.cn/20241107_vp/image6.png)

Каждый `OverlayPortal` имеет два реализованных объекта:

- Каждый `OverlayEntry` страницы хранит список `_sortedTheaterSiblings` типа `LinkedList<_OverlayEntryLocation>`.

- При каждом вызове `OverlayPortal` создается объект `_OverlayEntryLocation`, который является своего рода "слотом", и метод `OverlayPortal#overlayChildBuilder` добавляет этот `_OverlayEntryLocation` в `_sortedTheaterSiblings`.

- В конечном итоге, этот "слот" запускает обновление layout через `_theater._addDeferredChild(child)`.

![image](http://img.cdn.guoshuyu.cn/20241107_vp/image7.png)

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

![image](http://img.cdn.guoshuyu.cn/20241107_vp/image8.png)

Из точки зрения уровня:- В `Overlay`, `OverlayPortal` обычно располагается после ближайшего ему `OverlayEntry` (обычно это маршрут страницы) и перед следующим `OverlayEntry`, поэтому он может находиться в любом месте текущей страницы, не затмевая следующую страницу.  ![image](http://img.cdn.guoshuyu.cn/20241107_vp/image9.gif)


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

![image10.gif](http://img.cdn.guoshuyu.cn/20241107_vp/image10.gif)

Конечно, реализация этой анимации — это уже другая история: [«Шейдер для создания эффектной частицной анимации»](https://juejin.cn/post/7435659292868476964)

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master