RenderObject Tree — a Flutter hierarchia harmadik szintje, amely a felület tényleges képernyőn történő megjelenítéséért felelős. Ennek a fának minden csomópontja elvégzi a méretek kiszámítását (layout) és a rajzolást (painting), átalakítva a widgetek konfigurációját pixelekké. A Widget Tree-től eltérően a RenderObject Tree egyszer jön létre, és csak a geometria vagy a megjelenés megváltozásakor frissül. A Flutter API Reference, 2025 szerint a RenderObject Tree hatékonysága közvetlenül meghatározza az animációk simaságát és az alkalmazás válaszsebességét.
Főbb pontok
RenderObject Tree — a megjelenítési objektumok hierarchikus struktúrája, amely a felület tényleges memóriabeli reprezentációja. Minden RenderObject ismeri a méreteit, a képernyőn elfoglalt helyzetét és képes megrajzolni önmagát. Ellentétben a widgetekkel, amelyek könnyűek és másodpercenként több százszor újjáteremthetők, a RenderObject — egy nehéz objektum közvetlen hozzáféréssel a grafikus csővezetékhez.
A widgettől a pixelig vezető út három szakaszon megy keresztül: A Widget Tree leírja, minek kell a képernyőn lennie; Az Element Tree kezeli az életciklust; RenderObject Tree végzi a tényleges munkát. Minden RenderObjectWidget (pl. Padding, Transform, CustomPaint) létrehoz egy RenderObject-ot, amely hozzáadódik a megjelenítési fához. RenderBox — a leggyakoribb RenderObject típus, a szabványos widgetek 99%-ánál használatos.
RenderObject — egy absztrakt osztály, amely meghatározza a layout és paint interfészét. Tartalmaz hivatkozásokat a szülő RenderObject-ra és a gyermek objektumokra, valamint absztrakt metódusokat: performLayout, paint és hitTest. Minden konkrét RenderObject ezeket a metódusokat a saját viselkedése szerint implementálja: a RenderFlex elosztja a teret a gyermek elemek között, a RenderImage megjeleníti a képet, a RenderParagraph megjeleníti a szöveget.
abstract class RenderObject {
RenderObject? parent;
Constraints constraints;
ParentData? parentData;
bool _needsLayout = true;
bool _needsPaint = true;
void performLayout();
void paint(PaintingContext context, Offset offset);
}
Ebben az egyszerűsített struktúrában a RenderObject _needsLayout és _needsPaint jelzőket tartalmaz, amelyek frissítésre szorulóként jelölik meg az objektumot. Amikor a Widget Tree megváltozik, az Element Tree a megfelelő RenderObject-okat „piszkosként„ jelöli meg layout vagy paint számára, és a következő képkockánál a Flutter csak a szükséges műveleteket hajtja végre.
Minden RenderObject két kulcsfontosságú fázist hajt végre: layout (méretek és pozíciók meghatározása) és paint (rajzolás). Ezek a fázisok szigorú sorrendben hajtódnak végre: először layout a teljes fára, majd paint. A paint fázis kihagyható, ha a layout nem változott, ami GPU-erőforrásokat takarít meg.
A layout során a Flutter korlátokat (constraints) továbbít felülről lefelé a RenderObject Tree-n keresztül. Minden szülő beállítja a gyermek objektum számára a minimális és maximális szélességet és magasságot. A gyermek objektum kiszámítja a méretét e korlátokon belül, és visszaadja a szülőnek. Ezt a folyamatot „downward pass„-nak (korlátok lefelé továbbítása) és „upward pass„-nak (méretek felfelé továbbítása) nevezik.
A layout befejezése után a Flutter végrehajtja a paint-et — az egyes RenderObject-ok rajzolását. A szülő létrehoz egy PaintingContext-et, átadja a gyermek objektumoknak és megadja az eltolást (offset). Minden RenderObject megrajzolja magát a Canvas-on grafikus primitívek segítségével: téglalapok, körök, szöveg, képek és transzformációk. A Canvas közvetlenül kommunikál a Skia-val vagy az Impellerrel.
A Flutter a „piszkos„ csomópontok mechanizmusát használja a munka minimalizálására. Ha csak egy RenderObject mérete változik, a Flutter nem számolja újra a teljes fa layout-ját — megjelöli a megváltozott csomópontot és annak lehetséges őseit a legközelebbi rögzített méretű RenderBox-ig. Hasonlóképpen, a megjelenés változásakor csak a paint kerül megjelölésre a layout megismétlése nélkül.
A Flutter kínál több RenderObject kategóriát különböző feladatokhoz: RenderBox a szabványos téglalap alakú elemekhez, RenderSliver a görgethető területekhez és egyéni RenderObject a nem szabványos megjelenítéshez CustomPainter segítségével. Minden típus a saját szerepére van optimalizálva a megjelenítési fában.
RenderBox — a téglalap alakú felületelemek alaposztálya. Altípusai: RenderPadding (távolságot ad hozzá), RenderTransform (transzformációkat alkalmaz), RenderFlex (megvalósítja a Row-t és Column-t), RenderStack (elemek egymásra helyezése), RenderImage (képek megjelenítése), RenderParagraph (szöveg megjelenítése). Minden altípus felülírja a performLayout-ot és paint-et a saját logikája szerint.
RenderSliver — egy RenderObject típus a görgethető területekkel való munkához. A RenderBox-tól eltérően a Sliver SliverConstraints-t használ, amely a látható területről (viewport) tartalmaz információkat. A RenderSliverList és RenderSliverGrid csak a látható területbe eső elemekhez hoz létre RenderObject-ot, lehetővé téve a millió rekordot tartalmazó listák feldolgozását.
Nem szabványos grafikához használja a CustomPaint-et és CustomPainter-t. A CustomPaint létrehoz egy RenderCustomPaint objektumot, amely meghívja a paint metódusokat a CustomPainter-ben. Ez lehetővé teszi tetszőleges alakzatok, grafikonok és animációk rajzolását teljes Canvas feletti kontrollal. A Flutter Team szerint a CustomPainter hatékonyabb, mint az egymásba ágyazott szabványos widgetek összetett vektorgrafikák esetén.
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;
}
Ebben a példában a CirclePainter egy kört rajzol a Canvas-ra. A shouldRepaint metódus csak színváltozáskor ad vissza true-t, megakadályozva a szükségtelen újrarajzolást. A CustomPainter-nek a lehető legkönnyebbnek kell lennie — minden nehéz számítást a paint metódusokon kívül végezzen.
RenderObject Tree az Element Tree-ből jön létre a RenderObjectWidget mechanizmusán keresztül. Minden RenderObjectWidget (Padding, Transform, CustomPaint) létrehoz egy RenderObjectElement-et, amely létrehozza és kezeli a megfelelő RenderObject-ot. Az elem közvetítőként működik: továbbítja a konfigurációt a widgettől a RenderObject-nak és értesíti a RenderObject-ot a változásokról.
Amikor a RenderObjectElement csatlakoztatásra kerül, meghívja a widget createRenderObject metódusát. A widget létrehoz egy RenderObject példányt és visszaadja az elemnek. Az elem beágyazza a RenderObject-ot a RenderObject Tree-be a szülő RenderObject insertChildLayout metódusának meghívásával. Ez a folyamat csak az első csatlakoztatáskor történik — a későbbi frissítéseknél az elem egyszerűen frissíti a meglévő RenderObject paramétereit.
Amikor a widget konfigurációja megváltozik (pl. a padding értéke változik), az elem meghívja az updateRenderObject metódust, amely továbbítja az új konfigurációt a meglévő RenderObject-nak. A RenderObject „piszkosként„ jelöli meg magát layout vagy paint számára, és a következő képkockánál a keretrendszer elvégzi a szükséges frissítéseket.
Az elem eltávolításakor az unmount metódus hívódik meg, amely eltávolítja a RenderObject-ot a RenderObject Tree-ből és felszabadítja az erőforrásokat. A RenderObject.remove meghívásra kerül a szülőtől való leválasztáshoz, ezután az objektumot a szemétgyűjtő begyűjtheti. A Flutter garantálja, hogy a RenderObject nem lóg a fában a megfelelő elem nélkül.
RepaintBoundary — egy widget, amely külön réteget hoz létre a tartalmának megjelenítéséhez. Amikor a RepaintBoundary tartalma megváltozik, csak ez a réteg rajzolódik újra, a képernyő többi része változatlan marad. A RepaintBoundary különösen hasznos animációkhoz, videólejátszókhoz, interaktív grafikonokhoz és más gyakran frissülő elemekhez.
A RepaintBoundary alapjául a RenderRepaintBoundary — egy speciális RenderObject szolgál, amely külön PictureLayer-t hoz létre. Az első megjelenítéskor a RenderRepaintBoundary grafikus parancsokat rögzít ebbe a rétegbe. A későbbi frissítéseknél, ha csak a RepaintBoundary-n belüli tartalom változott, a Flutter csak ezt a réteget rajzolja újra, nem a teljes képernyőt. A többi réteg változatlan marad és újra felhasználásra kerül.
Nem minden widgetnek van szüksége RepaintBoundary-ra. Használja, amikor a felület egy része magas frekvenciával (60 FPS és felette) frissül, míg a képernyő többi része statikus. Tipikus példák: animált töltésjelző, videólejátszó, játék Canvas, gyakran újrarajzoló CustomPainter. Statikus szöveghez vagy gombokhoz a RepaintBoundary felesleges és csak növeli a memóriafogyasztást.
| Forgatókönyv | Javaslat | Ok |
|---|---|---|
| Animáció | Csomagolja RepaintBoundary-ba | Elkülöníti a gyakran frissülő területet |
| Görgethető lista | Nem szükséges | A ListView automatikusan használ rétegeket |
| Statikus szöveg | Nem szükséges | Nincs gyakori újrarajzolás |
| CustomPainter | Ajánlott | A grafika gyakori újrarajzolása |
Teljesítmény a RenderObject Tree csomópontjainak számától, a fa mélységétől és az újrarajzolások gyakoriságától függ. A Flutter DevTools eszközei („Rendering„ fül) lehetővé teszik a RenderObject Tree valós idejű elemzését: repaint-ek száma, layout és paint ideje, rétegek száma és méreteik. A rendszeres elemzés segít a szűk keresztmetszetek azonosításában.
Overdraw — az a helyzet, amikor egy pixelt többször rajzolnak meg képkockánként. Például, amikor egy félig átlátszó widget egy másikra kerül, a GPU mindkét réteget megrajzolja. Használjon opaque jelzőket (Container color-rel decoration helyett) az átlátszatlan elemekhez, hogy a Flutter kihagyja a láthatatlan rétegeket. A Flutter Team szerint az overdraw csökkentése akár 30%-kal is rövidítheti a paint idejét.
Minden RepaintBoundary és néhány widget (Opacity, ClipRRect, Transform) külön réteget (PictureLayer) hoz létre. Túl sok réteg növeli a kompozíciós időt. Használjon csoportosítást: több különálló widgeten lévő Opacity helyett alkalmazzon egy Opacity-t egy konténeren. Ahelyett, hogy minden elemen ClipRRect-et használna, alkalmazzon ClipRRect-et egy közös konténeren.
Amikor egy widget const-ként van deklarálva, a Flutter tudja, hogy a konfigurációja nem változik, és újrafelhasználhatja a megfelelő RenderObject-ot újbóli létrehozás nélkül. A const-konstruktorok csökkentik a szemétgyűjtő terhelését és felgyorsítják az első képkockát. Használja a const-ot rögzített paraméterű widgetekhez: ikonok, címsorok, dekoratív elemek.
const Text("Cím", style: TextStyle(fontSize: 24));
const Icon(Icons.home, color: Colors.blue);
const SizedBox.shrink();
Gyakran ismételt kérdések
Widget Tree — a felület könnyű konfigurációja, amely minden rebuild-kor újjáépül. RenderObject Tree — a megjelenítési objektumok nehéz fája, amely megőrződik és csak a geometria vagy megjelenés változásakor frissül.
Használja a Flutter DevTools-ot — „Rendering„ fül. Látni fogja a megjelenítési rétegeket, a layout és paint idejét, valamint részletes információkat minden RenderObject-ról: méretek, constraints, frissítési szükséglet jelzők.
Dirty — egy RenderObject, amely frissítésre szorulóként van megjelölve. A _needsLayout vagy _needsPaint jelző a konfiguráció változásakor állítódik be, és a Flutter a következő képkockában layout-ot vagy paint-et hajt végre erre a csomópontra.
Igen, hozzon létre egy alosztályt a RenderBox-ból és írja felül a performLayout és paint metódusokat. Használja a RenderObjectWidget-et az egyéni RenderObject Widget Tree-be ágyazásához. Ez egy haladó technika nem szabványos megjelenítéshez.
RepaintBoundary létrehoz egy RenderRepaintBoundary-t, amely a RenderObject Tree egy részét egy külön rétegbe izolálja. A tartalom változásakor csak ez a réteg rajzolódik újra, a fa többi része változatlan marad.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is