Flutter state management megoldások

SetState, Provider, BLoC: ismerd meg, mikor melyiket érdemes használni a három népszerű módszer közül
LogiNet Mobile Dev Team

LogiNet Mobile Dev Team

Natív iOS, Android és cross-platform mobil szakértők

A state management a Flutter fejlesztés egyik kulcsfontosságú területe, mivel meghatározza, hogyan kezeljük a felhasználói interakciók és a megjelenítendő adatok változásait. Három népszerű módszert mutatunk be részletesen a state kezelésére, a setState, a Provider, és a BLoC (Business Logic Component) megoldásokat. Az aktuális probléma, az alkalmazás komplexitása és a karbantartási igény dönti el, hogy egy alkalmazás esetén melyiket célszerű használni.


Mi az a state management?

A state (állapot) egy olyan adat, amely befolyásolja, hogy egy adott widget hogyan néz ki, vagy hogyan viselkedik. Például egy gomb színe, egy számláló értéke, vagy egy bevásárlólista tartalma mind state-hez köthető. A state management pedig az az eszköz, amely segít abban, hogy az alkalmazás folyamatosan nyomon kövesse és kezelje ezeket a változásokat.

A state management kétféle állapota

Ideiglenes vagy helyi állapot

Az ideiglenes vagy helyi állapot olyan adat, amelyet egyetlen widgeten belül tárolhatunk és kezelhetünk. Ilyen lehet például:

  • az aktuális tab egy NavigationBar-on
  • egy UI animáció állapota
  • a kiválasztott érték egy listában

Alkalmazás állapot

Az alkalmazás állapot egy tartós adat, amelyet az alkalmazás több részében is felhasználunk, változására reagálunk, megmarad a felhasználói munkamenetek között. Ilyen például:

  • a bejelentkezett felhasználó adatai
  • a kosár tartalma egy kereskedelmi alkalmazásban
  • kedvencek listája

Flutter state management - alkalmazás állapot

Állapotkezelés szempontjából fontos, hogy a Flutterben kétféle widgetet különböztetünk meg.

Stateless Widget

Olyan widget, amelynek nincs állapota. Ez azt jelenti, hogy a widget nem változik az alkalmazás futása során, és nem kell újrarajzolni azt semmilyen állapotváltozás miatt. Abban az esetekben alkalmazzuk, amikor az adott UI elem statikus, és nem változik, mint például egy statikus szöveg vagy ikon. Ha állapotkezelést használunk egy Stateless widgetben, azt az állapotot valahol máshol kell kezelnünk, például egy szülő Stateful widgetben, vagy egy globális state management eszközzel (pl. Provider vagy BLoC).

Stateful Widget

Saját állapottal rendelkező widget, ami ha változik, a UI-nak reagálnia kell ezekre a módosulásokra. Ilyenkor a rendszer újrarajzolja a widgetet az új állapot alapján. Lokális állapotkezelés esetén ezt a widgetet használjuk.


State management megoldások

1. setState

A setState a legkönnyebben használható megoldás az állapotkezelésre, és akkor érdemes alkalmazni, amikor az állapotváltozások egyszerűek és lokálisak. Ilyen esetekben csak az adott widgetet érinti a változás, és az állapot frissítésekor a widget újrarajzolódik. Ehhez a StatefulWidgetet kell használnunk, aminek a beépített metódusa a setState.

Példa:

class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('$_counter'),
        ElevatedButton(
          onPressed: _incrementCounter,
          child: const Icon(Icons.add),
        ),
      ],
    );
  }
}

 

Ebben a példában a számláló értéke a Hozzáadás gomb megnyomásának hatására a setState függvény segítségével frissül, ami a widget újrarajzolásához vezet.

2. Provider

A Provider egy népszerű megoldás a közepes bonyolultságú alkalmazásokhoz, amelyeknél az állapotnak több widgeten keresztül kell elérhetőnek lennie. A Provider-t akkor használjuk, ha az alkalmazásnak egy nagyobb struktúrája van, és az állapotot több különböző widgetnek is figyelnie kell. Támogatja az állapot könnyű átláthatóságát és megosztását.

Flutter state management - Provider

Példa:

class Counter extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

class CounterProviderWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => Counter(),
      child: Column(
        children: [
          Expanded(child: CounterWidgetA()),
          Expanded(child: CounterWidgetB()),
        ],
      ),
    );
  }
}

class CounterWidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<Counter>(
      builder: (context, counter, child) {
        return Scaffold(
          body: Center(child: Text('${counter.count}')),
          floatingActionButton: FloatingActionButton(
            onPressed: counter.increment,
            child: const Icon(Icons.add),
          ),
        );
      },
    );
  }
}

class CounterWidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<Counter>(
      builder: (context, counter, child) {
        return Center(child: Text('${counter.count}'));
      },
    );
  }
}

 

A példában szereplő Counter osztály a ChangeNotifier-ből származik, ami lehetővé teszi az osztály változásaira való feliratkozást. A ChangeNotifierProvider segítségével biztosítjuk a Counter egy példányának az elérhetőségét, így azon widgetek felé kell helyezni, amelyek használják. Jelen esetben a CounterWidgetA és CounterWidgetB is eléri a Provider.of<Counter>(context) segítségével, így ez a fajta állapotmegosztás widgeteken is átível.

3. BLoC

A Flutter egyik leggyakrabban használt state management megoldása a BLoC (Business Logic Component). A BLoC segít elkülöníteni az alkalmazás logikáját és az állapotkezelést a felhasználói felülettől, így a kód könnyebben karbantartható, tesztelhető és skálázható. Ezt úgy valósítja meg, hogy események (events) formájában fogadja a felhasználói interakciókat vagy más külső adatokat, és állapotokat (states) ad vissza a widgetek számára. Így a UI csak az aktuális állapot megjelenítésére koncentrál, míg a BLoC kezeli a változásokat.

Flutter state management - BLoC

Példa:

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterEvent>((event, emit) {
      if (event is Increment) {
        emit(state + 1);
      } else if (event is Decrement) {
        emit(state - 1);
      }
    });
  }
}

abstract class CounterEvent {}

class Increment extends CounterEvent {}

class Decrement extends CounterEvent {}

class CounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => CounterBloc(),
      child: Column(
        children: [
          Expanded(child: CounterWidgetA()),
          Expanded(child: CounterWidgetB()),
        ],
      ),
    );
  }
}

class CounterWidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterBloc = BlocProvider.of<CounterBloc>(context);
    return Scaffold(
      body: Center(
        child: BlocBuilder<CounterBloc, int>(
          builder: (context, count) {
            return Text('$count');
          },
        ),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => counterBloc.add(Increment()),
            child: Icon(Icons.add),
          ),
          FloatingActionButton(
            onPressed: () => counterBloc.add(Decrement()),
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

class CounterWidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: BlocBuilder<CounterBloc, int>(
          builder: (context, count) {
            return Text('$count');
          },
        ),
      ),
    );
  }
}

 

Ebben a megoldásban az Increment és Decrement események vezérlik a CounterBloc-ot, ezekre reagálva frissül a számláló értéke amit megjelenít két különálló widget is. Az állapot biztosításához itt BlocProvider-t használunk, valamint a BlocBuilder-nek köszönhetően csak az alatta lévő widget fog újrarajzolódni a változás hatására.

Előnyök:

  • Kód újrafelhasználhatóság: a logika és az állapotkezelés elkülönítése megkönnyíti a kód karbantartását és újrafelhasználását.
  • Tesztelhetőség: mivel a BLoC elkülöníti az üzleti logikát a UI-tól, az egyes részek könnyebben tesztelhetők.
  • Következetesség: a stream-alapú adatáramlás biztosítja, hogy az állapot mindig naprakész legyen, és minden widget reagáljon a változásokra.

Hátrányok:

  • Bonyolultság: a BLoC minta tanulási görbéje meredekebb lehet, különösen kisebb projektek esetében túl bonyolultnak tűnhet.
  • Boilerplate kód: a BLoC implementáció sok boilerplate kódot igényel, különösen kisebb állapotkezelési feladatokhoz.

Mikor melyik state management megoldást érdemes választani?

Érdemes ötvözni a megoldásokat egy alkalmazás esetében. 

A lokális állapotkezelést kifejezetten azoknál az eseteknél használjuk, amikor az állapot kizárólag egy widgetre vonatkozik, ilyenkor nincs hatással más komponensre a változása. 

Az alkalmazás állapot típusú megoldásokat akkor célszerű használni, amikor több widget, vagy akár az egész alkalmazás számára fontos adatot szeretnénk tárolni. Ezek közül a Provider közepes komplexitású alkalmazásoknál ajánlott, ahol egyszerűbb adatmodelleket kell implementálni az állapotok reprezentálására. 

A Bloc nagyobb projekteknél ideális, ahol fontos az átláthatóság, a könnyű karbantarthatóság és a skálázhatóság, mivel kellően elszeparálja az üzleti logikát a felhasználói felülettől. Akkor a leghatékonyabb, ha az alkalmazás összetett logikát és állapotkezelést igényel.

A LogiNetnél natív és flutter mobil applikációk, egyedi app fejlesztésében is a segítségedre vagyunk. Vedd fel kollégáinkkal a kapcsolatot, beszéljünk róla mi lehet számodra a legideálisabb megoldás!
LogiNet Mobile Dev Team

LogiNet Mobile Dev Team LogiNet Mobile Dev Team LinkedIn profilja

Natív iOS, Android és cross-platform mobil szakértők
Mobil app fejlesztői csapatunk tagjai komoly tapasztalattal rendelkeznek új, egyedi mobil applikáció készítésében, a meglévő mobil app továbbfejlesztésében, refaktorálásban. Naprakészek a legújabb technológiák terén. Céljuk, hogy a legmodernebb megoldásokat az elsők között építsék be a fejlesztett alkalmazásokba.