Ders İçeriği
Flutter uygulamaları büyüdükçe ve karmaşıklaştıkça, basit setState veya orta seviye çözümler (Provider, BLoC/Cubit, GetX) yetersiz kalabilir veya daha spesifik ihtiyaçlar doğurabilir. Bu bölümde, daha büyük ölçekli uygulamalar için tasarlanmış veya belirli senaryolarda öne çıkan ileri seviye durum yönetimi çözümlerini inceleyeceğiz.
1.1. Riverpod
Riverpod, provider paketinin güvenli, test edilebilir ve daha esnek bir yeniden yazımıdır. Özellikle bağımlılık enjeksiyonu ve durum yönetimi konusunda provider'ın bazı sınırlamalarını aşmak için tasarlanmıştır. Riverpod, derleme zamanı güvenliği, otomatik hata yakalama ve daha kolay test edilebilirlik gibi avantajlar sunar.
Neden Riverpod?
•Derleme Zamanı Güvenliği: Provider'da context bağımlılığı nedeniyle ortaya çıkabilecek bazı çalışma zamanı hatalarını (örneğin, ProviderNotFoundException) derleme zamanında yakalar.
•Esneklik: Bir sağlayıcının (provider) birden fazla yerde kullanılmasına veya farklı sağlayıcıların birbirine bağımlı olmasına olanak tanır.
•Test Edilebilirlik: Sağlayıcıları ve durumları test etmek çok daha kolaydır, çünkü context bağımlılığı yoktur.
•Otomatik İptal: Kullanılmayan sağlayıcıları otomatik olarak iptal ederek bellek sızıntılarını önler.
Temel Kavramlar:
•Provider: Herhangi bir değeri (örneğin, bir nesne, bir değer) sağlayan en temel sağlayıcıdır.
•StateProvider: Basit bir durumu (örneğin, bir sayaç) yönetmek için kullanılır. Durumu değiştirmek için state özelliğini kullanırsınız.
•ChangeNotifierProvider: ChangeNotifier sınıfını kullanan durumları sağlamak için kullanılır. provider paketindeki karşılığına benzer.
•FutureProvider: Asenkron bir işlemden (örneğin, API çağrısı) gelen veriyi yönetmek için kullanılır. Yükleme, hata ve veri durumlarını otomatik olarak yönetir.
•StreamProvider: Bir akıştan (stream) gelen veriyi yönetmek için kullanılır.
•ConsumerWidget / ConsumerStatefulWidget: Sağlayıcıları dinleyen widget'lardır. ref nesnesi aracılığıyla sağlayıcılara erişirler.
•ref.watch(): Bir sağlayıcıdaki değişiklikleri dinler ve sağlayıcı değiştiğinde widget'ı yeniden oluşturur.
•ref.read(): Bir sağlayıcının mevcut değerini okur, ancak değişiklikleri dinlemez. Genellikle bir olay tetiklendiğinde (örneğin, bir düğmeye basıldığında) kullanılır.
Örnek: Basit Bir Sayaç Uygulaması (Riverpod ile)
Öncelikle flutter_riverpod paketini pubspec.yaml dosyanıza eklemeniz gerekir:dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^2.4.9 # En güncel sürümü kullanın
Sonra flutter pub get komutunu çalıştırın.import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// 1. Bir StateProvider tanımlayın
final counterProvider = StateProvider<int>((ref) => 0);
void main() {
runApp(
// 2. Uygulamanızı ProviderScope ile sarmalayın
const ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Riverpod Sayaç Örneği',
theme: ThemeData.light(),
home: const CounterScreen(),
);
}
}
// 3. ConsumerWidget kullanarak sağlayıcıyı dinleyin
class CounterScreen extends ConsumerWidget {
const CounterScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch() ile counterProvider'daki değişiklikleri dinle
final int counter = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(title: const Text('Riverpod Sayaç')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Sayaç Değeri:',
style: TextStyle(fontSize: 20),
),
Text(
'$counter',
style: Theme.of(context).textTheme.headlineLarge,
),
const SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
// ref.read() ile sağlayıcının state'ine eriş ve güncelle
ref.read(counterProvider.notifier).state--;
},
child: const Icon(Icons.remove),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).state++;
},
child: const Icon(Icons.add),
),
],
),
],
),
),
);
}
}
Riverpod Kullanımının Avantajları:
•Güvenlik: Derleme zamanı hatalarını azaltır.
•Esneklik: Karmaşık bağımlılık grafikleriyle kolayca başa çıkar.
•Test Edilebilirlik: Bağımlılıkların kolayca mock'lanabilmesi sayesinde test yazmayı basitleştirir.
•Performans: Sadece ilgili widget'ları yeniden oluşturarak performansı optimize eder.
1.2. GetX (Derinlemesine)
Orta seviyede GetX'e genel bir bakış atmıştık. Şimdi GetX'in ileri seviye özelliklerine ve daha karmaşık senaryolarda nasıl kullanılabileceğine daha derinlemesine bakalım.
GetX, sadece durum yönetimi değil, aynı zamanda rota yönetimi, bağımlılık yönetimi, uluslararasılaştırma, tema yönetimi ve daha fazlasını tek bir pakette sunan kapsamlı bir mikro çerçevedir. Bu entegre yaklaşım, özellikle hızlı prototipleme ve geliştirme süreçlerinde büyük avantaj sağlar.
İleri Seviye GetX Özellikleri:
•Reaktif Programlama (.obs, Obx, GetX):
GetX'in reaktif programlama yetenekleri, durum değişikliklerini otomatik olarak dinleyerek UI'ı güncellemenizi sağlar. .obs ile herhangi bir değişkeni gözlemlenebilir hale getirebilirsiniz.
•Bağımlılık Yönetimi (Get.put, Get.find, Get.lazyPut):
GetX, bağımlılık enjeksiyonunu son derece basit hale getirir. Bir Controller'ı veya herhangi bir sınıfı Get.put() ile kaydedebilir ve Get.find() ile herhangi bir yerden erişebilirsiniz.
•Rota Yönetimi (Get.to, Get.off, Get.offAll, Get.toNamed):
GetX, navigasyonu basitleştiren ve context'e ihtiyaç duymayan bir rota yönetimi sunar. Named routes veya dinamik rotalarla kolayca çalışabilirsiniz.
•Uluslararasılaştırma (i18n):
GetX, uygulamanızda çoklu dil desteği (localization) eklemeyi kolaylaştırır. Çevirileri bir Map içinde tanımlayabilir ve Get.updateLocale() ile dili değiştirebilirsiniz.
•Tema Yönetimi:
GetX ile tema değiştirmek de oldukça basittir.
GetX Kullanım Senaryoları:
•Hızlı Prototipleme: Az kodla hızlıca uygulama geliştirmek isteyenler için idealdir.
•Küçük ve Orta Ölçekli Uygulamalar: Kapsamlı özellik seti sayesinde birçok ihtiyacı tek başına karşılayabilir.
•Performans Odaklı Uygulamalar: Reaktif yapısı sayesinde gereksiz yeniden çizimleri minimize eder.
•Tek Kişilik Geliştiriciler veya Küçük Ekipler: Öğrenme eğrisi nispeten düşüktür ve hızlıca sonuç alınabilir.
GetX, Flutter geliştirme deneyimini basitleştiren ve hızlandıran güçlü bir araçtır. Ancak, sunduğu tüm özellikleri tek bir pakette topladığı için, bazı geliştiriciler tarafından
god object olarak eleştirilebilir. Projenizin büyüklüğüne ve ekibinizin tercihlerine göre bu aracı kullanıp kullanmayacağınıza karar vermelisiniz.
1.3. Diğer Durum Yönetimi Çözümleri (MobX, Redux)
Flutter ekosisteminde Riverpod, Provider, BLoC/Cubit ve GetX dışında da popüler durum yönetimi çözümleri bulunmaktadır. Her birinin kendine özgü felsefesi, avantajları ve dezavantajları vardır. İşte bunlardan bazıları:
•MobX: Reaktif programlama prensiplerine dayanan bir durum yönetimi kütüphanesidir. Gözlemlenebilir (observable) veriler, eylemler (actions) ve reaksiyonlar (reactions) kavramları üzerine kuruludur. MobX, durum değişikliklerini otomatik olarak algılar ve ilgili UI bileşenlerini günceller. Daha az boilerplate kodu ile hızlı geliştirme imkanı sunar.
•@observable: Değişkenleri gözlemlenebilir hale getirir. Bu değişkenler değiştiğinde, onları dinleyen her şey otomatik olarak güncellenir.
•@action: Durumu değiştiren fonksiyonları işaretler. Bu, durum değişikliklerinin tek bir yerden yapılmasını sağlar.
•@computed: Gözlemlenebilir verilerden türetilen ve sadece bağımlılıkları değiştiğinde yeniden hesaplanan değerleri tanımlar.
•Observer: Gözlemlenebilir verileri dinleyen ve değiştiğinde kendini yeniden oluşturan bir widget.
•Otomatik reaktivite sayesinde az boilerplate kodu.
•Kolay öğrenilebilir ve sezgisel API.
•Güçlü hata ayıklama araçları.
•Büyük ve karmaşık durumlarda yönetimi zorlaşabilir.
•Bazı geliştiriciler için sihirli (magic) gelebilir, bu da kodun anlaşılmasını zorlaştırabilir.
•Redux: JavaScript dünyasından Flutter'a adapte edilmiş, tahmin edilebilir bir durum yönetimi kütüphanesidir. Tek bir merkezi durum deposu (store), durum değişikliklerini tetikleyen eylemler (actions) ve bu eylemlere göre durumu güncelleyen reducer'lar prensibine dayanır. Redux, özellikle büyük ve karmaşık uygulamalarda durumun tutarlılığını ve izlenebilirliğini sağlamak için tercih edilir.
•Store: Uygulamanın tüm durumunu tek bir yerde tutan nesne.
•Action: Uygulamada meydana gelen olayları tanımlayan düz JavaScript nesneleri. Durumu doğrudan değiştirmezler, sadece ne olduğunu belirtirler.
•Reducer: Mevcut durumu ve bir eylemi alarak yeni bir durum döndüren saf fonksiyonlar. Durum değişikliklerinin tek ve tahmin edilebilir bir şekilde yapılmasını sağlarlar.
•Middleware: Eylemler reducer'a ulaşmadan önce veya sonra ek işlemler yapmak için kullanılır (örneğin, asenkron işlemler, loglama).
•Durum değişikliklerinin izlenebilirliği ve tahmin edilebilirliği.
•Zaman yolculuğu hata ayıklama (time-travel debugging) gibi güçlü hata ayıklama araçları.
•Büyük ve karmaşık uygulamalar için ölçeklenebilirlik.
•Çok fazla boilerplate kodu gerektirebilir.
•Öğrenme eğrisi diğer çözümlere göre daha dik olabilir.
•Küçük uygulamalar için gereksiz karmaşık olabilir.
Durum yönetimi çözümü seçimi, projenizin büyüklüğüne, ekibinizin deneyimine ve kişisel tercihlerinize bağlıdır. Her bir çözümün kendine özgü güçlü ve zayıf yönleri vardır. Başlamadan önce farklı çözümleri araştırmanız ve projenizin ihtiyaçlarına en uygun olanı seçmeniz önemlidir.