Flutter Widgets — що це, дерево віджетів і build

Автор: IT Sectr Опубліковано: 2026-02-21 Час читання: 8 хв

Flutter Widgets — базові будівельні блоки користувацького інтерфейсу у фреймворку Google Flutter, випущеному в стабільній версії 1.0 у грудні 2018 року. У Flutter все — віджет: від екрана та кнопки до відступу та вирівнювання. Віджети утворюють дерево (widget tree), яке Flutter рендерить на власний двигун Skia/Impeller, минаючи нативні компоненти платформи. За даними Google I/O (2025), Flutter використовується більш ніж у 500 000 додатків у Google Play та App Store. Архітектура Flutter розділяє віджети на StatelessWidget (незмінні) та StatefulWidget (із змінним станом через State-об'єкт).

Головне

  • Flutter Widgets — всі елементи UI у Flutter: від екрана та кнопки до відступу та вирівнювання; фреймворк не використовує нативні компоненти платформи.
  • StatelessWidget — віджет без змінного стану; відображає дані, передані через конструктор, і не змінюється після збірки (build).
  • StatefulWidget — віджет із змінним станом, що зберігається в окремому об'єкті State, який переживає ребілди.
  • Дерево віджетів (widget tree) — ієрархія віджетів, що описують UI; Flutter перетворює її в Element Tree та Render Tree для рендерингу.
  • BuildContext — контекст збірки, що надає доступ до позиції віджета в дереві, теми, MediaQuery та InheritedWidget.

Що таке Flutter Widgets?

Flutter Widgets — всі елементи UI у фреймворку Flutter, реалізовані як незмінні (immutable) Dart-класи. Віджет — це конфігурація елемента UI: його розмір, колір, положення, обробники подій. На відміну від Android (XML + View) або iOS (Storyboard + UIView), Flutter не використовує нативні компоненти — все малюється двигуном Skia (Android/iOS/Linux) або Impeller (iOS/macOS) на Canvas. Це забезпечує однаковий зовнішній вигляд на всіх платформах без платформених розбіжностей. Flutter постачається з двома наборами віджетів: Material Widgets (дизайн Google Material Design 3 для Android/вебу) та Cupertino Widgets (дизайн Apple HIG для iOS/macOS). Розробник може комбінувати обидва набори в одному додатку. Кожен віджет має метод build(), що повертає дочірній віджет або їх композицію — так будується дерево віджетів.

Три дерева Flutter

Flutter оперує трьома деревами: Widget Tree (конфігурація, створюється розробником), Element Tree (зіставлення віджета з позицією в дереві, керується фреймворком), Render Tree (об'єкти, що обчислюють layout та виконують paint). Widget створює Element, який створює RenderObject. Element перевикористовується при зміні конфігурації (rebuild) — це механізм оптимізації Flutter: якщо тип віджета та key збігаються, Element не перестворюється, а оновлюється нова конфігурація. Розуміння трьох дерев — ключ до оптимізації продуктивності Flutter-додатків.

StatelessWidget: незмінні віджети

StatelessWidget — віджет, який не зберігає внутрішній стан. Він приймає параметри через конструктор і відображає їх без можливості зміни після збірки. StatelessWidget ідеально підходить для статичних елементів: заголовків, іконок, аватарів, списків із фіксованими даними. Метод build() викликається один раз при створенні та при зміні батьківського віджета. Оскільки StatelessWidget не має стану, він не потребує окремого State-об'єкта — build() визначений прямо в класі віджета.

dart
import 'package:flutter/material.dart';

class ProfileHeader extends StatelessWidget {
  final String name;
  final String avatarUrl;

  const ProfileHeader({super.key, required this.name, required this.avatarUrl});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        CircleAvatar(
          radius: 24,
          backgroundImage: NetworkImage(avatarUrl),
        ),
        const SizedBox(width: 12),
        Text(
          name,
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
      ],
    );
  }
}

ProfileHeader — StatelessWidget, що приймає name та avatarUrl через конструктор. CircleAvatar відображає аватар, Text — ім'я. Віджет не може змінити ці значення після створення. Використання const конструктора (const ProfileHeader) дозволяє Flutter перевикористовувати екземпляр віджета при rebuild батька — це мікрооптимізація, рекомендована для всіх StatelessWidget.

StatefulWidget: віджети зі станом

StatefulWidget — віджет із змінним станом, який зберігається в окремому об'єкті State. Сам StatefulWidget — незмінний (immutable), як і StatelessWidget. Змінна частина винесена в State: State — об'єкт, який створюється один раз і переживає множинні ребілди віджета. Коли стан змінюється (setState()), Flutter викликає build() State, оновлюючи UI. StatefulWidget використовується для інтерактивних елементів: лічильники, текстові поля, чекбокси, анімації, таймери.

dart
import 'package:flutter/material.dart';

class LikeButton extends StatefulWidget {
  const LikeButton({super.key});

  @override
  State<LikeButton> createState() => _LikeButtonState();
}

class _LikeButtonState extends State<LikeButton> {
  bool liked = false;
  int count = 42;

  void _toggleLike() {
    setState(() {
      liked = !liked;
      count += liked ? 1 : -1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _toggleLike,
      child: Text(liked ? '♥ $count' : '♡ $count'),
    );
  }
}

LikeButton — StatefulWidget з State-класом _LikeButtonState. Поля liked та count — стан, який зберігається в State і не скидається при rebuild. setState() повідомляє Flutter про необхідність перебудувати UI. Flutter не відтворює State при кожному батьківському rebuild — тільки при зміні конфігурації (key або тип віджета). В IT Sectr ми використовуємо StatelessWidget для відображення та StatefulWidget для інтерактивних компонентів, дотримуючись принципу мінімізації StatefulWidget до меж мутації.

Дерево віджетів: від Widget до RenderObject

Widget Tree — ієрархічна структура віджетів, що описує UI додатка. Flutter перетворює Widget Tree в Element Tree: кожен Widget створює Element, який відстежує позицію в дереві та зв'язок з RenderObject. Render Tree виконує layout (визначення розмірів та позицій) та paint (відображення). Element — ключовий оптимізаційний шар: якщо тип віджета та key не змінилися при rebuild, Element перевикористовується, оновлюючи конфігурацію без перестворення RenderObject. Це забезпечує продуктивність Flutter при високій частоті кадрів (60/120 FPS).

dart
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter віджети')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.flutter_dash, size: 80),
            const SizedBox(height: 16),
            const Text(
              'Привіт, Flutter!',
              style: TextStyle(fontSize: 24),
            ),
          ],
        ),
      ),
    );
  }
}

Дерево віджетів HomeScreen: Scaffold → AppBar + Center → Column → [Icon, SizedBox, Text]. Scaffold — кореневий layout-віджет Material Design. Center центрує дочірній Column. Column розташовує дітей вертикально з вирівнюванням по центру (mainAxisAlignment.center). Flutter обчислює layout зверху вниз (constraints передаються від батька), а розміри — знизу вгору (sizedByParent або sizing from children).

BuildContext: контекст збірки та доступ до дерева

BuildContext — дескриптор позиції віджета в Element Tree. Кожен метод build() отримує BuildContext, через який віджет може отримати доступ до: MediaQuery (розмір екрана, орієнтація, щільність пікселів, safe area), Theme (MaterialThemeData або CupertinoThemeData), Navigator (навігаційний стек), ScaffoldMessenger (SnackBar), InheritedWidget (прокидання даних вниз по дереву). BuildContext не повинен зберігатися після завершення build() або використовуватися поза синхронним контекстом — це призводить до помилок “Scaffold.of() called with a context that does not contain a Scaffold”. Замість збереження context використовуйте Builder-віджет або патерн InheritedWidget для доступу до даних.

Key: ідентифікація віджета в дереві

Key — опціональний параметр конструктора кожного віджета (super.key). Key допомагає Flutter ідентифікувати віджет при зміні порядку/кількості елементів у списку. Без key Flutter порівнює віджети за типом та позицією; при додаванні елемента в середину списку без key Flutter перевикористовує Element для неправильного віджета, викликаючи баги зі станом (StatefulWidget) та анімацією. Використовуйте ValueKey (унікальний ідентифікатор) для списків, ObjectKey (на основі об'єкта даних), UniqueKey (гарантовано унікальний при кожному rebuild — для форсованого перестворення).

Каталог віджетів Flutter: Material, Cupertino, Layout

Flutter SDK (стабільна версія 3.3+, Dart 3+) включає 200+ вбудованих віджетів, розділених на категорії: Layout (Row, Column, Stack, Container, Expanded, Flexible, Wrap, GridView, ListView, CustomScrollView), Material Design (Scaffold, AppBar, Card, Drawer, BottomNavigationBar, FloatingActionButton, TabBar, Chip, DataTable), Cupertino (CupertinoPageScaffold, CupertinoNavigationBar, CupertinoButton, CupertinoTabBar), Painting & Effects (Opacity, DecoratedBox, ClipRect, Transform, BackdropFilter, ShaderMask), Async (FutureBuilder, StreamBuilder) та Accessibility (Semantics, MergeSemantics, ExcludeSemantics). Layout-віджети — основа будь-якого Flutter UI: Row для горизонтальних послідовностей, Column для вертикальних, Stack для перекривних елементів, Container з BoxDecoration для декорування.

КатегоріяКлючові віджетиПризначення
LayoutRow, Column, Stack, Container, Expanded, FlexibleПозиціонування та розміри дочірніх елементів
MaterialScaffold, AppBar, Card, Drawer, FAB, TabBarUI-компоненти Material Design 3
CupertinoCupertinoPageScaffold, CupertinoNavigationBar, CupertinoButtoniOS-стилізовані компоненти
PaintingOpacity, ClipRect, Transform, DecoratedBox, BackdropFilterВізуальні ефекти та кліпінг
AsyncFutureBuilder, StreamBuilderАсинхронне завантаження даних з UI-станами
AccessibilitySemantics, MergeSemanticsСемантична розмітка для screen readers

Часті запитання

Чим StatelessWidget відрізняється від StatefulWidget?

StatelessWidget не має внутрішнього стану — всі дані передаються через конструктор і не змінюються. StatefulWidget зберігає змінний стан в окремому об'єкті State, який переживає ребілди. Зміна стану викликає setState(), і Flutter перебудовує UI. Для незмінних елементів використовуйте StatelessWidget; для інтерактивних — StatefulWidget.

Що таке BuildContext у Flutter?

BuildContext — дескриптор позиції віджета в Element Tree, що передається в метод build(). Через context віджет отримує доступ до MediaQuery, Theme, Navigator, ScaffoldMessenger та InheritedWidget. BuildContext не повинен зберігатися після завершення build(). Для доступу до даних використовуйте Builder-віджет або InheritedWidget.

Чи потрібно знати Dart для роботи з Flutter?

Так, Flutter використовує Dart як єдину мову програмування. Dart — статично типізована мова з null-safety, звуковими типами (sound null safety), extension methods та pattern matching. Для початку достатньо основ Dart: класи, функції, async/await, колекції. Dart компілюється в нативний код (AOT) через Dart Native та в JavaScript (для вебу) через Dart2JS.

Flutter — це тільки для мобільних платформ?

Ні, Flutter підтримує шість платформ: iOS, Android, Web, macOS, Windows, Linux. Більше 90% коду спільне для всіх платформ. Платформені адаптації (камера, файлова система) реалізуються через плагіни або platform channels. Для десктопа Flutter використовує віконну оболонку Flutter Desktop, для вебу — двигун CanvasKit (WebGL) або HTML-рендерер.

Як Flutter забезпечує продуктивність 60 FPS?

Flutter досягає 60/120 FPS за рахунок: власного 2D-двигуна (Skia/Impeller), відсутності нативного bridge (на відміну від React Native), трьох дерев (Widget → Element → Render) з перевикористанням Element при незмінному типі/key, layout-алгоритму з однонаправленим проходом (constraints down, sizes up) та RepaintBoundary для ізоляції перемальовування окремих зон екрана.

Підсумки

  • Flutter Widgets — всі елементи UI: від Scaffold до SizedBox; Flutter малює їх сам на Skia/Impeller, не використовуючи нативні компоненти.
  • StatelessWidget — віджет без стану; build() повертає UI на основі незмінних параметрів конструктора.
  • StatefulWidget — віджет із змінним станом у State; setState() викликає ребілд UI.
  • Три дерева Flutter: Widget (конфігурація) → Element (позиція) → Render (layout + paint); Element перевикористовується для продуктивності.
  • BuildContext — позиція віджета в дереві; доступ до MediaQuery, Theme, Navigator, InheritedWidget.
  • Material та Cupertino — два набори віджетів для Google Material Design 3 та Apple HIG відповідно.
  • Key (ValueKey, ObjectKey, UniqueKey) — ідентифікація віджета при зміні порядку/кількості в списку.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також