Flutter'da State Management: Provider, Riverpod ve Bloc Karşılaştırması
Flutter'da state (durum) yönetimi, uygulamanın büyüdükçe en kritik tasarım kararlarından birine dönüşür. Bu yazıda Provider, Riverpod ve Bloc çözümlerini aynı senaryo üzerinden karşılaştıracağız: bir ürün listesi ile sepet yönetimi.
1. Provider
Google'ın önerdiği, basit ve anlaşılır çözümdür. InheritedWidget üzerine inşa edilmiştir.
// pubspec.yaml: provider: ^6.1.2
// Model
class SepetModel extends ChangeNotifier {
final List<String> _urunler = [];
List<String> get urunler => List.unmodifiable(_urunler);
int get adet => _urunler.length;
void ekle(String urun) {
_urunler.add(urun);
notifyListeners();
}
void cikar(String urun) {
_urunler.remove(urun);
notifyListeners();
}
}
// main.dart
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => SepetModel(),
child: const UygulamaWidget(),
),
);
}
// Okuma
class SepetBadge extends StatelessWidget {
@override
Widget build(BuildContext context) {
final adet = context.watch<SepetModel>().adet;
return Badge(label: Text(""));
}
}
// Yazma (rebuild olmadan)
ElevatedButton(
onPressed: () => context.read<SepetModel>().ekle("Laptop"),
child: const Text("Sepete Ekle"),
)
2. Riverpod
Provider'ın eksikliklerini gidermek için tasarlanan Riverpod, compile-time güvenli, testable ve widget ağacından bağımsız çalışır.
// pubspec.yaml: flutter_riverpod: ^2.5.1
// Provider tanımı
final sepetProvider = StateNotifierProvider<SepetNotifier, List<String>>(
(ref) => SepetNotifier(),
);
class SepetNotifier extends StateNotifier<List<String>> {
SepetNotifier() : super([]);
void ekle(String urun) => state = [...state, urun];
void cikar(String urun) => state = state.where((u) => u != urun).toList();
}
// main.dart
void main() {
runApp(const ProviderScope(child: UygulamaWidget()));
}
// ConsumerWidget ile kullanım
class UrunKarti extends ConsumerWidget {
final String urun;
const UrunKarti(this.urun, {super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final sepet = ref.watch(sepetProvider);
final sepette = sepet.contains(urun);
return ListTile(
title: Text(urun),
trailing: IconButton(
icon: Icon(sepette ? Icons.shopping_cart : Icons.add_shopping_cart),
onPressed: () => sepette
? ref.read(sepetProvider.notifier).cikar(urun)
: ref.read(sepetProvider.notifier).ekle(urun),
),
);
}
}
// Türetilmiş state — AsyncNotifierProvider
final urunlerProvider = AsyncNotifierProvider<UrunlerNotifier, List<String>>(
() => UrunlerNotifier(),
);
class UrunlerNotifier extends AsyncNotifier<List<String>> {
@override
Future<List<String>> build() async {
return await ApiServis().urunleriGetir();
}
Future<void> yenile() async {
state = const AsyncLoading();
state = await AsyncValue.guard(() => ApiServis().urunleriGetir());
}
}
3. Bloc / Cubit
Büyük ölçekli uygulamalar için tasarlanmış, event-driven mimari sunar. Cubit daha basit, Bloc daha kapsamlıdır.
// pubspec.yaml: flutter_bloc: ^8.1.5
// Cubit (basit)
class SepetCubit extends Cubit<List<String>> {
SepetCubit() : super([]);
void ekle(String urun) => emit([...state, urun]);
void cikar(String urun) => emit(state.where((u) => u != urun).toList());
void temizle() => emit([]);
}
// Bloc (event-driven)
// Events
abstract class SepetEvent {}
class UrunEkle extends SepetEvent { final String urun; UrunEkle(this.urun); }
class UrunCikar extends SepetEvent { final String urun; UrunCikar(this.urun); }
// State
class SepetState { final List<String> urunler; const SepetState(this.urunler); }
class SepetBloc extends Bloc<SepetEvent, SepetState> {
SepetBloc() : super(const SepetState([])) {
on<UrunEkle>((event, emit) =>
emit(SepetState([...state.urunler, event.urun])));
on<UrunCikar>((event, emit) =>
emit(SepetState(state.urunler.where((u) => u != event.urun).toList())));
}
}
// UI kullanımı
BlocProvider(
create: (_) => SepetBloc(),
child: BlocBuilder<SepetBloc, SepetState>(
builder: (context, state) {
return Text("Sepet: ürün");
},
),
)
Karşılaştırma Tablosu
| Özellik | Provider | Riverpod | Bloc |
|---|---|---|---|
| Öğrenme eğrisi | Düşük | Orta | Yüksek |
| Compile-time güvenlik | Kısmi | Tam | Tam |
| Test edilebilirlik | İyi | Mükemmel | Mükemmel |
| Büyük proje uygunluğu | Orta | İyi | Mükemmel |
| Boilerplate | Az | Az | Fazla |
| Devtools desteği | Yok | Kısmi | Tam |
Ne Zaman Hangisini Seçmeli?
- Provider: Küçük-orta projeler, prototip, hızlı geliştirme
- Riverpod: Orta-büyük projeler, async-heavy uygulamalar, modern yaklaşım
- Bloc: Kurumsal projeler, büyük ekipler, katı mimari gerektiren durumlar
Sonuç
Üç çözüm de production-ready olgunluktadır. Yeni başlıyorsanız Riverpod ile başlamanızı öneririm — Provider'ın sadeliğini, Bloc'un güvenilirliğini bir araya getirir. Büyük ekiplerde ise Bloc'un öngörülebilir event döngüsü ve DevTools desteği tercih sebebidir.