Riverpod 3.0 vs BLoC 9: Jaký státní management zvolit v roce 2026
Volba státní správy ve Flutteru je jedním z architektonických rozhodnutí nejpůsobivější pro projekt. Špatná volba se vyplácí refaktoringem drahé měsíce později. V roce 2026 se trh zkonsolidoval kolem dvou řešení připraveno k výrobě: Riverpod 3.0 e BloC 9. Poskytovatel — historické řešení — a být považován za dědictví pro nové projekty.
Univerzální odpověď neexistuje. Riverpod vyniká z hlediska DX (Developer Zkušenosti), bezpečnost při kompilaci a snížený standard. BLoC si udržuje výhodu v podnikových kontextech s velkými týmy, které vyžadují pevnou strukturu a auditní záznamy států a jasné oddělení, které si vynucuje testovatelnost. Tato příručka vám dává nástroje k výběru.
Co se naučíte
- Architektura a filozofie Riverpod 3.0 vs BLoC 9
- Rozhodovací matice: kterou použít na základě vašeho kontextu
- Riverpod: AsyncNotifier, generování kódu, testování s přepsáním
- BLoC: Cubit vs BLoC, uzavřené třídy, hydratace
- Migrace od poskytovatele k Riverpodu: vzory a úskalí
- Porovnání výkonu: znovu sestavit granularitu
- Komunita, ekosystém, dozrála v roce 2026
Riverpod 3.0: Filosofie a architektura
Riverpod byl navržen tak, aby vyřešil strukturální problémy poskytovatele: závislost na kontextu Flutter, nemožnost přístupu k externím poskytovatelům ze stromu widgetů nebyly nalezeny chyby za běhu pro poskytovatele. Riverpod to opravuje tyto problémy přesunem poskytovatelů globálně s bezpečností v době kompilace.
// 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: Filosofie a architektura
BLoC (Business Logic Component) přísně odděluje uživatelské rozhraní od obchodní logiky prostřednictvím jednosměrných toků: uživatelské rozhraní posílá události do BLoC, BLoC vysílá stavy, uživatelské rozhraní se v reakci na stavy přestavuje. Toto jasné oddělení je smyslem síly ve velkých týmech, kde je kritická testovatelnost a předvídatelnost.
// 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());
}
}
Rozhodovací matice: Riverpod vs BLoC
| Kritérium | Riverpod 3.0 | BloC 9 |
|---|---|---|
| Deska kotle | Nízká (gen gen) | Vysoká (události + stavy) |
| Bezpečnost během kompilace | Vynikající | Dobré (uzavřené třídy) |
| Testováno | Vynikající (přepsat) | Vynikající (bloc_test) |
| Pevná konstrukce | Flexibilní | Velmi tuhé |
| Skvělé týmy | Dobrý | Vynikající |
| Křivka učení | Průměrný | Vysoký |
| Asynchronní zpracování | Vynikající (AsyncValue) | Dobrý (manuální) |
| Vytrvalost státu | Integrované (úložiště Riverpod) | hydratovaný_blok |
Kdy zvolit Riverpod
- Malý-střední tým (1-5 vývojářů)
- Uvedení do provozu nebo rychle se vyvíjející produkt, kde zmenšení standardu urychluje vývoj
- Mnoho asynchronních požadavků, kde AsyncValue zjednodušuje načítání/chybu/data
- Migrace od poskytovatele (Family API)
- Když chcete přistupovat k poskytovatelům mimo strom widgetů (služby, úlohy na pozadí)
Kdy zvolit BLoC
- Podnikový tým (6+ vývojářů) s přísnými kontrolami kódu
- Regulovaná odvětví (fintech, zdravotnictví), která vyžadují státní auditní záznamy
- Projekt, kde musí být architektonicky vynuceno oddělení uživatelského rozhraní a logiky
- Tým se zkušenostmi Redux/NgRx: filozofie a podobně
- Když chcete, aby byl každý přechod stavu explicitní a sledovatelný
Migrace z Provider na Riverpod
// 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
Závěry
V roce 2026 jsou obě řešení vyzrálá a otestovaná. Výběr závisí z kontextu: Pro většinu projektů nabízí Riverpod 3.0 to nejlepší DX s méně kódem pro zápis a údržbu. Pro podnikové týmy s požadavky Díky auditním záznamům a pevné struktuře zůstává BLoC nepřekonatelná. Důležité e vybírejte a buďte konzistentní: vyhněte se míchání dvou vzorů ve stejném projekt bez jasné strategie.







