Riverpod — essentie, compilatie van afhankelijkheden in Flutter

Auteur: IT Sectr Gepubliceerd: 2026-02-19 Leestijd: 7 min

Riverpod — een compileerbare state- en afhankelijkheidsbeheerder voor Flutter, gemaakt door Rémi Roussel in 2021 als opvolger van Provider. Riverpod lost de fundamentele problemen van Provider op: gebrek aan compilatiecontrole, afhankelijkheid van BuildContext en moeilijkheden met ProviderNotFoundException. Volgens gegevens van pub.dev heeft het pakket meer dan 5.000 likes verzameld en vervangt het actief Provider in nieuwe projecten.

Belangrijkste

  • ProviderRef — object voor toegang tot andere providers binnen een provider
  • AsyncValue — wrapper voor asynchrone gegevens met loading/error/data statussen
  • Notifier — klasse voor muteerbare toestand met wijzigingsmethoden
  • ProviderScope — rootwidget die alle providers beheert
  • Code Generation — @riverpod annotaties voor automatische generatie van providers

Wat is Riverpod?

Riverpod — een bibliotheek voor state management en dependency injection in Flutter die providerbeschrijvingen compileert naar veilige Dart-code. In tegenstelling tot Provider zijn Riverpod-providers niet gebonden aan BuildContext: ze worden globaal of in ProviderScope aangemaakt en zijn overal toegankelijk. De compiler controleert types, afhankelijkheden en de integriteit van de providergraaf tijdens de buildfase, waardoor runtime-fouten zoals ProviderNotFoundException worden geëlimineerd.

Riverpod gebruikt het override-model voor testen: elke provider kan worden overschreven via ProviderScope.overrideWith zonder dat subklassen of mock-interfaces nodig zijn. Dit maakt testen geïsoleerd: elke test krijgt zijn eigen kopie van de afhankelijkhedengraaf die volledig wordt gecontroleerd.

Volgens de Flutter Community Survey 2025 staat Riverpod op de derde plaats qua populariteit na Provider en BLoC. Tegelijkertijd is Riverpod het snelst groeiende pakket: +120% installaties in 2024. Belangrijkste redenen: compilatieveiligheid, afwezigheid van ProviderNotFoundException, ingebouwde ondersteuning voor asynchronie via AsyncValue.

Soorten providers

Riverpod biedt 8 soorten providers, elk voor een specifiek scenario: Provider (constante/dienst), StateProvider (primitieve toestand), StateNotifierProvider (complexe logica met StateNotifier), ChangeNotifierProvider (voor migratie van Provider), FutureProvider (asynchrone gegevens, eenmalig), StreamProvider (reactieve stroom), NotifierProvider (nieuwe API, Flutter 3.10+) en AsyncNotifierProvider (asynchrone Notifier).

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 — het object dat aan elke provider wordt doorgegeven voor toegang tot andere providers. ref.watch — abonneren op wijzigingen, ref.read — eenmalig lezen, ref.invalidate — cache resetten. ProviderRef vervangt BuildContext van Provider: elke provider kan andere providers lezen zonder toegang tot de widgetboom. Dit maakt het mogelijk om een afhankelijkhedengraaf buiten de UI-laag te bouwen.

ProviderScope — de verplichte rootwidget voor Riverpod. ProviderScope slaat alle providers op, beheert hun levenscyclus en cacht waarden. Zonder ProviderScope zal de applicatie crashen met ProviderNotFoundException. ProviderScope kan genest worden — een geneste scope overschrijft de providers van de ouder, wat wordt gebruikt voor testen en isolatie van functies.

AsyncValue en werken met asynchronie

AsyncValue — een sealed-klasse van Riverpod voor het weergeven van asynchrone toestand. AsyncValue heeft drie varianten: AsyncData (succesvolle gegevens), AsyncError (fout), AsyncLoading (laden). In plaats van handmatig schakelen tussen loading/error/data retourneert elke FutureProvider of StreamProvider automatisch AsyncValue, en de widget verwerkt alle drie de statussen via 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 — methode voor patroonherkenning van alle drie de statussen. De compiler controleert dat alle drie de gevallen worden afgehandeld — als loading of error wordt vergeten, zal de code niet compileren. AsyncValue.whenData — alleen voor data (als loading/error niet nodig zijn). AsyncValue.guard — een try-catch wrapper voor het omzetten van uitzonderingen naar AsyncError. keepAlive — een vlag die vernietiging van de providercache bij het verlaten van het bereik voorkomt.

Code Generation en @riverpod

Codegeneratie — een belangrijk kenmerk van Riverpod 2.0+. De annotatie @riverpod boven een functie genereert automatisch een provider met het juiste type, ondersteuning voor refactoring en automatische aanvulling. Codegeneratie gebruikt riverpod_generator en build_runner. De ontwikkelaar schrijft een pure functie en al het andere — types, klassen, factory-constructors — wordt automatisch gegenereerd.

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

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

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

Notifier — een nieuwe API voor muteerbare toestand met codegeneratie. Notifier is een klasse met een build()-methode en methoden om de toestand te wijzigen. In tegenstelling tot StateNotifier heeft Notifier geen aparte toestandsklasse nodig en biedt directe toegang tot state via getter/setter. Riverpod genereert automatisch NotifierProvider voor elke Notifier-klasse met de annotatie @riverpod.

build_runner: codegeneratie wordt gestart met het commando dart run build_runner build. Gegenereerde bestanden hebben het achtervoegsel .g.dart en worden geïmporteerd in de broncode. Bij wijziging van annotaties of providertypes moet codegeneratie opnieuw worden gestart. Riverpod 2.x beveelt codegeneratie aan voor alle nieuwe projecten — handmatig aanmaken van providers raakt verouderd.

Riverpod vs Provider

Belangrijkste verschillen tussen Riverpod en Provider: onafhankelijkheid van BuildContext, compilatieveiligheid, ingebouwd werken met asynchronie, automatische caching en testen via override. Provider vereist BuildContext voor toegang tot de toestand (context.watch, context.read), Riverpod gebruikt WidgetRef en globaal gedeclareerde providers.

KenmerkProviderRiverpod
Afhankelijkheid van BuildContextJaNee
CompilatiecontroleNeeJa (via @riverpod)
ProviderNotFoundExceptionRuntimeOnmogelijk
AsynchronieHandmatigAsyncValue (ingebouwd)
TestenWrapper in ProviderProviderScope.overrideWith
CachingNeeAutomatisch + keepAlive

Migratie van Provider: Riverpod ondersteunt ChangeNotifierProvider.adaptive voor het gebruik van bestaande ChangeNotifiers zonder herschrijven. Gefaseerde migratie: eerst worden nieuwe functies met Riverpod geschreven, daarna worden oude Providers vervangen door Riverpod-providers via een adapter. Beide pakketten kunnen in hetzelfde project naast elkaar bestaan, wat migratie mogelijk maakt zonder ontwikkeling stil te leggen.

Riverpod testen

Riverpod testen is gebaseerd op ProviderScope.overrideWith. Elke provider wordt overschreven binnen de test-ProviderScope zonder mocks en DI-containers. ProviderContainer — een geïsoleerde omgeving voor tests zonder Flutter (pure Dart), waarmee providers kunnen worden getest zonder widgets te renderen.

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 — zonder Flutter. Gebruik ProviderContainer voor unittesten van providers zonder widgets. overrideWithValue — vervanging van de provider door een specifieke waarde. overrideWith — vervanging door een providerfactory (voor het mocken van diensten). autodispose — controleer in tests of de provider wordt vernietigd bij het verlaten van het bereik, met container.dispose().

Veelgestelde vragen

Waarin verschilt Riverpod van BLoC?

Riverpod — een bibliotheek voor state management met globale providers, AsyncValue en codegeneratie. BLoC — een architectuurpatroon met Event → Stream → State. Riverpod is gemakkelijker te leren en heeft een betere DX via @riverpod-annotaties. BLoC biedt strikte isolatie van bedrijfslogica en Event-tracering via BlocObserver. De keuze hangt af van de paradigma van het project: Riverpod staat dichter bij Provider, BLoC bij reactieve streams.

Wat is autodispose in Riverpod?

Autodispose — het mechanisme voor automatische vernietiging van een provider wanneer niemand erop is geabonneerd. Standaard zijn alle Riverpod-providers autodispose: wanneer de widget de boom verlaat, wordt de provider uit het geheugen verwijderd. keepAlive — een vlag die autodispose uitschakelt voor providers die altijd moeten blijven bestaan (API-cliënten, repositories, instellingen). Dit voorkomt geheugenlekken — ongebruikte providers worden automatisch vernietigd.

Hoe werkt ref.invalidate?

ref.invalidate — een methode die de cache van de provider forceert te resetten. Na invalidate wordt de provider opnieuw aangemaakt bij de volgende lezing: FutureProvider voert de async-functie opnieuw uit, StreamProvider abonneert zich opnieuw op de stroom. Gebruik invalidate voor geforceerde gegevensvernieuwing (pull-to-refresh, gebruikerswissel). ref.refresh — een combinatie van invalidate + lezen: reset en leest onmiddellijk de nieuwe waarde in één bewerking.

Kan Riverpod zonder codegeneratie worden gebruikt?

Ja. Riverpod 1.x werkt alleen zonder codegeneratie — providers worden handmatig aangemaakt via Provider(), StateNotifierProvider(), FutureProvider() enz. Riverpod 2.x ondersteunt beide benaderingen. Zonder codegeneratie is er meer boilerplate, maar geen afhankelijkheid van build_runner en dart run build_runner build. Voor kleine projecten (tot 30 providers) is handmatig aanmaken gerechtvaardigd, voor grote projecten is codegeneratie verplicht.

Wat zijn Family-providers?

Family — een modificator van een provider die een externe parameter accepteert. Bijvoorbeeld userProvider(123) — een provider die de gebruiker met ID 123 laadt. Family-providers cachen het resultaat voor elke unieke parameter apart. Gebruik Family voor een lijst met elementen waarbij elk element wordt geladen op ID. De Family-modificator is beschikbaar voor alle providertypes: Provider.family, FutureProvider.family, StreamProvider.family.

Samenvatting

  • Riverpod — compileerbare statebeheerder, opvolger van Provider zonder ProviderNotFoundException
  • ProviderRef — vervanger voor BuildContext voor toegang tot providers binnen andere providers
  • AsyncValue — sealed-klasse met loading/error/data statussen voor asynchrone gegevens
  • @riverpod codegeneratie — automatische type-inferentie en providerfabrieken
  • ProviderScope.overrideWith — geïsoleerd testen zonder mocks en DI-containers
  • Family — geparametriseerde providers met individuele caching
  • autodispose en keepAlive — automatisch beheer van de providerlevenscyclus

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook