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

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

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

Фреймворк выбора для интеграции Flutter — путеводитель по add-to-app

Эта статья является эксклюзивной публикацией сообщества Tuishe Juejin и запрещена для перепечатки в течение 14 дней, а после этого срока требует специального разрешения. Нарушение авторских прав будет преследоваться!

Это пятая часть серии "Фреймворк выбора для интеграции Flutter". Как было заявлено ранее, эта серия предназначена для быстрого выбора подходящих функциональных модулей для создания Flutter проекта или сборки его стартовой платформы. Это руководство, поэтому особенно полезно для новичков.

Это одно из немногих руководств по поддержке add-to-app в Flutter.

Изначально планировалось не затрагивать эту тему, так как смешанное развитие с использованием add-to-app всегда было проблемой для Flutter, но в настоящее время это непредотвратимый сценарий. Иногда даже возникают такие "странные" требования, как встраивание Flutter UI в RN. Поэтому, если все имеют такие потребности, давайте обсудим варианты таких проектов.

Add-to-appСначала почему add-to-app считается особенным в контексте Flutter? Потому что компоненты управления Flutter и маршруты отрисовываются через независимый FlutterEngine, который управляет отдельным механизмом рендеринга UI и стеком страниц для Flutter.Другими словами, Flutter представляет собой "одностраничное" приложение относительно платформы, используя пример, часто приводимый ранее:

  • В текущем стеке маршрутов Flutter есть два Flutter-маршрута: FlutterA и FlutterB;
  • Когда открывается новый Activity / ViewController, запускается native page X, которое добавляется в стек маршрутов native слоя, закрывая тем самым FlutterActivity / FlutterViewController. Таким образом, FlutterA и FlutterB также скрываются;
  • Если теперь открыть новый Flutter маршрут FlutterC, он будет скрыт за счет native page X;

image3

Таким образом, можно понять, что по умолчанию Flutter работает как "одностраничное" приложение, и его стек маршрутов несовместим с native слоем. Другие проблемы, такие как совместное использование данных памяти и встраивание UI-компонентов, являются болевыми точками add-to-app.


Выбор метода интеграции add-to-app

Официальная документация предлагает два способа интеграции для Android и три для iOS.

  • Android: локальный aar + удаленный aar — этот метод позволяет Android-разработчикам работать без установки Flutter SDK.

  • iOS: [генерация xcframework] — этот метод позволяет iOS-разработчикам работать без установки Flutter SDK.

Хотя эти способы имеют недостаток в необходимости отдельной отладки, с точки зрения проектной耦合和协调开发,我认为这将更加符合需求。


Хотя эти способы имеют недостаток в необходимости отдельной отладки, с точки зрения проектной耦合和协调开发,我认为这将更加符合需求。


Хотя эти способы имеют недостаток в необходимости отдельной отладки, с точки зрения проектной耦合和协调开发, я считаю, что это будет более соответствовать требованиям.Кроме того, если у вас остаются вопросы относительно использования add-to-app, то официальный проект put-flutter-to-work может послужить хорошим примером для изучения.

Если ваш add-to-app не работает, рекомендуется обратиться к этому Demo как лучшему варианту, например, конфигурация зависимостей и предварительная загрузка движка через FlutterEngineCache и executeDartEntrypoint.

Гибридные маршруты

Это ключевой раздел статьи, где можно предвидеть, что гибридные маршруты и совместное использование данных будут самыми большими проблемами при работе с add-to-app, например:

  • Открытие native страницы A
  • Открытие flutter страницы B
  • Открытие native страницы C
  • Открытие flutter страницы D

В процессе этого следует обеспечивать согласованность гибридных маршрутов и бесперебойное взаимодействие данных. Поэтому, на этом этапе, "открытие нескольких flutter страниц одновременно" становится необходимым требованием, но его реализация имеет различные подходы:

  • Каждый flutter экран создает новый Flutter Engine
  • Несколько flutter экранов используют один Flutter Engine

Каждый из этих подходов имеет свои преимущества и недостатки, и именно они являются предметом рассмотрения данной статьи.

FlutterEngineGroup

Для некоторых разработчиков FlutterEngineGroup может быть мало знакомым, это решение было представлено вместе с версией Flutter 2.0 как официальное решение для add-to-app. Решение FlutterEngineGroup использует несколько движков для поддержки гибридов, и официально заявлено, что каждое последующее использование движка после первого занимает всего 180КБ памяти на Android и iOS.


В предыдущих решениях каждый дополнительный Engine мог увеличивать использование памяти на 19МБ для Android и 13МБ для iOS.

После использования FlutterEngineGroup, все FlutterEngine создаются с помощью FlutterEngineGroup. Созданные FlutterEngine могут независимо использоваться в FlutterActivity/FlutterViewController, FlutterFragment и FlutterView.На самом деле, FlutterEngineGroup не обязательно используется для смешивания маршрутов. Как показано на следующем анимированном изображении, в примере официального проекта multiple_flutters также реализован случай, когда два FlutterFragment находятся внутри одного экрана. Основной идеей является то, что FlutterEngineGroup позволяет поддерживать минимальное потребление памяти при работе с несколькими engines.| | | | ------------------------------------------------------- | ------------------------------------------------------------- |

Причина того, почему FlutterEngineGroup обеспечивает минимальное потребление памяти при работе с несколькими Engines, заключается в том, что FlutterEngine, созданные через FlutterEngineGroup, могут использовать общие контексты GPU, метрики шрифтов и изоляционные группы снимков. То есть новый Engine может быть создан через старый Engine методом spawn.

Внутри FlutterEngineGroup основное управление осуществляется через dartEntrypoint:

  • findAppBundlePath по умолчанию указывает на директорию flutter_assets;
  • entrypoint фактически представляет собой имя метода запуска в Dart-коде; это значит, что он связан с методом runApp в Dart, который можно указать через @pragma('vm:entry-point');
  • данные между Dart-слоем и нативным слоем обмениваются через MethodChannel

После внедрения FlutterEngineGroup:

  • на уровне Dart можно открыть исходный экран через MethodChannel;
  • на уровне нативного кода можно открыть новый Flutter экран, создав новый FlutterEngine;
  • даже можно открыть диалоговое окно FlutterView на уровне нативного кода;Конечно, вы уже заметили, что поскольку каждый Flutter экран представляет собой отдельный Engine, благодаря концепции дизайна Dart-isolate, память каждого отдельного Engine не может быть совместно использована. Значит, когда вам требуется делиться данными, вы должны хранить данные на уровне нативной реализации, а затем внедрять или передавать их в каждый Flutter-компонент, как это указано в официальной документации: каждый Flutter-компонент больше похож на отдельный модуль Flutter.> Конечно, это также приводит к некоторым неудобствам, таким как: одинаковое изображение может несколько раз загружаться в нативной реализации и различных Flutter Engines, что требует использования внешних текстур для управления памятью на уровне нативной реализации.

А вот преимущества FlutterEngineGroup очевидны: он поддерживается официально, не требует сторонних библиотек и является легковесным решением.

На самом деле, FlutterEngineGroup полезен не только при работе с несколькими страницами, но даже если у вас всего один Engine, его можно использовать, например, при создании Flutter Engine внутри сервиса и построении независимого FlutterView. Однако, после того как страница остается бездейственной около 20 минут, могут возникнуть следующие проблемы:

E/MessageQueue-JNI: java.lang.RuntimeException: Невозможно выполнить операцию, так как FlutterJNI не связан с нативной средой.

Используя FlutterEngineGroup, вы заметите значительное улучшение ситуации с повторным созданием и сборкой Engine.

Почему здесь подробно рассматривается FlutterEngineGroup: потому что он играет важную роль в последующих решениях.

flutter_boost

В контексте добавления Flutter в существующее приложение, flutter_boost должно быть знакомо многим. Этот ранний открытый проект поддерживает маршрутизацию в смешанном режиме, используя один Engine для совместного использования данных на уровне Dart.Эта модель имеет явные преимущества: совместное использование состояния данных на уровне Dart, поскольку используется всего один Engine. Однако существуют и недостатки, как показано на следующей схеме процесса рендера flutter_boost версии 2.0, где поддержка одного Engine для рендера нескольких Surface не была официально рекомендована, что замедлило развитие проекта.

Однако flutter_boost был основным выбором для early adopters, но если вы хотите использовать flutter_boost сейчас, лучше всего начать с версии Flutter SDK 3.0.

Дело в том, что до версии 3.0 flutter_boost самостоятельно копировал и поддерживал собственные изменения уровня Embedding Engine, что замедляло обновление и увеличивало вторжение. А с версии 3.0 flutter_boost расширяется через наследование, обеспечивая лучшую совместимость в будущем. Хотя flutter_boost также заявляет, что версия flutter_boost 3.0 будет совместима с Flutter 2.0, вы должны это понимать.

На основе текущей версии flutter_boost 3.0 основные преимущества новых версий следующие:

  • В сравнении с flutter_boost 2.0 API для Android и iOS были выровнаны, жизненный цикл был унифицирован, а после оптимизации код стал более читаемым;

  • Нет вторжения в код Engine, обеспечивая совместимость со множеством версий Flutter и избегая проблем, связанных с невозможностью обновления Flutter SDK из-за flutter_boost;

  • Поддержка активна и продолжается.Конечно, если сравнивать с FlutterEngineGroup, то преимущества flutter_boost заключаются в следующем:

  • Поддержка передачи данных между страницами;

  • Унифицированный интерфейс вызова смешанных маршрутов;

  • Поддержка общего доступа данных на уровне Dart;

  • Поддержка передачи событий между платформами.

А недостатки могут быть такими:

Изображение

Как видно, поддерживать дополнительную логику вне механизма Flutter сложно, особенно сложно это делать, когда flutter_boost всё ещё активно развивается и поддерживается. На данный момент можно сказать, что flutter_boost 3.0 является хорошим выбором.

flutter_thrio

Проект flutter_thrio был открыт компанией Hellobike как решение для интеграции "add-to-app". После прекращения поддержки Hellobike проект был взят на себя сообществом flutter_thrio для дальнейшего развития и поддержки.flutter_thrio использует модель множественного Engine, одновременно поддерживая переиспользование Engine. Исходя из текущего состояния кода (bf16529), его работа сводится к тому, что:> Все операции с маршрутами поддерживаются через нативный код, а один ThrioFlutterActivity / FlutterViewController может содержать несколько Dart страниц. Если последняя страница не является контейнером Flutter, новый FlutterEngine создается методом spawn и строится новая страница с контейнером Flutter.

Схема работы flutter_thrio

Внутреннее выполнение flutter_thrio с использованием нескольких Engine аналогично подходу FlutterEngineGroup. Однако он использует рефлексию для получения flutterJNI из FlutterEngine, а затем создает новый FlutterEngine методом spawn.

PS, в текущей версии flutter_thrio метка isMultiEngineEnabled может вызывать путаницу, так как она больше контролирует entrypoint, который используется для принятия решения о запуске нового контейнера FlutterEngine.

image13

Кроме того, flutter_thrio использует упаковку, чтобы обеспечить единый notify интерфейс для синхронизации данных через страницы на всех трёх платформах. Это также является обычной реализацией связи данных в add-to-app.

Какие преимущества имеет flutter_thrio?

  • Возможность запускать FlutterEngine по требованию
  • Нижний уровень использования памяти
  • Единая система маршрутизации и данных
  • Лёгкий код, что делает его более удобным для внедрения

Какие недостатки имеет эта библиотека?- Несоответствие требованиям Fragment

  • Отсутствие функции present, доступной в iOS
  • Официальная поддержка прекращена, ограниченные инвестиции со стороны сообщества, понимание проекта зависит от изучения исходного кода: "Пользователи, которые не хотят внимательно изучать исходный код, могут отказаться от этой библиотеки, так как многие настройки являются жестко закрепленными, а я сам не планирую тратить время на написание большого количества документации".> У многих функций и идей в flutter_thrio есть много положительного, но ресурсы для поддержки ограничены. Надеюсь, что проект будет продолжать развиваться в будущем.

mix_stack

Микс_Стэк — это личная открытая библиотека для смешанной маршрутизации, которая по дизайну напоминает раннюю версию легковесного Boost, использующую одиночный режим Engine. Поэтому она реализует смешанную маршрутизацию путём переключения поверхности при переходе между Flutter контейнерами.

image14

В микс_стэк каждый Native Flutter Container содержит независимый Navigator для управления внутренними стеками Flutter, управление которыми осуществляется через внутренние стеки Flutter для контроля над текущими Native Container.

Особенностью микс_стэк является поддержка нескольких вкладок Flutter View и управления скрытием и показом Native интерфейса с Flutter конца, одним из самых интересных аспектов является NativeOverlayReplacer.Например, как показано на следующем рисунке, на FlutterView находятся два native компонента — navigationBar и tabBar. При попытке вывода нового маршрута Flutter эти native компоненты будут его затенять. Однако, поскольку требуется эффект Hero, мы не хотим использовать новые native контейнеры для этого. В этом случае можно использовать NativeOverlayReplacer.| | | | ------------------------------------------------------------ | ------------------------------------------- |Как показано на правой стороне анимации выше, в странице можно указать autoHidesOverlayNames с помощью NativeOverlayReplacer, а затем вызвать registerAutoPushHiding перед открытием маршрута Flutter popup. В результате это позволяет достичь эффекта, когда компоненты Flutter отображаются поверх нативных.

NativeOverlayReplacer(
  autoHidesOverlayNames: ["tabBar", "navigationBar"],
...
NativeOverlayReplacer.of(context).registerAutoPushHiding()

Основной принцип заключается в том, что при открытии страницы через нативный слой используется метод createBitmap для создания скриншотов двух нативных компонентов, после чего байтовое представление (toByteArray) этих скриншотов передается в Flutter. Когда компоненты Flutter отображаются, нативные компоненты скрываются, и с использованием Stack рисуются битмапы нативных компонентов на нужных позициях, обеспечивая таким образом эффект, как будто компоненты Flutter отрисованы поверх нативных.

Преимущества использования mix_stack:

  • Поддержка вызова на уровне View
  • Более гибкий подход
  • Обладает возможностью использования NativeOverlayReplacer
  • Поддерживает несколько Tab Flutter View

Недостатки mix_stack также очевидны:

  • mix_stack имеет более высокую вторгаемость, особенно на платформе Android, где он использует рефлексию для получения доступа к различным внутренним переменным и методам FlutterEngine, FlutterTextureView и embedding.
  • Личное обслуживание, количество пользователей пока невелико, степень потенциальных проблем неизвестна.> Примечание: если вы столкнулись с ошибкой установки Android при запуске демо, попробуйте отключить строку debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4' в директории android/app.

fusion

fusion использует подход FlutterEngineGroup. По умолчанию при множественных переходах между Flutter и нативными страницами приложение всегда будет иметь один экземпляр FlutterEngine, то есть соответствует режиму REUSE_MODE в fusion.

Конечно, для случаев, таких как FusionFragment и Dialog, fusion предлагает возможность не использовать повторное использование, позволяя создавать независимый FlutterEngine с минимальными затратами.

Если рассмотреть код, то код fusion значительно проще. Например, основные действия, выполняемые FusionActivity после наследования от FlutterActivity: - Найти текущий FlutterView в активности и вызвать метод detachFromFlutterEngine, чтобы отключить его.

  • В методе onResume вызвать engine.activityControlSurface.attachToActivity и flutterView?.attachToFlutterEngine, чтобы восстановить использование движка.

Основной идеей дизайна Fusion является максимально простое объединение соответствующих логик, поэтому вторжение минимальное, а также используется уже существующая логика embedding, если это возможно. По данным демонстрационных примеров, проблем с использованием памяти пока нет.

Преимущества Fusion заключаются в том, что автор уделяет большое внимание деталям:- Возможность неправильного отображения цвета значков состояния при переходах между контейнерами Flutter и Native;

  • Проблема отсутствия отображения имени приложения при входе в режим задач при наличии страницы Flutter в вершине стека;
  • Проблема восстановления страницы Flutter после выхода приложения в фоновый режим и последующего возобновления работы;
  • Поддержка жизненного цикла компонентов.

Конечно, у Fusion есть и недостатки:

  • Автор gtbluesky не предоставил проект на GitHub, поэтому обратная связь и обсуждение сталкиваются с трудностями;
  • Присутствие некоторых ситуаций, когда происходит мерцание экрана;
  • Неизвестно, существуют ли ещё какие-либо проблемы.

PS: Если демо не запускается, попробуйте изменить compileSdkVersion на 32 и установите все ext.kotlin_version на '1.7.10'.

В конце концов

После вышеописанного обзора текущего состояния и поддержки различных решений для интеграции Flutter, надеюсь, вы получили представление о возможных подходах. Выбор конкретного решения зависит от ваших потребностей. Каждый из этих подходов имеет свои слабости и ограничения, но главное — это будущее развитие и поддержка этих решений.Здесь стоит отметить, что открытый исходный код больше всего ценен тем, что он предлагает решение проблемы, эффективное взаимодействие и внесение предложений могут способствовать здоровому развитию проекта. Если сообщество состоит только из запросов на исправление ошибок, то проект будет сложно поддерживать долго. Поэтому важно, что предлагаемое решение действительно полезно, а не то, что проект является частью KPI. Это то, что я считаю наиболее важным в рамках открытого исходного кода.

Опубликовать ( 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