RenderObject Tree: що це, принципи рендерингу та роль у Flutter

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

RenderObject Tree — це третій рівень ієрархії Flutter, відповідальний за фактичне відображення інтерфейсу на екрані. Кожен вузол цього дерева виконує обчислення розмірів (layout) та малювання (painting), перетворюючи конфігурацію віджетів в пікселі. На відміну від Widget Tree, RenderObject Tree створюється один раз і оновлюється лише при зміні геометрії або зовнішнього вигляду. Згідно з Flutter API Reference, 2025, ефективність RenderObject Tree безпосередньо визначає плавність анімацій та швидкість відгуку додатка.

Головне

  • RenderObject Tree — це ієрархія об’єктів відображення, які обчислюють розміри та малюють інтерфейс на екрані.
  • Кожен RenderObject виконує дві фази: layout (визначення розмірів та позицій) і paint (малювання пікселів).
  • RenderObject Tree створюється з Element Tree та синхронізується з ним через елементи.
  • RepaintBoundary ізолює частину дерева для локального перемалювання без зміни всього екрану.
  • Продуктивність RenderObject Tree залежить від глибини, кількості шарів та частоти перемалювання.

Що таке RenderObject Tree в Flutter?

RenderObject Tree — це ієрархічна структура об’єктів відображення, яка служить фактичним представництвом інтерфейсу в пам’яті. Кожен RenderObject знає свій розмір, положення на екрані та як себе намалювати. На відміну від віджетів, які легкі та можуть перестворюватися сотні разів на секунду, RenderObject — це важкий об’єкт з прямим доступом до графічного конвеєра.

Від конфігурації до відображення

Шлях від віджету до пікселів проходить через три етапи: Widget Tree описує, що має бути на екрані; Element Tree керує життєвим циклом; RenderObject Tree виконує фактичну роботу. Кожен RenderObjectWidget (наприклад, Padding, Transform, CustomPaint) створює RenderObject, який додається до дерева відображення. RenderBox — найпоширеніший тип RenderObject, який використовується в 99% стандартних віджетів.

Архітектура RenderObject

RenderObject — це абстрактний клас, що визначає інтерфейс для layout та paint. Він містить посилання на батьківський RenderObject та дочірні об’єкти, а також абстрактні методи: performLayout, paint та hitTest. Кожен конкретний RenderObject реалізує ці методи відповідно до своєї поведінки: RenderFlex розподіляє простір між дітьми, RenderImage відображає зображення, RenderParagraph відображає текст.

dart
abstract class RenderObject {
  RenderObject? parent;
  Constraints constraints;
  ParentData? parentData;
  bool _needsLayout = true;
  bool _needsPaint = true;

  void performLayout();
  void paint(PaintingContext context, Offset offset);
}

У цій спрощеній структурі RenderObject містить прапори _needsLayout та _needsPaint, які позначають об’єкт як такий, що потребує оновлення. Коли Widget Tree змінюється, Element Tree позначає відповідні RenderObject як «брудні» для layout або paint, і на наступному кадрі Flutter виконує лише необхідні операції.

Дві фази RenderObject: layout і paint

Кожен RenderObject виконує дві ключові фази: layout (визначення розмірів та позицій) і paint (малювання). Ці фази виконуються в суворому порядку: спочатку layout для всього дерева, потім paint. Фазу paint можна пропустити, якщо layout не змінився, що економить ресурси GPU.

Фаза layout: обмеження та розміри

Під час layout Flutter передає обмеження (constraints) зверху вниз через RenderObject Tree. Кожен батьківський елемент встановлює мінімальну та максимальну ширину та висоту для свого дочірного елемента. Дочірний елемент обчислює свій розмір у межах цих обмежень і повертає його батьківському елементу. Цей процес називається «низхідний прохід» (передача обмежень вниз) та «висхідний прохід» (передача розмірів вгору).

  • BoxConstraints — найпоширеніший тип обмежень: minWidth, maxWidth, minHeight, maxHeight.
  • SliverConstraints використовується в ScrollView для віртуалізації: додає інформацію про прокрутку до стандартних обмежень.
  • RenderSliverMultiBoxAdaptor керує layout віртуалізованих списків, створюючи RenderObject лише для видимих елементів.

Фаза paint: відображення на екрані

Після завершення layout Flutter виконує paint — малювання кожного RenderObject. Батьківський елемент створює PaintingContext, передає його дочірним об’єктам та вказує зміщення (offset). Кожен RenderObject малює себе на Canvas використовуючи графічні примітиви: прямокутники, кола, текст, зображення та трансформації. Canvas взаємодіє безпосередньо з Skia або Impeller.

Позначення брудних вузлів

Flutter використовує механізм «брудних» вузлів для мінімізації роботи. Коли змінюється лише розмір одного RenderObject, Flutter не перераховує layout всього дерева — він позначає змінений вузол та його можливих предків до найближчого RenderBox з фіксованим розміром. Аналогічно, при зміні зовнішнього вигляду позначається лише paint без повторного layout.

Типи RenderObject в Flutter

Flutter надає кілька категорій RenderObject для різних завдань: RenderBox для стандартних прямокутних елементів, RenderSliver для областей прокрутки та користувацькі RenderObject для нестандартного відображення через CustomPainter. Кожен тип оптимізований для своєї ролі в дереві відображення.

RenderBox та його підтипи

RenderBox — базовий клас для прямокутних елементів інтерфейсу. Його підтипи включають: RenderPadding (додає відступи), RenderTransform (застосовує трансформації), RenderFlex (реалізує Row та Column), RenderStack (накладає елементи), RenderImage (відображає зображення), RenderParagraph (відображає текст). Кожен підтип перевизначає performLayout та paint для своєї логіки.

RenderSliver та віртуалізація

RenderSliver — це тип RenderObject для роботи з областями прокрутки. На відміну від RenderBox, Sliver використовує SliverConstraints, які включають інформацію про вікно прогляду (viewport). RenderSliverList та RenderSliverGrid створюють RenderObject лише для елементів у видимій області, що дозволяє обробляти списки з мільйонами записів.

Користувацький RenderObject через CustomPainter

Для нестандартної графіки використовуйте CustomPaint та CustomPainter. CustomPaint створює RenderCustomPaint, який викликає методи paint класу CustomPainter. Це дозволяє малювати довільні фігури, графіки та анімації з повним контролем над Canvas. За даними команди Flutter, CustomPainter ефективніший за вкладені стандартні віджети для складної векторної графіки.

dart
class CirclePainter extends CustomPainter {
  final Color color;

  CirclePainter({required this.color});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = color;
    canvas.drawCircle(
      Offset(size.width / 2, size.height / 2),
      size.width / 3,
      paint,
    );
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) =>
    oldDelegate.color != color;
}

У цьому прикладі CirclePainter малює коло на Canvas. Метод shouldRepaint повертає true лише при зміні кольору, що запобігає непотрібному перемалюванню. CustomPainter має бути якомога легшим — виконуйте всі важкі обчислення поза методами paint.

RenderObject Tree створюється з Element Tree через механізм RenderObjectWidget. Кожен RenderObjectWidget (Padding, Transform, CustomPaint) створює RenderObjectElement, який свою чергу створює та керує відповідним RenderObject. Елемент виступає посередником: він передає конфігурацію від віджету до RenderObject та сповіщає RenderObject про зміни.

Створення RenderObject з елемента

Коли RenderObjectElement монтується, він викликає метод createRenderObject свого віджету. Віджет створює екземпляр RenderObject та повертає його елементу. Елемент вставляє RenderObject в RenderObject Tree, викликаючи метод insertChildLayout на батьківському RenderObject. Цей процес відбувається лише при першому монтуванні — при подальших оновленнях елемент просто оновлює параметри існуючого RenderObject.

Синхронізація через updateRenderObject

Коли конфігурація віджету змінюється (наприклад, змінюється значення padding), елемент викликає метод updateRenderObject, який передає нову конфігурацію існуючому RenderObject. RenderObject позначає себе як «брудний» для layout або paint, і на наступному кадрі фреймворк виконує необхідні оновлення.

Видалення RenderObject

Коли елемент демонтується, викликається метод unmount, який видаляє RenderObject з RenderObject Tree та звільняє ресурси. RenderObject.remove викликається для від’єднання від батька, після чого об’єкт може бути зібраний збірником сміття. Flutter гарантує, що жоден RenderObject не висить в дереві без відповідного елемента.

RepaintBoundary та ізоляція відображення

RepaintBoundary — це віджет, який створює окремий шар для відображення свого вмісту. Коли вміст всередині RepaintBoundary змінюється, лише цей шар перемальовується, а решта екрану залишається незмінною. RepaintBoundary особливо корисний для анімацій, відеоплеерів, інтерактивних графіків та інших елементів, які часто оновлюються.

Як працює RepaintBoundary

В основі RepaintBoundary лежить RenderRepaintBoundary — спеціальний RenderObject, який створює окремий PictureLayer. При першому відображенні RenderRepaintBoundary записує графічні команди в цей шар. При подальших оновленнях, якщо змінюється лише вміст всередині RepaintBoundary, Flutter перемальовує лише цей шар, а не весь екран. Інші шари залишаються незмінними та використовуються повторно.

Коли використовувати RepaintBoundary

Не кожен віджет потребує RepaintBoundary. Використовуйте його, коли частина інтерфейсу оновлюється з високою частотою (60 FPS або вище), а решта екрану статична. Типові приклади: анімований індикатор завантаження, відеоплеер, ігровий Canvas, CustomPainter з частим перемалюванням. Для статичного тексту або кнопок RepaintBoundary надлишковий і лише збільшує споживання пам’яті.

СценарійРекомендаціяПричина
АнімаціяОбгорнути в RepaintBoundaryІзолює область, яка часто оновлюється
Список прокруткиНе потрібноListView автоматично використовує шари
Статичний текстНе потрібноНемає частого перемалювання
CustomPainterРекомендованоЧасте графічне перемалювання

Оптимізація RenderObject Tree для продуктивності

Продуктивність RenderObject Tree залежить від кількості вузлів, глибини дерева та частоти перемалювання. Flutter DevTools (вкладка «Rendering») дозволяє аналізувати RenderObject Tree в реальному часі: кількість перемалювань, час layout та paint, кількість шарів та їхні розміри. Регулярний аналіз допомагає виявити вузькі місця.

Уникайте надмірного overdraw

Overdraw — це ситуація, коли один піксель малюється кілька разів за кадр. Наприклад, коли напівпрозорий віджет перекриває інший, GPU малює обидва шари. Використовуйте непрозорі прапори (Container з color замість decoration) для непрозорих елементів, щоб Flutter пропускав невидимі шари. За даними команди Flutter, зменшення overdraw може скоротити час paint до 30%.

Мінімізуйте кількість шарів

Кожен RepaintBoundary та деякі віджети (Opacity, ClipRRect, Transform) створюють окремий шар (PictureLayer). Занадто багато шарів збільшує час композиції. Використовуйте групування: замість кількох віджетів Opacity на окремих елементах, застосуйте один Opacity на контейнер. Замість ClipRRect на кожному елементі, використовуйте ClipRRect на спільному контейнері.

Використовуйте const-конструктори

Коли віджет оголошується як const, Flutter знає, що його конфігурація не зміниться, і може повторно використати відповідний RenderObject без перестворення. const-конструктори зменшують навантаження на збірник сміття та прискорюють перший кадр. Використовуйте const для віджетів з фіксованими параметрами: іконки, заголовки, декоративні елементи.

dart
const Text("Назва", style: TextStyle(fontSize: 24));
const Icon(Icons.home, color: Colors.blue);
const SizedBox.shrink();

Часто запитувані питання

Чим RenderObject Tree відрізняється від Widget Tree?

Widget Tree — це легка конфігурація інтерфейсу, яка перестворюється при кожному перебудовуванні. RenderObject Tree — це важке дерево об’єктів відображення, яке зберігається та оновлюється лише при зміні геометрії або зовнішнього вигляду.

Як побачити RenderObject Tree в налагоджувачі?

Використовуйте Flutter DevTools — вкладка «Rendering». Ви побачите шари відображення, час layout та paint, а також детальну інформацію про кожен RenderObject: розміри, обмеження та прапори брудності.

Що таке брудний RenderObject?

Dirty — це RenderObject, позначений як такий, що потребує оновлення. Прапорець _needsLayout або _needsPaint встановлюється при зміні конфігурації, і Flutter виконує layout або paint для цього вузла на наступному кадрі.

Чи можна створити власний RenderObject?

Так, створіть підклас RenderBox та перевизначте методи performLayout та paint. Використовуйте RenderObjectWidget для вбудовування користувацького RenderObject в Widget Tree. Це просунута техніка для нестандартного відображення.

Як RepaintBoundary впливає на RenderObject Tree?

RepaintBoundary створює RenderRepaintBoundary, який ізолює частину RenderObject Tree в окремому шарі. Коли вміст змінюється, лише цей шар перемальовується, а решта дерева залишається незмінною.

Підсумки

  • RenderObject Tree — третій рівень архітектури Flutter, відповідальний за layout та paint кожного елемента інтерфейсу.
  • Кожен RenderObject виконує дві фази: layout (обчислення розмірів та позицій) та paint (малювання пікселів через Canvas).
  • RenderBox — базовий тип для стандартних елементів, RenderSliver для віртуалізації прокручуваних списків.
  • RenderObject Tree синхронізується з Element Tree через RenderObjectElement, який створює та оновлює RenderObject.
  • RepaintBoundary ізолює частину дерева в окремому шарі, запобігаючи повному перемалюванню екрану при локальних змінах.
  • Продуктивність залежить від глибини дерева, кількості шарів, overdraw та використання const-конструкторів.
  • Flutter DevTools надає інструменти для аналізу RenderObject Tree: час layout, час paint, кількість шарів та перемалювань.

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

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

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

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