Автор оригинала: Егор Жбанов
Переводчик: Джоти, UC International R&D
Спереди написано: Добро пожаловать в официальный аккаунт «UC International Technology», мы предоставим вам качественные технические статьи, связанные с клиентом, сервером, алгоритмом, тестированием, данными, интерфейсом и т. д., не ограничиваясь оригинальностью и перевод.
Сегодня мы сделали объявление на Flutter Live: мы пытаемся запустить Flutter в сети. В этом посте описывается, как мы подошли к задаче, а также о текущем состоянии технологии. В конце статьи мы прикрепляем ответы на такие вопросы, как совместная работа и встраивание.
Давайте быстро рассмотрим архитектуру Flutter. Flutter — это многоуровневая система, так что более высокие уровни проще в использовании и многое выражают с помощью очень небольшого количества кода, в то время как нижние уровни обеспечивают больший контроль за счет необходимости иметь дело с некоторой сложностью. Когда более высокие уровни не соответствуют потребностям разработчика, они могут перейти на более низкие уровни. Разработчики имеют доступ ко всем слоям поверх Flutter Engine.
Мобильная архитектура Flutter
Во Flutter Flutter Engine представлен как библиотека самого низкого уровня, dart:ui. Он не заботится о компонентах, физической реализации, анимации или макете (кроме текстового макета). Все, о чем он заботится, это как объединить изображения на экране и преобразовать их в пиксели. Писать приложения прямо на dart:ui сложно. Вот почему мы создаем более высокие слои.
Все, что находится поверх dart:ui, мы называем «фреймворком». Все, что ниже него, является «двигателем». Фреймворк полностью написан на языке Dart. Большая часть движка написана на C++, части для Android написаны на Java, а части для iOS написаны на Objective-C. Некоторые базовые классы и функции в dart:ui написаны на Dart и в основном используются в качестве моста между Dart и C++.
Flutter также предоставляет систему плагинов. Плагины написаны на указанном языке и имеют прямой доступ к накопленным OEM и сторонним библиотекам мобильной экосистемы. Вы можете создавать плагины для Android, используя Java или Kotlin. Разработка плагина для iOS выполняется с использованием Objective-C или Swift.
Hello, The Web
Веб-платформа развивалась на протяжении десятилетий, охватывая множество технологий и спецификаций. Есть несколько общих терминов, используемых для описания большого количества связанных функций: HTML, CSS, SVG, JavaScript, WebGL. Чтобы запустить Flutter в Интернете, нам нужно:
Скомпилируйте код Dart: Flutter написан на Dart, и нам нужно запустить Dart в Интернете.
Выберите подмножество Flutter для запуска в Интернете: Нецелесообразно запускать весь код Flutter в Интернете. Некоторые из них зависят от платформы, например Android и iOS.
Выберите подходящее подмножество веб-функций: Со временем веб-платформа накапливает дублирующийся функционал. Например, вы можете рисовать графику, используя HTML + CSS, SVG, Canvas и WebGL.
Dart компилирует JavaScript с самого начала. Многие важные приложения теперь компилируются из Dart в JavaScript и запускаются в производственной среде. Стратегия компиляции Flutter опирается на ту же инфраструктуру.
Когда мы начали исследовать, мы столкнулись с несколькими вариантами рендеринга пользовательского интерфейса. Мы быстро поняли, что конкретный слой Flutter, который мы хотели поддерживать, определяет, какую веб-технологию мы будем использовать. Мы построили три прототипа:
Просто виджеты: этот прототип реализует структуру виджетов Flutter и предоставляет основной набор виджетов макета в качестве основы для создания пользовательских виджетов. Для макета и позиционирования он использует встроенные функции Интернета, такие как flexbox, макет сетки, прокрутка браузера (через overflow:scroll) и т. д.
Widgets + индивидуальный макет: этот прототип включает систему компоновки Flutter (предоставленную RenderObject), но сопоставляет объекты рендеринга непосредственно с элементами HTML.
Flutter Web Engine: этот прототип сохраняет все слои поверх dart:ui и предоставляет реализацию dart:ui, которая запускается в браузере.
Одной из самых ценных особенностей Flutter является то, что его можно переносить на разные платформы. Вполне возможно (а иногда даже рекомендуется) писать собственный код для конкретной платформы, который можно использовать совместно без межплатформенной настройки. Это означает, что приложения, предназначенные для нескольких платформ, могут быть написаны с использованием единой кодовой базы.
После попытки перенести несколько примеров приложений в Интернет мы поняли, что прототипы №1 и №2 не обеспечивают того уровня переносимости, который нравится разработчикам Flutter. Поэтому мы решили использовать прототип № 3, разработанный Flutter Web Engine, потому что он имеет самый высокий уровень повторного использования кода между платформами:
Веб-архитектура Flutter (Колибри)
Теперь, когда мы знаем, что хотим реализовать весь API dart:ui, нам нужно выбрать набор веб-технологий, на которых мы будем основываться. Flutter отображает пользовательский интерфейс по одному кадру за раз. В каждом кадре Flutter будетПостроитьвиджеты, выполнитьмакет, и, наконец, на экранерисоватьOни.
Строить виджеты
Механизм построения виджета не зависит от среды, в которой работает приложение. Процесс просто создает экземпляр объекта в памяти, отслеживает его состояние и, когда состояние изменяется, вычисляет минимальные системные обновления низкого уровня, компоновку и отрисовку и т. д. Перенести эту часть в сеть очень просто. После того, как команда Dart реализовала поддержку супер-миксинов в dart2js, компилятор почти без проблем скомпилировал все виджеты и фреймы виджетов в JavaScript.
макет
Система компоновки немного сложна. Самая большая проблема — это верстка текста. Все, кроме Center, Row, Column, Stack, Scrollable, Padding, Wrap и т. д., выкладывается фреймворком и, таким образом, компилируется в сеть без изменений.
Во Flutter вы можете создать объект Paragraph и вызвать его метод layout() для реализации макета текста. К сожалению, в Интернете отсутствует API прямого текстового макета. Хитрость, которую мы используем для измерения свойств текстового макета, заключается в том, чтобы сначала разрешить макет браузера, а затем прочитать соответствующие свойства из элемента DOM.
При размещении абзацев текста Flutter измеряет высоту абзаца, ширину, максимальную внутреннюю ширину, минимальную внутреннюю ширину, а также базовые линии букв и идеографических символов. Эти свойства показаны ниже.
Paragraph layout attributes
Вы можете найти более подробную информацию в документации Paragraph Flutter.
Для измерения этих свойств мы сначала поместите абзац в элемент HTML DOM, а затем прочитав размеры элемента. Это приведет к тому, что макет браузера. Например, чтобы получить ширину и высоту элемента, мы называем OffsetWidth и Offsetheight. Для измерения базовой линии мы помещаем абзац в элемент, который сконфигурирован для использования строк Flex для макета. Рядом с абзацем мы размещаем другой элемент называемый зондом. Поскольку зонд выровнен с базовой линии текста, вызывая GetBoundingClientRect, получит базовую линию. Мы используем аналогичный трюк для измерения минимальной и максимальной внутренней ширины.
Картина
Следует отметить, что мы должны рисовать виджеты. Эту область было труднее всего исследовать, и она остается самой активной областью наших исследований. В конце кадра все наши виджеты нужно отрисовывать в пикселях на экране. В браузерах это означает, что они должны сводиться к некоторой комбинации HTML/CSS, Canvas, SVG и WebGL.
Мы не рассматривали WebGL главным образом потому, что он низкоуровневый и требует от нас повторной реализации того, что уже могут делать браузеры, например разметки текста и растеризации 2D-графики. Другая причина заключается в том, что мы не выяснили, как компоненты, отличные от Flutter, сочетаются с WebGL для доступности, выбора текста и композиции.
Наш ранний прототип генерировал HTML-элемент для каждого RenderObject. Результаты оправдали ожидания, но в итоге оказалось, что слишком сильно изменился API. Нам пришлось поддерживать огромный прирост кода с Flutter, поэтому мы отложили эту идею.
В настоящее время мы изучаем два подхода одновременно:
HTML+CSS+Canvas
CSS Paint API
HTML+CSS+Canvas
На основе этого метода мы классифицируем изображения, сгенерированные фреймворком, на изображения, выраженные с помощью HTML + CSS, и изображения, выраженные с помощью Canvas 2D. Затем мы выводим HTML DOM, который сочетает в себе HTML, CSS и 2D-холст.
Мы предпочитаем HTML + CSS, так как он поддерживается списком отображения браузера. Это означает, что мы можем оставить оптимизацию растеризации изображения механизму рендеринга браузера. И мы также можем применять произвольные преобразования, особенно вращение и масштабирование, не беспокоясь о пикселизации. Мы называем эту реализацию холста DomCanvas.
Если мы не можем выразить изображения с помощью HTML + CSS, мы будем использовать Canvas. Canvas 2D позволяет нам рисовать почти все команды рисования Flutter. Если вы сравните Canvas во Flutter с CanvasRenderingContext2D в Интернете, вы обнаружите много общего. Рисование на холсте эффективно, потому что оно не создает изменяемых узлов дерева, таких как HTML DOM или SVG, которые необходимо поддерживать с течением времени.
Одна из проблем с 2D Canvas заключается в том, что браузер представляет его как растровое изображение, буфер памяти, в котором хранятся пиксели ширины x высоты. Поэтому масштабирование холста приводит к пикселизации. Если масштабирование приводит к изменению размера изображения, нам также необходимо изменить размер холста. Мы обнаружили, что выделение операций холста было довольно затратным, поэтому вместо этого было решено изменить их размер. Кроме того, при компоновке нескольких холстов на одной странице браузер должен выполнять растровую композицию, что также требует настройки. Составление растров отличается от отображения списков. Вы можете нарисовать несколько списков отображения в одном и том же буфере памяти. Мы называем холст, поддерживаемый Canvas 2D, для реализации BitmapCanvas. Мы изучаем способы сделать растровые холсты более эффективными.
Чтобы выразить непрозрачность, преобразования, смещения, прямоугольники обрезки и другие слои Flutter, мы используем простые элементы HTML. Например, слой непрозрачности становится элементом overflow: hidden
Когда все сделано, кадр отображается на странице в виде дерева элементов HTML с DomCanvas и BitmapCanvas в качестве листовых узлов. Например 🌰:
Sample HTML DOM structure of a frame
Эквивалентное дерево слоев Flutter (называемое потоковым слоем) во Flutter Engine выглядит следующим образом:
Sample Flutter Engine layer structure
Их структуры очень похожи. Самая большая разница заключается в том, что в Интернете мы должны выбирать разные реализации изображений в зависимости от контента.
HTML + CSS + Canvas работает во всех современных браузерах. Однако мы уже смотрим в будущее:
CSS Paint API
CSS Paint — это новый веб-API и большая часть Houdini. Houdini — это совместный проект нескольких поставщиков браузеров, целью которого является предоставление разработчикам некоторых частей движка CSS. В частности, CSS Paint API позволяет разработчикам рисовать пользовательскую графику в HTML-элементах, когда эти элементы запрашивают рисование. Например, вы можете назначить отрисовку фона элемента пользовательскому рисовальщику CSS. Он очень похож на холст со следующими важными отличиями:
Это рисование не выполняется независимо базовым JavaScript, а чем-то, что называется ворклетом рисования. Это немного похоже на веб-воркер в том, что у него есть собственное пространство памяти. Он выполняет рисование на этапе рисования браузера после фиксации изменений DOM.
CSS Paint поддерживается списками отображения, а не растровыми изображениями. Это действительно лучшее из обоих миров — эффективность рисования, подобная 2D-холсту, и отсутствие пикселизации.
В настоящее время краска CSS не поддерживает рисование текста.
На момент написания этой статьи Chrome и Opera были единственными браузерами, официально поддерживающими CSS Paint. В то время как другие браузеры находятся на разных стадиях выпуска собственных реализаций.
У нас есть экспериментальная поддержка CSS Paint API во Flutter for Web, и она показала хорошие результаты, особенно с точки зрения производительности. Наша реализация просто сериализует команду рисования как пользовательское свойство CSS. Рабочий процесс рисования считывает эти команды и выполняет их. Мы используем обычные элементы HTML, такие как
и , для отображения текста.
Наш текущий механизм сериализации не особенно эффективен — это дерево JSON, преобразованное во вложенный список, — но частью проекта Houdini является добавление поддержки типизированных массивов. Когда он будет доступен, мы будем кодировать команду рисования как типизированный массив вместо строки JSON. Типизированные массивы являются переносимыми, что означает, что они могут быть переданы по ссылке из основного JavaScript в ворклет рисования без копирования памяти.
Синергия и встраивание
Вызов библиотек Dart из Flutter
Веб-приложения Flutter имеют доступ ко всем библиотекам Dart, которые в настоящее время работают в Интернете.
Вызов библиотек JavaScript из Flutter
Веб-приложения Flutter полностью поддерживают пакеты JS-interop Dart: package:js иdart:js
.
Использование CSS в веб-приложениях Flutter
В настоящее время Flutter берет на себя полный контроль над правильностью и производительностью веб-страниц. Например, мы используем только небольшое подмножество CSS, которое соответствует некоторым рекомендациям по производительности, таким как https://csstriggers.com/. Неразборчивое использование CSS на странице может привести к неожиданным результатам во Flutter.
Еще одна причина избегать CSS во Flutter для веб-приложений заключается в том, что во время разработки Flutter необходимо знать все свойства макета при рендеринге фрейма. CSS действует как черный ящик. Например, если вы хотите отобразить прокручиваемый список оконных виджетов, вы должны создать экземпляр и сгенерировать HTML для всех виджетов и применить необходимые свойства CSS (например, flex-direction row и overflow:scroll). Затем браузер выкладывает все и отображает это на экране. Код приложения не участвует в процессе компоновки.
Наконец, чтобы сохранить переносимость кода Flutter между платформами, мы стараемся избегать CSS, чтобы мы могли запускать один и тот же код изначально на Android и iOS.
Встроить Flutter в существующее веб-приложение
Мы еще не добавили надлежащую поддержку для этого, но мы намерены исследовать это в будущем. Мы рассматриваем методы
Встраивание компонентов, отличных от Flutter, во Flutter
Мы пока не поддерживаем встраивание компонентов, отличных от Flutter, в веб-приложения Flutter — настраиваемые элементы, компоненты React, компоненты Angular, но мы планируем изучить это в будущем. Можно использовать представления платформы для размещения внешнего контента в веб-приложении Flutter. Следует учитывать, что внешнее содержимое может повлиять на производительность и корректность приложения. Поскольку компоненты, отличные от Flutter, могут содержать произвольный CSS, как упоминалось выше, это может быть проблематично. Необходимы дополнительные исследования.
портативность
Наша цель — перенести как можно больше фреймворка в Интернет. Однако это не означает, что любое приложение Flutter будет работать в Интернете без изменений кода. Веб-приложение Flutter по-прежнему остается веб-приложением; оно изолировано в браузере и может делать только то, что позволяет веб-браузер. Например, если ваше приложение Flutter использует собственный плагин, не реализованный в Интернете (например, ARCore), вы не сможете запустить приложение в Интернете. Точно так же нет прямого доступа к файловой системе или низкоуровневой сети.
Текущее состояние
Мы создали достаточно веб-движков для рендеринга большей части галереи Flutter. Мы еще не портировали виджеты Cupertino, но все виджеты Material, Material Theming и демонстрационные приложения Shrine и Contact Profile уже работают в Интернете.
Flutter работает в десктопном презентационном видео Chromehttps://www.youtube.com/watch?v=5IrPi2Eo-xM
Мы планируем открыть исходный код этого проекта в ближайшее время и рады поделиться им с сообществом открытого исходного кода. Проект начался как исследование внутреннего исходного дерева Google. Как только код станет стабильным, мы намерены перенести разработку на GitHub, где у нас будет возможность выделить его из нашей внутренней инфраструктуры. В то же время не удивляйтесь, если вы увидите связанные с Интернетом пулл-реквесты в организации github.com/flutter!
в заключении
Надеюсь, эта статья дала вам представление о проблемах, которые мы решаем, чтобы помочь Flutter лучше работать в Интернете. Ваши взгляды и мнения приветствуются.
Оставайтесь с нами на Google I/O 2019!
Английский оригинал:
https://medium.com/flutter-io/hummingbird-building-flutter-for-the-web-e687c2a023a8
Хорошая рекомендация статьи:
Я хочу изучить Flutter, но не знаю, с чего начать?
Член команды Google Flutter Ли Юцянь подтвердил свое участие в 13-м Форуме передовых технологий D2.
«UC International Technology» стремится делиться с вами высококачественными техническими статьями.
Добро пожаловать, чтобы подписаться на наш официальный аккаунт и поделиться статьей с друзьями