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
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.
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).
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 — 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.
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.
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.
@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.
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.
| Kenmerk | Provider | Riverpod |
|---|---|---|
| Afhankelijkheid van BuildContext | Ja | Nee |
| Compilatiecontrole | Nee | Ja (via @riverpod) |
| ProviderNotFoundException | Runtime | Onmogelijk |
| Asynchronie | Handmatig | AsyncValue (ingebouwd) |
| Testen | Wrapper in Provider | ProviderScope.overrideWith |
| Caching | Nee | Automatisch + 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 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.
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
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.
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.
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.
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.
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
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.
Lees ook