RenderObject Tree: co to jest, zasada renderowania i rola we Flutter

Autor: IT Sectr Opublikowano: 2026-07-01 Czas czytania: 9 min

RenderObject Tree — to trzeci poziom hierarchii we Flutter, odpowiedzialny za faktyczne renderowanie interfejsu na ekranie. Każdy węzeł tego drzewa wykonuje obliczanie rozmiarów (layout) i rysowanie (painting), przekształcając konfigurację widgetów w piksele. W przeciwieństwie do Widget Tree, RenderObject Tree jest tworzony raz i aktualizowany tylko przy zmianie geometrii lub wyglądu. Według danych Flutter API Reference, 2025, wydajność RenderObject Tree bezpośrednio określa płynność animacji i szybkość reakcji aplikacji.

Najważniejsze

  • RenderObject Tree — hierarchia obiektów renderowania, które obliczają rozmiary i rysują interfejs na ekranie.
  • Każdy RenderObject wykonuje dwie fazy: layout (określanie rozmiarów i pozycji) oraz paint (rysowanie pikseli).
  • RenderObject Tree jest tworzony z Element Tree i synchronizowany z nim poprzez elementy.
  • RepaintBoundary izoluje część drzewa do lokalnego przerysowania bez wpływu na cały ekran.
  • Wydajność RenderObject Tree zależy od głębokości, liczby warstw i częstotliwości przerysowań.

Czym jest RenderObject Tree we Flutter?

RenderObject Tree — to hierarchiczna struktura obiektów renderowania, która jest faktycznym reprezentacją interfejsu w pamięci. Każdy RenderObject zna swoje rozmiary, położenie na ekranie i potrafi się narysować. W przeciwieństwie do widgetów, które są lekkie i mogą być odtwarzane setki razy na sekundę, RenderObject — to ciężki obiekt z bezpośrednim dostępem do potoku graficznego.

Od konfiguracji do renderowania

Ścieżka od widgetu do pikseli przechodzi trzy etapy: Widget Tree opisuje, co powinno być na ekranie; Element Tree zarządza cyklem życia; RenderObject Tree wykonuje faktyczną pracę. Każdy RenderObjectWidget (np. Padding, Transform, CustomPaint) tworzy RenderObject, który jest dodawany do drzewa renderowania. RenderBox — najczęściej używany typ RenderObject, stosowany dla 99% standardowych widgetów.

Architektura RenderObject

RenderObject — to abstrakcyjna klasa definiująca interfejs dla layout i paint. Zawiera odniesienia do nadrzędnego RenderObject i obiektów potomnych, a także abstrakcyjne metody: performLayout, paint i hitTest. Każdy konkretny RenderObject implementuje te metody zgodnie ze swoim zachowaniem: RenderFlex rozdziela przestrzeń między elementami potomnymi, RenderImage wyświetla obraz, RenderParagraph wyświetla tekst.

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

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

W tej uproszczonej strukturze RenderObject zawiera flagi _needsLayout i _needsPaint, które oznaczają obiekt jako wymagający aktualizacji. Gdy Widget Tree się zmienia, Element Tree oznacza odpowiednie RenderObject jako „brudne„ dla layout lub paint, a przy następnej klatce Flutter wykonuje tylko niezbędne operacje.

Dwie fazy RenderObject: layout i paint

Każdy RenderObject wykonuje dwie kluczowe fazy: layout (określanie rozmiarów i pozycji) oraz paint (rysowanie). Te fazy są wykonywane w ścisłej kolejności: najpierw layout dla całego drzewa, potem paint. Faza paint może zostać pominięta, jeśli layout się nie zmienił, co oszczędza zasoby GPU.

Faza layout: ograniczenia i rozmiary

Podczas layout Flutter przekazuje ograniczenia (constraints) z góry na dół przez RenderObject Tree. Każdy rodzic określa dla obiektu potomnego minimalną i maksymalną szerokość oraz wysokość. Obiekt potomny oblicza swój rozmiar w ramach tych ograniczeń i zwraca go rodzicowi. Ten proces nazywa się „downward pass„ (przekazywanie ograniczeń w dół) i „upward pass„ (przekazywanie rozmiarów w górę).

  • BoxConstraints — najczęściej używany typ ograniczeń: minWidth, maxWidth, minHeight, maxHeight.
  • SliverConstraints — używany w ScrollView do wirtualizacji: do standardowych ograniczeń dodawana jest informacja o przewijaniu.
  • RenderSliverMultiBoxAdaptor — zarządza layoutem zwirtualizowanych list, tworząc RenderObject tylko dla widocznych elementów.

Faza paint: wyświetlanie na ekranie

Po zakończeniu layout Flutter wykonuje paint — rysowanie każdego RenderObject. Rodzic tworzy PaintingContext, przekazuje go obiektom potomnym i określa przesunięcie (offset). Każdy RenderObject rysuje siebie na płótnie Canvas, używając prymitywów graficznych: prostokąty, koła, tekst, obrazy i transformacje. Canvas bezpośrednio współdziała z Skia lub Impeller.

Oznaczanie brudnych węzłów

Flutter używa mechanizmu „brudnych„ węzłów do minimalizacji pracy. Gdy zmienia się tylko rozmiar jednego RenderObject, Flutter nie przelicza layoutu całego drzewa — oznacza zmieniony węzeł i jego potencjalnych przodków aż do najbliższego RenderBox o stałym rozmiarze. Podobnie, przy zmianie wyglądu oznaczany jest tylko paint, bez ponownego layoutu.

Typy RenderObject we Flutter

Flutter udostępnia kilka kategorii RenderObject do różnych zadań: RenderBox dla standardowych prostokątnych elementów, RenderSliver dla obszarów przewijanych oraz niestandardowe RenderObject do nietypowego renderowania przez CustomPainter. Każdy typ jest zoptymalizowany do swojej roli w drzewie renderowania.

RenderBox i jego podtypy

RenderBox — klasa bazowa dla prostokątnych elementów interfejsu. Jego podtypy: RenderPadding (dodaje odstępy), RenderTransform (stosuje transformacje), RenderFlex (implementuje Row i Column), RenderStack (nakładanie elementów), RenderImage (wyświetlanie obrazów), RenderParagraph (wyświetlanie tekstu). Każdy podtyp nadpisuje performLayout i paint dla własnej logiki.

RenderSliver i wirtualizacja

RenderSliver — to typ RenderObject do pracy z obszarami przewijanymi. W przeciwieństwie do RenderBox, Sliver używa SliverConstraints, które zawierają informacje o widocznym obszarze (viewport). RenderSliverList i RenderSliverGrid tworzą RenderObject tylko dla elementów znajdujących się w widocznym obszarze, co pozwala na obsługę list z milionami rekordów.

Niestandardowy RenderObject przez CustomPainter

Do niestandardowej grafiki użyj CustomPaint i CustomPainter. CustomPaint tworzy RenderCustomPaint, który wywołuje metody paint w CustomPainter. Pozwala to na rysowanie dowolnych kształtów, wykresów i animacji z pełną kontrolą nad Canvas. Według Flutter Team, CustomPainter jest bardziej wydajny niż zagnieżdżone standardowe widgety dla złożonej grafiki wektorowej.

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;
}

W tym przykładzie CirclePainter rysuje okrąg na Canvas. Metoda shouldRepaint zwraca true tylko przy zmianie koloru, co zapobiega niepotrzebnemu przerysowaniu. CustomPainter powinien być maksymalnie lekki — wszystkie ciężkie obliczenia wykonuj poza metodami paint.

RenderObject Tree jest tworzony z Element Tree poprzez mechanizm RenderObjectWidget. Każdy RenderObjectWidget (Padding, Transform, CustomPaint) tworzy RenderObjectElement, który tworzy i zarządza odpowiednim RenderObject. Element pełni rolę pośrednika: przekazuje konfigurację z widgetu do RenderObject i powiadamia RenderObject o zmianach.

Tworzenie RenderObject z elementu

Gdy RenderObjectElement jest montowany, wywołuje metodę createRenderObject swojego widgetu. Widget tworzy instancję RenderObject i zwraca ją elementowi. Element osadza RenderObject w RenderObject Tree, wywołując metodę insertChildLayout u nadrzędnego RenderObject. Ten proces zachodzi tylko przy pierwszym montowaniu — przy kolejnych aktualizacjach element po prostu aktualizuje parametry istniejącego RenderObject.

Synchronizacja przez updateRenderObject

Gdy konfiguracja widgetu się zmienia (np. zmienia się wartość padding), element wywołuje metodę updateRenderObject, która przekazuje nową konfigurację istniejącemu RenderObject. RenderObject oznacza siebie jako „brudny„ dla layout lub paint, a przy następnej klatce framework wykonuje niezbędne aktualizacje.

Usuwanie RenderObject

Przy demontażu elementu wywoływana jest metoda unmount, która usuwa RenderObject z RenderObject Tree i zwalnia zasoby. RenderObject.remove jest wywoływany w celu odłączenia od rodzica, po czym obiekt może zostać zebrany przez garbage collector. Flutter gwarantuje, że RenderObject nie wisi w drzewie bez odpowiadającego mu elementu.

RepaintBoundary i izolacja renderowania

RepaintBoundary — to widget, który tworzy oddzielną warstwę do renderowania swojej zawartości. Gdy zawartość RepaintBoundary się zmienia, przerysowywana jest tylko ta warstwa, a pozostałe części ekranu pozostają bez zmian. RepaintBoundary jest szczególnie przydatny dla animacji, odtwarzaczy wideo, interaktywnych wykresów i innych często aktualizowanych elementów.

Jak działa RepaintBoundary

U podstaw RepaintBoundary leży RenderRepaintBoundary — specjalny RenderObject, który tworzy oddzielną warstwę PictureLayer. Przy pierwszym renderowaniu RenderRepaintBoundary zapisuje polecenia graficzne w tej warstwie. Przy kolejnych aktualizacjach, jeśli zmieniła się tylko zawartość wewnątrz RepaintBoundary, Flutter przerysowuje tylko tę warstwę, a nie cały ekran. Pozostałe warstwy pozostają niezmienione i są ponownie używane.

Kiedy używać RepaintBoundary

Nie każdy widget potrzebuje RepaintBoundary. Używaj go, gdy część interfejsu aktualizuje się z wysoką częstotliwością (60 FPS i więcej), a reszta ekranu jest statyczna. Typowe przykłady: animowany wskaźnik ładowania, odtwarzacz wideo, płótno gry, CustomPainter z częstym przerysowaniem. Dla statycznego tekstu lub przycisków RepaintBoundary jest zbędny i tylko zwiększa zużycie pamięci.

ScenariuszZaleceniePowód
AnimacjaOpakować w RepaintBoundaryIzoluje często aktualizowany obszar
Lista przewijanaNie wymaganeListView automatycznie używa warstw
Tekst statycznyNie wymaganeBrak częstych przerysowań
CustomPainterZalecaneCzęste przerysowanie grafiki

Optymalizacja RenderObject Tree dla wydajności

Wydajność RenderObject Tree zależy od liczby węzłów, głębokości drzewa i częstotliwości przerysowań. Narzędzia Flutter DevTools (zakładka „Rendering„) pozwalają analizować RenderObject Tree w czasie rzeczywistym: liczba repaint, czas layout i paint, liczba warstw i ich rozmiary. Regularna analiza pomaga zidentyfikować wąskie gardła.

Unikaj nadmiernego overdraw

Overdraw — to sytuacja, gdy jeden piksel jest rysowany kilka razy na klatkę. Na przykład, gdy półprzezroczysty widget nakłada się na inny, GPU rysuje obie warstwy. Używaj przezroczystych flag (Container z color zamiast decoration) dla nieprzezroczystych elementów, aby Flutter pomijał niewidoczne warstwy. Według Flutter Team, zmniejszenie overdraw może skrócić czas paint do 30%.

Minimalizuj liczbę warstw

Każdy RepaintBoundary i niektóre widgety (Opacity, ClipRRect, Transform) tworzą oddzielną warstwę (PictureLayer). Zbyt wiele warstw zwiększa czas kompozycji. Używaj grupowania: zamiast wielu Opacity na oddzielnych widgetach zastosuj jeden Opacity na kontenerze. Zamiast ClipRRect na każdym elemencie użyj ClipRRect na wspólnym kontenerze.

Używaj const-konstruktorów

Gdy widget jest zadeklarowany jako const, Flutter wie, że jego konfiguracja się nie zmieni i może ponownie użyć odpowiedniego RenderObject bez odtwarzania. Konstruktory const zmniejszają obciążenie garbage collectora i przyspieszają pierwszą klatkę. Używaj const dla widgetów o stałych parametrach: ikony, nagłówki, elementy dekoracyjne.

dart
const Text("Tytuł", style: TextStyle(fontSize: 24));
const Icon(Icons.home, color: Colors.blue);
const SizedBox.shrink();

Często zadawane pytania

Czym RenderObject Tree różni się od Widget Tree?

Widget Tree — to lekka konfiguracja interfejsu, która jest odtwarzana przy każdym rebuild. RenderObject Tree — to ciężkie drzewo obiektów renderowania, które jest zachowywane i aktualizowane tylko przy zmianie geometrii lub wyglądu.

Jak zobaczyć RenderObject Tree w debuggerze?

Użyj Flutter DevTools — zakładka „Rendering„. Zobaczysz warstwy renderowania, czas layout i paint, a także szczegółowe informacje o każdym RenderObject: rozmiary, constraints, flagi potrzeby aktualizacji.

Czym jest dirty RenderObject?

Dirty — to RenderObject, który jest oznaczony jako wymagający aktualizacji. Flaga _needsLayout lub _needsPaint jest ustawiana przy zmianie konfiguracji, a Flutter wykonuje layout lub paint dla tego węzła w następnej klatce.

Czy można stworzyć własny RenderObject?

Tak, utwórz podklasę RenderBox i nadpisz metody performLayout oraz paint. Użyj RenderObjectWidget do osadzenia niestandardowego RenderObject w Widget Tree. To zaawansowana technika do niestandardowego renderowania.

Jak RepaintBoundary wpływa na RenderObject Tree?

RepaintBoundary tworzy RenderRepaintBoundary, który izoluje część RenderObject Tree w oddzielnej warstwie. Przy zmianie zawartości przerysowywana jest tylko ta warstwa, a reszta drzewa pozostaje bez zmian.

Podsumowanie

  • RenderObject Tree — trzeci poziom architektury Flutter, odpowiedzialny za layout i paint każdego elementu interfejsu.
  • Każdy RenderObject wykonuje dwie fazy: layout (obliczanie rozmiarów i pozycji) oraz paint (rysowanie pikseli przez Canvas).
  • RenderBox — podstawowy typ dla standardowych elementów, RenderSliver — do wirtualizacji przewijanych list.
  • RenderObject Tree jest synchronizowany z Element Tree przez RenderObjectElement, który tworzy i aktualizuje RenderObject.
  • RepaintBoundary izoluje część drzewa w oddzielnej warstwie, zapobiegając pełnemu przerysowaniu ekranu przy lokalnych zmianach.
  • Wydajność zależy od głębokości drzewa, liczby warstw, overdraw i używania const-konstruktorów.
  • Flutter DevTools udostępnia narzędzia do analizy RenderObject Tree: czas layout, paint, liczba warstw i repaint.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również