Riverpod — esența, compilarea dependențelor în Flutter

Autor: IT Sectr Publicat: 2026-02-19 Timp de citire: 7 min

Riverpod — un manager de stare și dependențe compilabil pentru Flutter, creat de Rémi Roussel în 2021 ca succesor al Provider. Riverpod rezolvă problemele fundamentale ale Provider: lipsa verificării la compilare, dependența de BuildContext și dificultatea cu ProviderNotFoundException. Conform datelor pub.dev, pachetul a strâns peste 5 mii de aprecieri și înlocuiește activ Provider în proiecte noi.

Principalele

  • ProviderRef — obiect pentru accesarea altor provideri în interiorul unui provider
  • AsyncValue — înveliș pentru date asincrone cu stările loading/error/data
  • Notifier — clasă pentru stare mutabilă cu metode de modificare
  • ProviderScope — widget-ul rădăcină care gestionează toți providerii
  • Code Generation — adnotări @riverpod pentru generarea automată a providerilor

Ce este Riverpod?

Riverpod — o bibliotecă pentru gestionarea stării și injectarea dependențelor în Flutter, care compilează descrierea providerilor în cod Dart sigur. Spre deosebire de Provider, providerii Riverpod nu sunt legați de BuildContext: sunt creați global sau în ProviderScope și sunt accesibili din orice loc. Compilatorul verifică tipurile, dependențele și integritatea grafului de provideri în faza de build, eliminând erorile de runtime precum ProviderNotFoundException.

Riverpod folosește modelul override pentru testare: fiecare provider poate fi suprascris prin ProviderScope.overrideWith fără a fi nevoie să creeze subclase sau să mock-uiască interfețe. Acest lucru face testarea izolată: fiecare test primește propria copie a grafului de dependențe, care este complet controlată.

Conform Flutter Community Survey 2025, Riverpod ocupă locul trei ca popularitate după Provider și BLoC. În același timp, Riverpod este cel mai rapid pachet în creștere: +120% instalări în 2024. Principalele motive: siguranța la compilare, absența ProviderNotFoundException, suportul încorporat pentru asincronitate prin AsyncValue.

Tipuri de provideri

Riverpod oferă 8 tipuri de provideri, fiecare pentru un scenariu specific: Provider (constantă/serviciu), StateProvider (stare primitivă), StateNotifierProvider (logică complexă cu StateNotifier), ChangeNotifierProvider (pentru migrare de la Provider), FutureProvider (date asincrone, o singură dată), StreamProvider (flux reactiv), NotifierProvider (API nou, Flutter 3.10+) și AsyncNotifierProvider (Notifier asincron).

Dart
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);

  void increment() => state++;
  void decrement() => state--;
}

class CounterScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('$count');
  }
}

ProviderRef — obiectul transmis fiecărui provider pentru accesarea altor provideri. ref.watch — abonare la modificări, ref.read — citire unică, ref.invalidate — resetarea cache-ului. ProviderRef înlocuiește BuildContext din Provider: orice provider poate citi alți provideri fără acces la arborele de widget-uri. Acest lucru permite construirea unui graf de dependențe în afara stratului UI.

ProviderScope — widget-ul rădăcină obligatoriu pentru funcționarea Riverpod. ProviderScope stochează toți providerii, le gestionează ciclul de viață și cache-uiește valorile. Fără ProviderScope aplicația va cădea cu ProviderNotFoundException. ProviderScope poate fi imbricat — un scope imbricat suprascrie providerii părintelui, ceea ce este folosit pentru testare și izolarea funcționalităților.

AsyncValue și lucrul cu asincronitatea

AsyncValue — o clasă sealed a Riverpod pentru reprezentarea stării asincrone. AsyncValue are trei variante: AsyncData (date reușite), AsyncError (eroare), AsyncLoading (încărcare). În locul comutării manuale între loading/error/data, fiecare FutureProvider sau StreamProvider returnează automat AsyncValue, iar widget-ul gestionează toate cele trei stări prin ref.watch.

Dart
final userProvider = FutureProvider((ref) async {
  final api = ref.watch(apiProvider);
  return await api.fetchUser();
});

class UserScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userProvider);
    return userAsync.when(
      data: (user) => UserWidget(user),
      error: (e, _) => ErrorWidget(e.toString()),
      loading: () => CircularProgressIndicator(),
    );
  }
}

AsyncValue.when — metodă pentru potrivirea tiparelor tuturor celor trei stări. Compilatorul verifică că toate cele trei cazuri sunt gestionate — dacă se uită loading sau error, codul nu se va compila. AsyncValue.whenData — doar pentru data (dacă loading/error nu sunt necesare). AsyncValue.guard — un înveliș try-catch pentru convertirea excepțiilor în AsyncError. keepAlive — un flag care previne distrugerea cache-ului providerului la ieșirea din domeniul de vizibilitate.

Code Generation și @riverpod

Generarea de cod — o caracteristică cheie a Riverpod 2.0+. Adnotarea @riverpod deasupra unei funcții generează automat un provider cu tipul corect, suport pentru refactorizare și autocompletare. Generarea de cod folosește riverpod_generator și build_runner. Dezvoltatorul scrie o funcție pură, iar tot restul — tipuri, clase, constructori factory — sunt generate automat.

Dart
@riverpod
String helloWorld(HelloWorldRef ref) {
  return 'Hello World';
}

// Generat: final helloWorldProvider = Provider((ref) => 'Hello World');

@riverpod
class Counter extends _$Counter {
  int build() => 0;
  void increment() => state++;
}

Notifier — un API nou pentru stare mutabilă cu generare de cod. Notifier este o clasă cu metoda build() și metode de modificare a stării. Spre deosebire de StateNotifier, Notifier nu necesită o clasă separată de stare și oferă acces direct la state prin getter/setter. Riverpod generează automat NotifierProvider pentru fiecare clasă Notifier cu adnotarea @riverpod.

build_runner: generarea de cod se rulează cu comanda dart run build_runner build. Fișierele generate au sufixul .g.dart și sunt importate în codul sursă. La modificarea adnotărilor sau tipurilor de provideri, trebuie repornită generarea de cod. Riverpod 2.x recomandă generarea de cod pentru toate proiectele noi — crearea manuală a providerilor devine învechită.

Riverpod vs Provider

Diferențele principale dintre Riverpod și Provider: independența de BuildContext, siguranța la compilare, lucrul încorporat cu asincronitatea, cache-ul automat și testarea prin override. Provider necesită BuildContext pentru accesarea stării (context.watch, context.read), Riverpod folosește WidgetRef și provideri declarați global.

CaracteristicăProviderRiverpod
Dependența de BuildContextDaNu
Verificare la compilareNuDa (prin @riverpod)
ProviderNotFoundExceptionRuntimeImposibil
AsincronitateManualăAsyncValue (încorporat)
TestareÎnveliș în ProviderProviderScope.overrideWith
CacheNuAutomat + keepAlive

Migrarea de la Provider: Riverpod suportă ChangeNotifierProvider.adaptive pentru utilizarea ChangeNotifier-urilor existente fără rescriere. Migrare etapizată: mai întâi funcționalitățile noi sunt scrise cu Riverpod, apoi Provider-ii vechi sunt înlocuiți cu provideri Riverpod printr-un adaptor. Ambele pachete pot coexista în același proiect, permițând migrarea fără a îngheța dezvoltarea.

Testarea Riverpod

Testarea Riverpod se bazează pe ProviderScope.overrideWith. Fiecare provider este suprascris în ProviderScope-ul de test fără mock-uri și containere DI. ProviderContainer — un mediu izolat pentru teste fără Flutter (Dart pur), care permite testarea providerilor fără randarea widget-urilor.

Dart
import 'package:flutter_test/flutter_test.dart';
import 'package:riverpod/riverpod.dart';

void main() {
  test('Counter increments correctly', () {
    final container = ProviderContainer();
    container.read(counterProvider.notifier).increment();
    expect(container.read(counterProvider), 1);
  });

  testWidgets('UI updates on increment', (tester) async {
    await tester.pumpWidget(
      ProviderScope(
        overrides: [counterProvider.overrideWithValue(5)],
        child: CounterScreen(),
      ),
    );
    expect(find.text('5'), findsOneWidget);
  });
}

ProviderContainer — fără Flutter. Folosiți ProviderContainer pentru teste unitare ale providerilor fără widget-uri. overrideWithValue — înlocuirea providerului cu o valoare specifică. overrideWith — înlocuirea cu o fabrică de provider (pentru mock-uri de servicii). autodispose — în teste verificați dacă providerul este distrus la ieșirea din domeniul de vizibilitate, cu container.dispose().

Întrebări frecvente

Cu ce se deosebește Riverpod de BLoC?

Riverpod — o bibliotecă de gestionare a stării cu provideri globali, AsyncValue și generare de cod. BLoC — un model arhitectural cu Event → Stream → State. Riverpod este mai ușor de învățat și are o experiență de dezvoltare mai bună prin adnotările @riverpod. BLoC oferă o izolare strictă a logicii de afaceri și urmărirea Event prin BlocObserver. Alegerea depinde de paradigma proiectului: Riverpod este mai aproape de Provider, BLoC — de fluxurile reactive.

Ce este autodispose în Riverpod?

Autodispose — mecanismul de distrugere automată a providerului atunci când nimeni nu este abonat la el. În mod implicit, toți providerii Riverpod sunt autodispose: la ieșirea widget-ului din arbore, providerul este eliminat din memorie. keepAlive — un flag care dezactivează autodispose pentru providerii care trebuie să trăiască întotdeauna (clienți API, depozite, setări). Acest lucru previne scurgerile de memorie — providerii nefolosiți sunt distruși automat.

Cum funcționează ref.invalidate?

ref.invalidate — o metodă care resetează forțat cache-ul providerului. După invalidate, providerul este recreat la următoarea citire: FutureProvider execută din nou funcția async, StreamProvider se reabonează la flux. Folosiți invalidate pentru reîmprospătarea forțată a datelor (pull-to-refresh, schimbare utilizator). ref.refresh — o combinație de invalidate + citire: resetează și citește imediat noua valoare într-o singură operație.

Se poate folosi Riverpod fără generare de cod?

Da. Riverpod 1.x funcționează doar fără generare de cod — providerii sunt creați manual prin Provider(), StateNotifierProvider(), FutureProvider() etc. Riverpod 2.x suportă ambele abordări. Fără generare de cod există mai mult boilerplate, dar nu există dependență de build_runner și dart run build_runner build. Pentru proiecte mici (până la 30 de provideri) crearea manuală este justificată, pentru proiecte mari generarea de cod este obligatorie.

Ce sunt providerii Family?

Family — un modificator de provider care acceptă un parametru extern. De exemplu, userProvider(123) — un provider care încarcă utilizatorul cu ID 123. Providerii Family cache-uiesc rezultatul pentru fiecare parametru unic separat. Folosiți Family pentru o listă de elemente unde fiecare element este încărcat după ID. Modificatorul Family este disponibil pentru toate tipurile de provideri: Provider.family, FutureProvider.family, StreamProvider.family.

Rezumat

  • Riverpod — manager de stare compilabil, succesor al Provider fără ProviderNotFoundException
  • ProviderRef — înlocuitor pentru BuildContext pentru accesarea providerilor în interiorul altor provideri
  • AsyncValue — clasă sealed cu stările loading/error/data pentru date asincrone
  • Generare de cod @riverpod — inferență automată a tipurilor și fabricilor de provideri
  • ProviderScope.overrideWith — testare izolată fără mock-uri și containere DI
  • Family — provideri parametrizați cu cache individual
  • autodispose și keepAlive — gestionarea automată a ciclului de viață al providerilor

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și