Riverpod 3.0 vs BLoC 9: 2026'da Hangi Eyalet Yönetimini Seçmeli
Flutter'da durum yönetimi seçimi mimari kararlardan biridir bir proje için en etkili olanıdır. Kötü bir seçim, yeniden düzenlemeyle karşılığını verir aylar sonra pahalı. 2026 yılında pazar iki çözüm etrafında birleşti üretime hazır: Nehir Pod 3.0 e Blok 9. Sağlayıcı - tarihsel çözüm - ve yeni projeler için miras olarak kabul edilecek.
Evrensel bir cevap yok. Riverpod, DX açısından üstündür (Geliştirici Deneyim), derleme zamanı güvenliği ve azaltılmış standart metin. BLoC avantajını koruyor Katı yapı ve denetim yolları gerektiren büyük ekiplerin olduğu kurumsal bağlamlarda durumların ve test edilebilirliği zorlayan net bir ayrımın varlığı. Bu kılavuz size seçebileceğiniz araçlar.
Ne Öğreneceksiniz
- Riverpod 3.0 ve BLoC 9'un mimarisi ve felsefesi
- Karar matrisi: bağlamınıza göre hangisi kullanılmalı?
- Riverpod: AsyncNotifier, kod oluşturma, geçersiz kılma ile test etme
- BLoC: Cubit ve BLoC, kapalı sınıflar, sıvı alımı
- Sağlayıcıdan Riverpod'a Geçiş: Kalıplar ve Zorluklar
- Performans karşılaştırması: ayrıntı düzeyini yeniden oluşturun
- Topluluk, ekosistem, 2026'da olgunlaştı
Riverpod 3.0: Felsefe ve Mimarlık
Riverpod, Sağlayıcının yapısal sorunlarını çözmek için tasarlandı: Flutter bağlamına bağımlılık, dışarıdaki sağlayıcılara erişememe Widget ağacından, sağlayıcılar için çalışma zamanı hataları bulunamadı. Riverpod sorunu çözdü Derleme zamanı güvenliği ile sağlayıcıları global olarak hareket ettirerek bu sorunları ortadan kaldırın.
// pubspec.yaml: dipendenze Riverpod 3.0
dependencies:
flutter_riverpod: ^3.0.0
riverpod_annotation: ^3.0.0
dev_dependencies:
riverpod_generator: ^3.0.0
build_runner: ^2.4.0
riverpod_lint: ^2.0.0
// Struttura base: provider senza context
// Senza code generation (verboso ma esplicito)
import 'package:flutter_riverpod/flutter_riverpod.dart';
// Provider semplice: valore computato
final userNameProvider = Provider<String>((ref) => 'Federico');
// StateProvider: stato semplice (counter, booleano, stringa)
final counterProvider = StateProvider<int>((ref) => 0);
// AsyncNotifierProvider: stato asincrono con loading/error/data
class UserNotifier extends AsyncNotifier<User> {
@override
Future<User> build() async {
// build() e chiamato all'inizializzazione e ai rebuild
return ref.watch(userRepositoryProvider).fetchCurrentUser();
}
Future<void> updateProfile(String name) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final updated = await ref.read(userRepositoryProvider).update(name);
return updated;
});
}
}
final userProvider = AsyncNotifierProvider<UserNotifier, User>(UserNotifier.new);
// Riverpod con code generation: zero boilerplate
// user_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user_provider.g.dart'; // generato da build_runner
@riverpod
class UserController extends _$UserController {
@override
Future<User> build() async {
// ref disponibile come campo, nessun parametro al costruttore
return ref.watch(userRepositoryProvider).fetchCurrentUser();
}
Future<void> updateProfile(String name) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
return ref.read(userRepositoryProvider).update(name);
});
}
}
// @riverpod genera automaticamente userControllerProvider
// Equivalente a:
// final userControllerProvider = AsyncNotifierProvider<UserController, User>(
// UserController.new
// );
// Widget consumer: accede allo stato senza context.read()
class UserProfileWidget extends ConsumerWidget {
const UserProfileWidget({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// ref.watch: si ricostruisce quando lo stato cambia
final userAsync = ref.watch(userControllerProvider);
return userAsync.when(
loading: () => const CircularProgressIndicator(),
error: (error, stack) => ErrorWidget(error.toString()),
data: (user) => Text(user.name),
);
}
}
BLoC 9: Felsefe ve Mimarlık
BLoC (İş Mantığı Bileşeni), kullanıcı arayüzünü iş mantığından kesin olarak ayırır tek yönlü akışlar aracılığıyla: Kullanıcı arayüzü olayları BLoC'ye gönderir, BLoC durumları yayar, Kullanıcı arayüzü durumlara yanıt olarak kendisini yeniden oluşturur. Bu açık ayrılık asıl mesele Test edilebilirlik ve öngörülebilirliğin kritik olduğu büyük ekiplerde güç.
// pubspec.yaml: dipendenze BLoC 9
dependencies:
flutter_bloc: ^9.0.0
equatable: ^2.0.5 // Per comparazione stati
dev_dependencies:
bloc_test: ^9.0.0 // Testing utilities
// Cubit: versione semplificata di BLoC (senza eventi espliciti)
// Usalo quando lo stato ha transizioni semplici
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0); // stato iniziale
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
void reset() => emit(0);
}
// BLoC completo: con eventi e stati espliciti
// Sealed classes Dart 3.x per stati type-safe
// events
abstract class AuthEvent {}
class LoginRequested extends AuthEvent {
final String email, password;
LoginRequested(this.email, this.password);
}
class LogoutRequested extends AuthEvent {}
class TokenRefreshRequested extends AuthEvent {}
// states con sealed class
sealed class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthAuthenticated extends AuthState {
final User user;
AuthAuthenticated(this.user);
}
class AuthUnauthenticated extends AuthState {}
class AuthError extends AuthState {
final String message;
AuthError(this.message);
}
// BLoC
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final AuthRepository _repo;
AuthBloc(this._repo) : super(AuthInitial()) {
on<LoginRequested>(_onLoginRequested);
on<LogoutRequested>(_onLogoutRequested);
}
Future<void> _onLoginRequested(
LoginRequested event,
Emitter<AuthState> emit,
) async {
emit(AuthLoading());
try {
final user = await _repo.login(event.email, event.password);
emit(AuthAuthenticated(user));
} catch (e) {
emit(AuthError(e.toString()));
}
}
Future<void> _onLogoutRequested(
LogoutRequested event,
Emitter<AuthState> emit,
) async {
await _repo.logout();
emit(AuthUnauthenticated());
}
}
Karar Matrisi: Riverpod ve BLoC
| Kriter | Nehir Pod 3.0 | Blok 9 |
|---|---|---|
| Standartlar | Düşük (kod oluştur) | Yüksek (olaylar + durumlar) |
| Derleme zamanı güvenliği | Harika | İyi (kapalı sınıflar) |
| Test edildi | Mükemmel (geçersiz kıl) | Mükemmel (bloc_test) |
| Sert yapı | Esnek | Çok katı |
| Harika takımlar | İyi | Harika |
| Öğrenme eğrisi | Ortalama | Yüksek |
| Eşzamansız işleme | Mükemmel (AsyncValue) | İyi (manuel) |
| Devlet kalıcılığı | Entegre (Riverpod Depolama) | hidratlı_bloc |
Riverpod'u Ne Zaman Seçmelisiniz?
- Küçük-orta ekip (1-5 geliştirici)
- Standartların azaltılmasının geliştirmeyi hızlandırdığı başlangıç veya hızla gelişen ürün
- AsyncValue'un yükleme/hata/verileri basitleştirdiği birçok eşzamansız istek
- Sağlayıcıdan Taşıma (Aile API'si)
- Widget ağacının dışındaki sağlayıcılara (hizmetler, arka plan görevleri) erişmek istediğinizde
BLoC Ne Zaman Seçilmeli?
- Titiz kod incelemelerine sahip kurumsal ekip (6'dan fazla geliştirici)
- Devlet denetim izleri gerektiren, düzenlenmiş endüstriler (fintech, sağlık hizmetleri)
- Kullanıcı arayüzü/mantık ayrımının mimari olarak uygulanması gereken proje
- Redux/NgRx deneyimine sahip ekip: felsefe ve benzeri
- Her durum geçişinin açık ve izlenebilir olmasını istediğinizde
Sağlayıcıdan Riverpod'a Geçiş
// Migrazione graduale: Provider -> Riverpod
// PRIMA: Provider (legacy)
// Provider: dipende dal context
final userProvider = ChangeNotifierProvider<UserModel>((ctx) => UserModel());
// Nel widget:
// final user = Provider.of<UserModel>(context);
// final user = context.watch<UserModel>();
// DOPO: Riverpod equivalente
// Nessuna dipendenza dal context
class UserNotifier extends Notifier<UserState> {
@override
UserState build() => UserState.initial();
void setName(String name) => state = state.copyWith(name: name);
}
final userNotifierProvider = NotifierProvider<UserNotifier, UserState>(
UserNotifier.new
);
// Strategia di migrazione graduale:
// 1. Aggiungi flutter_riverpod al progetto senza rimuovere Provider
// 2. Avvolgi l'app con ProviderScope (necessario per Riverpod)
// ProviderScope puo coesistere con MultiProvider
// 3. Migra un provider alla volta partendo dai leaf (quelli senza dipendenze)
// 4. Usa ref.read(oldProviderViaAdapter) come bridge temporaneo
// 5. Rimuovi Provider quando tutti i consumer sono migrati
Sonuçlar
2026'da her iki çözüm de olgunlaştı ve savaşta test edildi. Seçim bağlıdır bağlamdan: Çoğu proje için Riverpod 3.0 en iyisini sunar Yazılması ve bakımı daha az kodla DX. Gereksinimleri olan kurumsal ekipler için Denetim yolları ve sağlam yapısıyla BLoC rakipsiz olmaya devam ediyor. Önemli e Seçin ve tutarlı olun: iki modeli aynı modelde karıştırmaktan kaçının net bir stratejisi olmayan proje.







