Flutter
Flutter — це відповідь Google на розробку під кілька платформ з однієї кодової бази, і підхід тут протилежний до React Native: замість просити віджети в операційної системи, Flutter постачає власний рушій рендерингу й малює кожен піксель сам. Це одне рішення пояснює однаковість, продуктивність, розмір бінарника й ті місця, де підхід буксує. На цій сторінці — конвеєр рендерингу, Dart, керування станом і те, куди Flutter пасує.
- Рендеринг
- Власний рушій, власні пікселі
- Мова
- Dart, AOT-компіляція
- API платформи
- Через канал
Flutter малює пікселі сам
Коли ви пишете кнопку у Flutter, не створюється ані UIButton, ані Android-Button. Flutter будує власне дерево, сам робить розкладку й віддає результат рушієві, який растеризує його на полотні на GPU. Платформа дає поверхню для малювання й майже нічого більше. Це і є той єдиний факт, який пояснює про фреймворк майже все інше.
Конвеєр проходить через три дерева, і розуміння, що є що, робить поведінку фреймворку прозорою. Віджети — це ваш опис: незмінний, дешевий, який викидають і будують заново постійно. Елементи — це те, що справді змонтовано, і вони переживають перебудови; саме тому віджет можна перестворювати шістдесят разів на секунду, не перестворюючи його стан. Об’єкти рендерингу роблять розкладку й малювання, і саме вони дорогі, — тому Flutter так старається їх перевикористовувати.
Те, чого Flutter не малює, він мусить попросити. Камера, біометрія, сповіщення, Bluetooth, вибір файлів, покупки в застосунку — усе це доступне через канал платформи, тобто асинхронне повідомлення коду на Swift чи Kotlin. На практиці для поширених випадків пакет уже існує, але залежність цілком реальна: кожен такий пакет — це нативний код, який хтось має підтримувати проти двох платформ, що змінюються. Доступність і введення тексту працюють, але вони примощені до сервісів платформи, а не успадковані від нативного віджета, — тому іноді потребують уваги, якої нативний застосунок не потребував би.
Dart і чому це не мінус
Вивчати мову заради фреймворку звучить як витрата, і Dart — найменша версія цієї витрати, яку можна побажати: будь-хто, кому зручно з TypeScript, Java чи C#, читає його першого ж вечора. Він статично типізований, надійний щодо null-безпеки — значення, що може бути null, не можна використати як ненульове, і компілятор це забезпечує, — і однопотоковий у межах ізоляту, з async / await, які виглядатимуть знайомо.
Незвичайним Dart робить те, що він компілюється двома різними способами під дві різні цілі. У розробці він працює на віртуальній машині з JIT-компіляцією — саме це уможливлює гаряче перезавантаження зі збереженням стану: ви змінюєте віджет, і запущений застосунок оновлюється менш ніж за секунду, не втрачаючи, де ви були. Для релізу він компілюється заздалегідь у машинний код, тож у поставленому застосунку немає ані інтерпретатора, ані мосту в JavaScript. Швидкість розробки й продуктивність релізу зазвичай торгуються одна проти одної; тут — ні.
Паралельність працює як у Node: один потік на ізолят, із циклом подій, тож await ніколи не блокує інтерфейс, а довге обчислення — блокує. Для справді важкої роботи ви запускаєте ще один ізолят, який має власну пам’ять і спілкується повідомленнями, а не спільним станом. Церемонії тут більше, ніж із потоком, — і зникає весь клас гонок за даними, які потоки приносять.
Керування станом і чесна відповідь
У Flutter немає офіційного рішення для керування станом — тому кожна розмова про це перетворюється на порівняння пакетів. Корисно дивитися на поле за тим, скільки структури нав’язує кожен варіант, і правильна відповідь залежить значно більше від розміру команди, ніж від переваг бібліотеки.
setState
Вбудований, локальний для одного віджета. Правильний для всього, чим володіє один екран, і хибний тієї миті, коли двом екранам потрібне те саме значення.
Provider / Riverpod
Впровадження залежностей плюс спостережуваний стан. Riverpod сучасніший: безпечний на етапі компіляції, тестований і незалежний від дерева віджетів.
BLoC
Події всередину, стани назовні, потоками. Забагато церемонії для малого застосунку; передбачувано й простежувано для великої команди на довгому проєкті.
Порада, що тримається незалежно від вибору: тримайте стан якомога ближче до місця, де його використовують, і виносьте назовні лише тоді, коли він справді потрібен другому місцю. Більшість складності, яку приписують керуванню станом у Flutter, створена власноруч: глобальне сховище, заведене першого ж дня для застосунку з чотирьох екранів, у якого спільного стану немає взагалі.
Як це виявляється в реальній поставці
Дві витрати варто знати до першого релізу. Рушій постачається всередині застосунку, тож бінарник Flutter стартує на кілька мегабайтів більшим за нативний — для більшості продуктів це байдуже, а на ринках, де розмір завантаження впливає на встановлення, це справжня проблема. І Flutter у вебі технічно підтримується, але рендерить у полотно, тож виділення тексту, SEO та доступність поводяться не як на звичайній сторінці: це пасує внутрішньому інструменту чи застосункоподібному продукту, а не публічному контентному сайту.
З іншого боку, інструментарій — це справжня сильна сторона. Гаряче перезавантаження змінює відчуття від роботи над інтерфейсом: ви ітеруєте відступи й кольори за секунди, а не за цикли збірки. Flutter DevTools показує дерево віджетів, кількість перебудов на кожен віджет і часову шкалу кадрів, що розділяє роботу UI й растеризації, — тож питання «чому тут смикається» має відповідь, а не здогад. Тести віджетів працюють без пристрою й достатньо швидкі, щоб тримати їх у pre-commit-хуку.
Де це вироджується
- Метод
buildна сотні рядків перебудовує цілий екран через одне змінене значення. - Віджети, винесені в методи, а не в класи:
constніколи не зможе їх пропустити. - Пакет глобального стану, узятий першого дня для застосунку, у якого спільного стану немає.
- Важкі обчислення в UI-ізоляті гублять кадри рівно так само, як заблокований цикл подій.
- Один дизайн на обидві платформи без огляду на конвенції жодної з них.
- Пакети з нативним кодом, узяті без перевірки, хто підтримує їх проти нових версій ОС.
- Flutter web, обраний для публічного контентного сайту, де полотно — хибний вихід.
Коли це застосовувати
Застосовуйте, коли
- Продукти, що мають виглядати й поводитися однаково на обох платформах — з однієї специфікації дизайну.
- Власні брендовані інтерфейси, де ви й так збиралися перевизначати віджети платформи.
- Насичений анімаціями інтерфейс: володіння конвеєром рендерингу тут саме й дає перевагу.
- Команди без наявних навичок React, для яких Dart не більша перешкода, ніж будь-який інший новий стек.
Уникайте, коли
- Застосунки, які мають відчуватися точно як платформа, — там успадковані нативні віджети і є суттю.
- Продукти, де розмір завантаження вирішує кількість встановлень: рушій їде разом із бінарником.
- Публічні контентні сайти: рендер у полотно — хибний інструмент для тексту й SEO.
- Усе навколо найновіших API платформи: до Flutter вони доходять після того, як дійшли до нативу.
Було корисно?
Поділіться з тим, хто працює над тією ж задачею.