2.22. Flutter: архитектура фреймворка

Давайте немного отступим от практических основ Flutter и взглянем на внутреннее устройство фреймворка.

В этом параграфе мы заглянем Flutter «под капот». Вы узнаете, из чего состоит Flutter-приложение и на какие слои разбит фреймворк.

Компоненты Flutter-приложения

Для начала посмотрим, из каких компонентов состоит стандартное Flutter-приложение. Чтобы его создать воспользуйтесь командой flutter create.

С первым слоем вы уже знакомы — это содержимое папки lib. Здесь происходит описание работы приложения: вёрстка интерфейса, программирование бизнес-логики, отправка запросов на сервер и так далее. Эту часть нашего приложения называют Dart App.

Второй слой — это как раз фреймворк, то есть Flutter. Посмотреть его исходные коды можно в открытом репозитории на GitHub. Он полностью написан на Dart и содержит в себе всё, что может понадобится разработчику: об этом мы также говорили в предыдущих главах.

Третий слой — движок. В документации Flutter он так и называется — engine. Движок отвечает за рендеринг элементов на экране, платформенные каналы, старт Dart-изолятов и другие функции, про которые мы поговорим чуть позже. Тут важно запомнить, что engine — это не компилятор Dart-кода. Где происходит компиляция мы расскажем чуть ниже.

Четвёртый слой — Embedder API. Это набор методов, который обязана реализовать конкретная платформа, для того чтобы запустить на ней движок Flutter. Это позволяет не привязывать реализацию движка к конкретной платформе.

Набор сущностей, реализующий этот контракт называется embedder — пятый слой нашего приложения. embedder привязывается к операционной системе конкретной платформы, это своего рода адаптер API-системы к API-engine. Наружу, через публичные методы, открываются различные платформенные возможности: обработка событий операционной системы, рендеринг, старт потоков и другие.

👉 Внимательный читатель заметит, что ответственность за рендеринг лежит и на engine, и на embedder. Ключевая разница здесь заключается в том, что engine содержит логику рендеринга, а embedder — предоставляет возможность рендеринга на платформе.

Эти слои связываются в единое приложение внутри Runner. В нём все части объединяются воедино в пакет приложения, который можно запустить на целевой платформе. Для этого приложение компилируется с помощью инструментов flutter_tools.

Структуру Flutter-приложения можно представить следующей схемой:

fluttern

Теперь рассмотрим подробнее слои:

  • Framework.
  • Engine.
  • Embedder.

Каждый из них также состоит из компонентов (подробнее можно увидеть на иллюстрации ниже) — расскажем, зачем они нужны, и какие задачи решают.

fluttern

Начнём со слоя Framework.

Слой: Framework

Его основные компоненты — UI-библиотеки, виджеты, Rendering и Foundation.

UI-библиотеки Material и Cupertino

Они содержат различные готовые элементы, которые сделаны в соответствии с гайдлайнами Android (Material Design) и iOS (Apple HIG). В них входят различные свойственные конкретным платформам UI-элементы.

  • Для Material это MaterialButton, AppBar, Scaffold, GridView, MaterialApp и так далее.
  • Для Cupertino — CupertinoTabScaffold, CupertinoListTile, CupertinoSwitch, CupertinoApp и другие.

flutter2

Widgets

С виджетами вы уже познакомились ранее. Подробнее на них мы останавливаться не будем, просто акцентируем ваше внимание, что они — один из компонентов фреймворка.

Rendering

Этот компонент отвечает за:

  • различные математические вычисления;
  • сетку координат;
  • нажатия и их связку с конкретным виджетом;
  • подсчёт размеров элементов;
  • вычисление скорости скролла и многое другое.

Как мы уже упоминали ранее, основным элементом слоя Rendering является RenderObject. Дерево этих объектов передаётся слою Engine для отрисовки.

👉 Важно: слой Rendering не содержит логики построения этого дерева — оно строится на стороне библиотеки Widgets.

И последнее. Слой Rendering — это абстракция над библиотекой Dart под названием dart:ui.

Она открывает низкоуровневые интерфейсы, которые нужны Flutter для построения приложений: например, классы для обработки ввода или рендеринга.

На самом деле вы можете написать Flutter-приложение без помощи RenderObject, или виджетов или UI-библиотек Material и Cupertino. dart:ui содержит всё необходимое для отрисовки интерфейса простого приложения (Canvas, TextBox, Paint).

Но тогда вам придётся самому просчитывать всю математику, необходимую для отрисовки, отлавливать и обрабатывать пользовательский ввод и, наконец, самостоятельно рисовать интерфейс.

Может быть такой подход и подойдет для очень простых приложений, но как только вам понадобится более сложный интерфейс, вы упретесь в высокую стену. Поэтому пользуйтесь Rendering.

Прочие компоненты

А еще фреймворк содержит следующие компоненты:

  • Gestures — классы для работы с пользовательским вводом (нажатия, скролл, различные жесты и так далее).
  • Animations — классы для создания собственных анимаций, если вам не хватило функциональности виджетов из Widgets и Material/Cupertino
  • Painting — набор классов, которые оборачивают интерфейс отрисовки engine в более удобное API, умеют работать с тенями, изображениями, закруглениями и прочим
  • Foundation — низкоуровневые классы-утилиты, которые используются другими слоями фреймворка. А ещё тут происходит самое важное — связывание приложения с engine. О этом мы подробнее расскажем в одном из следующих параграфов.

Слой: Engine

Давайте теперь поговорим про Engine. Общий, платформенно-не-специфичный engine написан на языках C/C++, он реализует в себе фунционал главных API Flutter.

Задачи, которыми занимается engine, можно представить следующей схемой:

fluttern

Для рендеринга engine использует графическую библиотеку Skia, разработанную Google. Ей, в том числе, пользуется браузер Google Chrome, система Android и другие продукты. А ещё она — open-source.

Слой: Embedder

К платформенно-специфичным частям engine относятся так называемые embedders. Это shell-оболочки, которые реализуют функционал, специфичный для конкретной платформы. Они выполняют связующую роль между платформенно-не-специфичным Engine и конкретной операционной системой: благодаря этому engine не нужно знать, на какой платформе он сейчас запущен.

fluttern

В слое embedders происходит взаимодействие с Event Loop — механизмом, который отвечает за обработку событий и асинхронность. Вот как оно устроено:

  • Происходит событие — например, пользователь нажал на кнопку, сервер отправил HTTP-ответ и так далее.
  • embedder перехватывает событие и доставляет его engine.
  • engine откладывает операцию ScheduleFrame.
  • Она планирует задачу перерисовки экрана, путем добавления события в Event Loop.

Еще embedders содержит логику запуска task runners, необходимых для работы Flutter:

  • Platform task runner — тут происходит взаимодействие конкретной платформы с движком engine: перехват различных событий платформы, нативные App Lifecycle и так далее. Он запускается на основном потоке в платформе.
  • UI task runner — тут происходит часть пайплайна рендеринга Flutter, Event Loop, главного изолята.
  • Raster task runner — непосредственно отрисовка на устройстве. Пересборка виджетов и рендеринг на GPU в одном потоке сильно снижает производительность, поэтому рендеринг на устройстве происходит в отдельном потоке.
  • IO task runner — загрузка из памяти/сети различных файлов: большие картинки, аудио, бинарные файлы и другие тяжелые операции.

Embedder сам решает, как распределить task runners по потокам в системе. В общем случае они распределяются по разным потокам, для лучшей производительности, но это не всегда так.


Вот мы и заглянули в «начинку» Flutter. Надеемся, нам удалось рассказать, какой слой и какой компонент за что отвечает.

Чтобы закрепить знания, советуем пройти квиз. А в следующем параграфе мы поговорим о том, как Flutter собирает приложение — и что нужно сделать для его установки на различных платформах.

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E

Отмечайте параграфы как прочитанные, чтобы видеть свой прогресс обучения

Вступайте в сообщество хендбука

Здесь можно найти единомышленников, экспертов и просто интересных собеседников. А ещё — получить помощь или поделиться знаниями.
Вступить
Сообщить об ошибке
Предыдущий параграф2.21. Project: интернационализация и локализация
Следующий параграф2.23. Flutter: виды сборки