Как часть серии статей, эта статья объясняет реализацию логики PlatformView
на платформе Android с использованием официальной технической документации и объясняет, почему клавиатура PlatformView
часто работает некорректно на Android.
Почему на iOS все работает более стабильно, также рассматривается в данной статье.
Исходя из концепции реализации Flutter, которая аналогична WebView
на Android, Flutter преобразует дерево виджетов в текстуры и использует Skia для отрисовки компонентов. Это обеспечивает отличное кросс-платформенное взаимодействие, но создаёт проблемы совместимости.
Это как WebView
: UI Flutter не превращается в Android-компоненты, а рендерится напрямую через Skia на SurfaceView
.
Поэтому по умолчанию Flutter UI никогда не будет содержать нативные Android-компоненты, что делает невозможной интеграцию таких компонентов, как WebView
или MapView
.
Для решения этой проблемы Flutter создал логику компонента AndroidView
, который позволяет разработчикам встраивать нативные Android-компоненты в Flutter UI.
AndroidView
требует сочетания с Flutter для полноценного отображения: в Flutter рендеринг содержимого AndroidView
происходит внутри VirtualDisplays
. Затем изображение, созданное в памяти VirtualDisplay
, можно получить через его Surface
.> VirtualDisplay
- это виртуальное пространство отображения, которое используется вместе с DisplayManager
. Обычно применяется при работе со вторым экраном или записи экрана. VirtualDisplay
рендерит содержимое виртуального пространства отображения на Surface
.Проще говоря, содержимое нативного компонента рисуется в памяти, после чего Flutter Engine получает данные рендера компонента через соответствующий textureId
и отображает их.
Используя текстуры из вывода VirtualDisplay
и объединяя их с существующим деревом рендера Flutter, Flutter может включать нативные Android-компоненты в своё дерево виджетов Flutter в виде графических элементов.
На платформе iOS вместо использования метода, аналогичного VirtualDisplay
, Flutter UI разделяется на два прозрачных текстура: одна находится ниже платформенного представления iOS, другая — выше него.
Основное преимущество такого подхода заключается в том, что Flutter UI, который требуется отображать ниже платформенного представления iOS, будет рисован на нижней текстуре; а Flutter UI, который требуется отображать выше платформенного представления, будет рисован на верхней текстуре. Они просто объединяются в конце.
Обычно этот способ лучше, так как это позволяет нативному Android view напрямую добавляться в слой UI Flutter.Однако, данная модель не поддерживается на платформе Android, поскольку после рендера фрейма в iOS система отправляет обратные вызовы, такие как: когда iOS виджет смещается вниз на 2px
, все остальные Flutter контролы также могут быть смещены вниз на 2px
.
Но на платформе Android нет никаких системных API, связанных с этим, поэтому невозможно обеспечить синхронное рендериование. Если принудительно использовать такой подход на Android, это может привести к множеству проблем, таких как несинхронность между AndroidView
и Flutter UI.
Подробнее об этом альтернативном подходе см. по адресу: https://flutter.dev/go/nshc
Хотя можно использовать VirtualDisplay
для внедрения Android контрола в Flutter UI, использование VirtualDisplay
создаёт ряд других проблем.
По умолчанию, PlatformViews
не могут получать события касания.
Это связано с тем, что AndroidView
фактически рендерится внутри VirtualDisplay
. Когда пользователь кликает на видимый "AndroidView"
, он действительно "кликает" на рендеримую текстуру Flutter. События касания пользователя передаются напрямую в Flutter View, а не в реальный AndroidView
, на котором они были нажаты.
AndroidView
использует логику тестирования нажатий из фреймворка Flutter для проверки, попал ли пользовательский жест в область, требующую специального обработки.> Подробнее: «Flutter полное руководство по разработке (тринадцатая глава, подробное исследование принципов касаний и скольжений)».
При успешном нажатии отправляется сообщение в Android embedding, содержащее детали события касания.
В встраивании Android координаты события в конце будут сопоставлены с координатами AndroidView
в VirtualDisplay
. После этого создается объект MotionEvent
для описания нового контролируемого устройства прикосновения, который затем передается внутреннему VirtualDisplay
для реального отклика AndroidView
.
MotionEvent
для AndroidView
может привести к отправке информации о прикосновении в неправильное место, если этот View производит другие виджеты.MotionEvent
может вызвать потерю некоторых данных из-за различий в механизмах.Обычно AndroidView
не может получить доступ к введенному тексту, так как положение VirtualDisplay
всегда считается состоянием unfocused
.На данный момент Android не предоставляет никаких API для динамического изменения фокуса окна Window
. В Flutter окно, которое находится в состоянии focused
, обычно представляет собой реальное содержание "текущего" Flutter текстурного изображения и UI, которое является видимым для пользователя.А InputConnections
(механизм ввода текста в Android) обычно игнорируются в неактивном состоянии View.
Flutter переопределил метод checkInputConnectionProxy
, чтобы Android воспринимал Flutter View как агента AndroidView
и редактора входных данных (IME), таким образом Android сможет получать InputConnections
из Flutter View и применять их к AndroidView
.
С версии Android Q InputMethodManager
(IMM) был изменён на экземпляр для каждого окна вместо глобальной единицы, поэтому простое "установление агента" больше не работает начиная с Q. Для решения этой проблемы Flutter создал подкласс Context
, который возвращает контекст, аналогичный IMM
в Flutter View, что позволяет использовать IMM
из Flutter View как агента при запросах IMM
. Это значит, что когда Android требует IMM
, VirtualDisplay
использует IMM
из Flutter View как агента.
При запросе AndroidView
предоставлять InputConnection
, он проверяет, является ли AndroidView
целью ввода. Если да, то [InputConnection
из AndroidView
будет получен и возвращен Android](https://github.com/flutter/engine/blob/036ddbb0ee6858ae532df82a2747aa93faee4487/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java#L206). - Android считает Flutter View как focused
и доступный, поэтому InputConnection
для AndroidView
может успешно быть получен и использован.
WebView
был сложнее, так как они имеют собственные внутренние логики для создания и установки входящих соединений, которые не полностью следуют протоколу Android. В плагине flutter_webview
требуется добавление других решений для активации ввода текста в WebView
.- Установка прокси-вью, который прослушивает входящие соединения вместе с WebView
на одном потоке. Без этой возможности WebView
будет внутренне потреблять все вызовы InputConnection
, не уведомляя прокси-вью Flutter.WebView
. Это предотвращает "зависание" ввода текста внутри WebView
.Обычно эта логика зависит от внутреннего поведения Android и может быть очень хрупкой, например: Проблемы с вводом с клавиатуры на устройствах Huawei и других в версии 1.12.
Некоторые текстовые функции остаются недоступными, такие как: диалоги "Копировать" и "Поделиться" пока недоступны.
PlatformView
увеличивает жизнеспособность и гибкость Flutter, но также приводит к множеству проблем, таких как темы вопросов, связанных с #webview-keyboard, #webview, #platform-views, а также, как указано в документации плагина webview_flutter:Этот плагин использует новую механику Flutter для встраивания Android и iOS представлений. Поскольку эта механика находится в стадии предварительной версии для разработчиков, этот плагин также рассматривается как предварительная версия для разработчиков.
webview_flutter
поддержка клавиатуры пока не предназначена для использования в продакшне, поскольку поддержка клавиатуры в WebView всё ещё находится в экспериментальной стадии.Теперь вы знаете, почемуPlatformView
в Flutter на Android сложно обеспечивать совместимость и почему связано много проблем с вводом с клавиатуры.
Наконец-то закончил двадцатую статью!(///▽///)
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )