Flutter'da state management tartismasi yillarca surdu ve tek bir doğru cevap olmadigi netlesti. Asil soru hangi kutuphanenin en populer olduğu degil; uygulamanizin karmasikligi, takım deneyimi, test ihtiyaci ve state'in yaşam dongusune gore hangi modelin sürdürülebilir oldugudur. Bu yazida setState'ten Riverpod'a kadar geniş bir yelpazeyi kriter bazli inceliyoruz.
State turleri ve kapsam
Once state'i siniflandirmak gerekir. Ephemeral (local) state tek widget veya ekranla sinirlidir: checkbox durumu, animasyon controller, form focus. App state oturum boyunca birden fazla ekranda paylasilir: sepet, tema, kullanıcı profili. Server state ise API'den gelen, cache ve invalidation kurallari olan veridir. Yanlış katman seçimi, gereksiz bağımlılık ve test zorluguna yol acar.
Yerel state icin setState yeterli mi?
Küçük ve izole ekranlarda setState hala en basit ve performansli cozumdur. Form wizard'inin tek adiminda veya basit sayac orneginde ek kutuphane getirmek over-engineering olabilir. Sorun, setState'in ekran buyudukce ve paylaşım arttikca olceklendirilememesidir. Callback cehennemine dusmemek icin en gec orta olcekte merkezi bir model dusunulmelidir.
InheritedWidget ve Provider
Provider, InheritedWidget uzerine kurulu hafif bir DI ve state tasiyicisidir. ChangeNotifierProvider ile imperatif notifyListeners modeli, Provider.value ile sabit bağımlılıklar sağlanır. Avantaji öğrenme egrisi düşük olmasi ve resmi dokumantasyonda sik referans edilmesidir. Dezavantaji ise büyük uygulamalarda provider agacinin yönetimi ve granular rebuild icin ekstra Selector kullanımı gerektirmesidir.
class CartNotifier extends ChangeNotifier {
final List<LineItem> _items = [];
List<LineItem> get items => List.unmodifiable(_items);
void add(LineItem item) {
_items.add(item);
notifyListeners();
}
}
Riverpod ve compile-time güvenlik
Riverpod, Provider'in yazarinin gelistirdigi nesil cozumdur. ProviderScope ile kok seviyede baslatilir; ref.watch, ref.read, ref.listen ayrimi yan etkileri azaltir. Code generation (riverpod_generator) ile tip güvenliği artar. Özellikle async provider'lar, aile (family) ve otomatik dispose mekanizmasi server state senaryolarinda gucludur.
StateProvider: basit primitive state.NotifierProvider: senkron domain mantigi.AsyncNotifierProvider: API yükleme/hata/refresh akislari.StreamProvider: WebSocket veya Firebase akislari.
BLoC / Cubit ve test edilebilirlik
BLoC pattern, event-driven akisi zorunlu kilar; Cubit ise daha hafif ve dogrudan metod cagrisiyla state yazar. Her iki modelde de is mantigi UI'dan ayrilir; bloc_test paketi ile birim test yazmak kolaydir. Finans, sağlık gibi denetim gerektiren projelerde event log'u ve açık state gecmisi tercih sebebidir. Maliyeti boilerplate ve basit ekranlarda fazla ceremonidir.
buildWhen ve performans
BlocBuilder icinde buildWhen: (prev, next) => prev.status != next.status gibi filtreler gereksiz rebuild'i azaltir. Cubit tarafinda immutable state (Freezed/Equatable) kullanmak bu karsilastirmayi güvenilir kilar.
GetX, MobX ve alternatifler
GetX hızlı prototipleme icin caziptir; routing, DI ve reactive state tek pakette sunulur. Büyük ekiplerde implicit bağımlılık ve test izolasyonu zorlasabilir. MobX, reaktif otomatik izleme ile az kodla cok güncelleme sağlar; fakat codegen ve gizli bağımlılık grafigi debug'u zorlastirabilir. Redux ve flutter_redux hala kullanılır; tek yonlu veri akisi ve time-travel debug avantaj sağlar, fakat Dart/Flutter ekosisteminde artik nis kalmistir.
Server state: Riverpod vs BLoC vs ayri katman
REST ve GraphQL verisi icin dio + repository + AsyncNotifier veya BLoC kombinasyonu yaygindir. Alternatif olarak flutter_hooks + fquery veya cached_query gibi React Query benzeri kutuphaneler cache invalidation'i otomatiklestirir. Önemli olan HTTP client'i state kutuphanesinden ayirmaktir; boylece kutuphane degistiginde ag katmanı etkilenmez.
Seçim matrisi
- Tek geliştirici, küçük MVP: setState veya hafif Provider.
- Orta ölçek, cok ekran: Riverpod veya Cubit.
- Karmaşık is kurallari, denetim: BLoC + Freezed.
- Hızlı POC: GetX, sonra refactor planla.
- Agir server cache: repository + AsyncNotifier veya dedicated cache paketi.
Takım ve mimari uyum
Doğru arac seçimi yalnizca teknik degil organizasyonel bir karardir. Takimin Dart deneyimi, mevcut örnek projeler ve test kulturu belirleyicidir. Birden fazla state cozumunu ayni uygulamada karistirmak genellikle kotu fikirdir; istisna, yerel setState + global Riverpod gibi net sinirli hibritlerdir.
State yönetimi kararini ADR (Architecture Decision Record) olarak yazin: neden Riverpod, hangi ekranlar local state, test stratejisi ne. Bu belge yeni gelen gelistiricilerin ayni hatayi tekrarlamasini onler.
Test ve gozlemlenebilirlik
Secilen çözümün WidgetTester ile nasil test edilecegini onceden dogrulayin. ProviderScope, ProviderScope overrides veya BlocProvider.value ile fake repository enjekte edilebilir. State gecislerini loglamak icin BlocObserver veya Riverpod observer kullanmak üretim oncesi hatalari yakalar.
Ozetle Flutter'da state management dini bir savas degil, muhendislik trade-off'udur. Yerel state'i yerinde birakın, paylasilan state'i açık modellerle yonetin, server state'i repository ve cache kurallariyla ayirin. Bu uc katman netlestiginde hangi kutuphaneyi secerseniz secin mimariniz okunabilir kalir.
Equatable ve Freezed gibi paketler state karsilastirmalarini güvenilir kilar; rebuild filtreleri ve diff tabanli UI guncellemeleri icin vazgecilmezdir. Immutable state, yanlislikla mutasyon kaynakli bug'lari da azaltir.
HookWidget ve flutter_hooks, StatefulWidget boilerplate'ini azaltir; fakat takım hooks semantigine hakim degilse okunabilirlik duser. Hooks + Riverpod kombinasyonu modern projelerde yayginlasiyor.
Form state icin ayri bir strateji dusunun: flutter_form_builder veya reactive_forms gibi paketler validation ve focus yönetimini merkezilestirir; genel app state cozumunden bagimsiz calisabilir.
Performans acisindan state değişim sikligi önemlidir. Saniyede onlarca güncelleme ureten sensor verisi UI'a dogrudan yazilmamali; throttle, debounce veya isolate ile filtrelenmelidir.
Migration yolu planlayin: Provider'dan Riverpod'a geciste once repository katmanini ayirin, ardindan ekran ekran provider'lari notifier'a tasiyin. Big bang refactor yerine modül modül geçiş riski dusurur.
Clean Architecture ile birlestirildiginde state çözümü use case katmanina baglanmamalidir; domain saf kalmali, presentation yalnizca sonuç state'ini tuketmelidir. Bu sınır, kutuphane degisiminde is kurallarini korur.
Oturum state'i (auth token, kullanıcı kimligi) ile UI state'i (sekme indeksi, sheet açık mi) karistirilmamali. Auth genellikle ayri AuthRepository + secure storage ile yönetilir; global navigator key ile yonlendirme auth stream'ine baglanir.
Code review'da kontrol edilecek maddeler: notifyListeners gereksiz cagriliyor mu, Bloc'ta event spam var mi, Riverpod'da ref.watch build dışında kullanilmis mi, setState icinde agir is var mi. Bu basit sorular çoğu performans ve bug riskini erken yakalar.
State persistence (hydrated_bloc, riverpod keepAlive + disk) uygulama yeniden acildiginda UX iyilestirir; fakat hassas veriyi disk snapshot'a yazmayin. Persist edilecek alanlar icin ayri policy dokumani olusturun.
Çoklu dil ve tema gibi cross-cutting state ThemeMode ve Locale icin Flutter'in yerleşik mekanizmalarini tercih edin; gereksiz global singleton'dan kacinilir. ThemeExtension ile tasarım token'lari state kutuphanesinden bagimsiz taşınabilir.
Son olarak state yönetimi secimini dondurulmus bir karar olarak degil, ölçülebilir varsayimlarla alin: ekip hizi, test kapsamasi, performans profili ve bakım maliyeti. Uc ay sonra retrospektif yapip gereksiz karmasikligi sadelestirmek, baslangicta doğru araci secmek kadar önemlidir.
Yeni baslayanlar icin resmi Flutter dokumantasyonundaki state management rehberi ve örnek repo'lar baslangic noktasi olmalidir; ardindan projenizin gerçek ihtiyacina gore daraltma yapin.
Karmaşık kutuphane seçimi yerine okunabilir kod ve net katman sınırları uzun vadede daha az teknik borc üretir.