Skip to content
Kezdőlap » Fa iskola a Flutterben

Fa iskola a Flutterben

Fa iskola a Flutterben? „Kertész leszek, fát nevelek, kelő nappal én is kelek.” – mondta ezt anno József Attila, akinek természetesen köze nem volt a Flutterhez.

Jogos a kérdés, hogy akkor meg mi ez a hülyeség???? 🙂 🙂 🙂

A jó hír, hogy nem ment el az eszem (még). És igen, a Flutterben léteznek fák. Még hozzá három is:

  • Widget Tree (Widget fa)
  • Element Tree (elem fa)
  • Render Tree (renderelési fa)

A legjobb lesz, ha elmagyarázom ezeket és nem húzom tovább az időt.

Azzal kezdem, hogy az Element és a Render Tree olyan fák, amikkel fejlesztőként nem sokat fogunk találkozni. Ezek kvázi láthatatlanok.

Widget Tree

A Widget Tree az nem más mint widgetek hierarchiája, kombinációja a programban.

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Fejléc címe');
        ), // AppBar  
        body: GradientContainer.purple(),
      ), // Scaffold
    ), // MaterialApp
  );
}

Látható, hogy van itt néhány widget, amik egymás belsejében vannak (widget a widgeten belül), tehát egy fát alkotnak.

Element Tree

A Flutter a memóriában is elkészíti ezt a fát a Widget Tree alapján. Mindezt azért teszi, hogy neki sokkal könnyebb legyen hatékonyan frissíteni a felhasználó felületet. Szóval ez a Flutter biznisze a színfalak mögött.

Tehát minden egyes Widgetnek, amit a kódban használtunk, írtunk, van megfeleltethető párja a memóriában.

Egy widget build metódusa elég gyakran lefut. Például azért, mert meghívjuk a setState() metódust, aminek a változásokat vissza kell vezetnie a képernyőre. Valahányszor egy widgetnek lefut a build metódusa, az ezen a widgeten belüli gyerek widgetek build metódusai is lefutnak. Mivel a Flutternek ellenőriznie kell, hogy a teljes widget fa és az abban történt változások megjelenjenek a képernyőn.

De a Flutternek nem szükségszerű az összes objektumot újra létrehoznia. Főleg azokat nem, amik már egyébként is léteznek. Ehelyett a teljes fának csak azon részeit frissíti, ahová új widgetek kerülnek be, vagy meglévő widgetek törlődnek.

A legfontosabb tehát, hogy a fában az elemek újrahasznosításra kerülnek, és nincsenek újra létrehozva minden egyes esetben, amikor frissül a felhasználói felület. Akkor kerülnek csak létrehozásra, amikor a widget legelőször a fába kerül. És ez nagyon hatékony működést tesz lehetővé!

Render Tree

A Flutter az Element Tree-t használja arra, hogy a képernyő „rajzolásakor” meg tudja határozni, hogy mely felhasználói felület elemeket kell neki meg- vagy újra rajzolnia (renderelnie).

A Render Tree a látható felhasználói elemeknek a fája, kombinációja. Tehát, amit a felhasználó lát a mobil képernyőjén.

A hatékony működés érdekében a Flutter tehát a képernyő azaz Render Tree nem változó elemeit békén hagyja. Amikor tehát valami megváltozik, akkor a Flutter megnézi az Element fát, hogy mi változott, és csak ezeket a változásokat vezeti át a Render fára, vagyis a képernyőre.

Felhasználói felület frissítésének folyamata

A lényeg tehát, hogy amikor a build() metódus hívásra kerül, a Flutter csekkolja az Element fát, szükség szerint újrahasználja a fában már létező elemeket. Aztán összehasonlítja az új Element fát a régivel, és, ha bármi eltérést talál, akkor ezeket a változásokat szépen átvezeti a Render fába és a képernyőn megjelennek a változások.

Nem rendereli tehát újra az egész képernyőt, hanem csak a változásokat.

És mindez az egész azért jó, mert nekünk fejlesztőknek a kisujjunkat sem kell mindezért mozdítani. A Flutter megcsinál mindent.

A mi reszortunk csak a Widget fa. Mi csak a widgeteket írogatjuk és hívogatjuk, építjük a widget fát, a többit a Flutter intézi az Element- és a Render fával. Fejlesztőként azonban nem árt tisztában lenni azzal, hogy mi zajlik a háttérben.

A felesleges widget buildek elkerülése

Az már világos, hogy a Flutter csak azokat a widgeteket fogja kirenderelni a képernyőre, amik változtak.

Amikor van egy StatefulWidget-ünk és valami kiváltja a setState()-et, akkor ez minden alkalommal futtatja a widgetnek a build metódusát. Ez egyúttal azt is jelenti, hogy azoknak a Widgeteknek a build metódusai is lefutnak, amik a Stateful widgeten belül vannak. Ezt viszont nem minden esetben akarjuk.

Itt van ez a példa kód:

class _UIUpdatesDemo extends State<UIUpdatesDemo> {
  var _isUnderstood = false;

  @override
  Widget build(BuildContext context) {
    print('UIUpdatesDemo BUILD called');
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const Text(
              'Every Flutter developer should have a basic understanding of Flutter\'s internals!',
              textAlign: TextAlign.center,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),
            const Text(
              'Do you understand how Flutter updates UIs?',
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 24),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                TextButton(
                  onPressed: () {
                    setState(() {
                      _isUnderstood = false;
                    });
                  },
                  child: const Text('No'),
                ),
                TextButton(
                  onPressed: () {
                    setState(() {
                      _isUnderstood = true;
                    });
                  },
                  child: const Text('Yes'),
                ),
              ],
            ),
            if (_isUnderstood) const Text('Awesome!'),
          ],
        ),
      ),
    );
  }
}

Amikor lefut a setState és ezáltal meghívódik a build metód, akkor a Flutter az összes gyerek widgetnek is futtatja a build metódusát. Ha változik valami a képernyőn, ha nem, akkor is.

Ha pontosan tudjuk, hogy mely widget fog változni a képernyőn, azaz melyiket kell újra renderelni, akkor jó lenne csak ennek az egynek a build metódusát lefuttatni nem? És a többit meg békén hagyni. Ez elég hasznos lehet egy bazi nagy app esetében ahol kismillió widget van egymásba ágyazva, magyarul a widget tree egy kész Mammutfenyő.

A fenti példában mindössze egyetlenegy widgetnek kell a képernyőn frissülnie, az pedig ez a feltételtől függően megjelenő / eltűnő Text widget:

if (_isUnderstood) const Text('Awesome!'),

Szóval mi a bánatnak kellene akkor az összes többinek is feleslegesen hívogatni a build metódusát?

A megoldás tehát, hogy csak azok a widgetek maradjanak a StatefulWidget-ben, amik valóban változnak. Ami úgymond statikus, az kerüljön át inkább egy StatelessWidget-be.

Illetve még egy jó tanács, hogy egy StatefulWidge legyen olyan kicsi, amilyen kicsi csak lehet, hogy ezzel is növeljük a performanciáját / teljesítményét, gyorsaságát az alkalmazásunknak.

Vélemény, hozzászólás?

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük