Codegeneratie met riverpod_generator en @riverpod
Gebruik de annotaties van riverpod_generator om typeveilige providers zonder boilerplate te genereren.
Codegeneratie met riverpod_generator en @riverpod is een gratis Mobiele ontwikkeling met Flutter-les op CoddyKit. Dit is les 2 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Mobiele ontwikkeling met Flutter. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Mobiele ontwikkeling met Flutter bevat in totaal 4 lessen.
Waarom codegeneratie?
Vóór Riverpod 2.0 koos je handmatig het juiste providertype: Provider, StateProvider, FutureProvider, StreamProvider, NotifierProvider enzovoort. De verkeerde keuze betekende dat je code moest herschrijven.
Het pakket riverpod_generator draait dit om. Je schrijft een gewone functie of klasse en voegt de annotatie @riverpod toe. De generator inspecteert je retourtype en maakt automatisch de juiste, volledig typeveilige provider voor je.
- Minder boilerplate — geen handmatige providerdeclaraties.
- Typeveilige parameters — geef argumenten door zonder omslachtige
.family-constructies. - Standaard automatisch verwijderd — gegenereerde providers gedragen zich als
autoDispose.
De afhankelijkheden toevoegen
Voor codegeneratie heb je zowel pakketten voor runtime als voor ontwikkeling nodig. riverpod_annotation levert de annotatie @riverpod die je in de broncode gebruikt. riverpod_generator en build_runner voeren de buildstap uit die de .g.dart-bestanden genereert.
Een typische pubspec.yaml voor een Flutter-app ziet er als volgt uit.
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.5
dev_dependencies:
build_runner: ^2.4.11
riverpod_generator: ^2.4.0
custom_lint: ^0.6.4
riverpod_lint: ^2.3.10Je eerste gegenereerde provider
De kleinste gegenereerde provider is een functie op topniveau met de annotatie @riverpod. De eerste parameter is altijd een Ref-object; het retourtype bepaalt de rest.
Omdat deze functie synchroon een gewone String retourneert, genereert de generator een alleen-lezenprovider die deze waarde beschikbaar maakt. Je gebruikt die via ref.watch(helloWorldProvider), precies zoals een handgeschreven Provider<String>.
Let op de twee vereiste onderdelen: de part-directive en de opmerking // ignore_for_file is optioneel — maar part 'file.g.dart'; is verplicht.
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'hello.g.dart';
@riverpod
String helloWorld(Ref ref) {
return 'Hello, Riverpod 2.0';
}De generator uitvoeren
De annotatie doet op zichzelf niets totdat build_runner het bijbehorende .g.dart-bestand genereert. Voer dit uit vanuit de hoofdmap van het project.
- Eenmalige build: genereert één keer en sluit daarna af.
--delete-conflicting-outputsverwijdert verouderde gegenereerde bestanden. - Bewakingsmodus: genereert automatisch opnieuw telkens wanneer je een bronbestand opslaat — ideaal tijdens actieve ontwikkeling.
Na afloop kun je het symbool helloWorldProvider importeren.
# Generate once
dart run build_runner build --delete-conflicting-outputs
# Or watch and rebuild on save
dart run build_runner watch --delete-conflicting-outputsHet retourtype bepaalt de provider
De generator leest je retourtype en kiest stilletjes het bijbehorende providertype. Dat is het belangrijkste gemak van codegeneratie: je hoeft nooit meer zelf een providertype te benoemen.
Tretourneren → synchrone provider (zoalsProvider<T>).Future<T>retourneren → asynchrone provider dieAsyncValue<T>beschikbaar maakt (zoalsFutureProvider).Stream<T>retourneren → streamprovider dieAsyncValue<T>beschikbaar maakt (zoalsStreamProvider).
Hieronder verandert de provider in een asynchrone provider door alleen de handtekening te wijzigen naar Future — verder hoef je niets aan te passen.
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user.g.dart';
@riverpod
Future<String> userName(Ref ref) async {
await Future<void>.delayed(const Duration(seconds: 1));
return 'Ada Lovelace';
}Parameters doorgeven (geen .family meer)
Bij handgeschreven providers betekende parametriseren dat je .family en één tuple-achtig argument gebruikte. Met de generator kun je na ref gewone functieparameters toevoegen; die worden sterk getypeerde providerargumenten.
Hier accepteert messageProvider een int id. Je roept de provider aan als ref.watch(messageProvider(42)). Meerdere parameters en benoemde of optionele parameters werken ook.
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'message.g.dart';
@riverpod
Future<String> message(Ref ref, int id) async {
final repo = ref.watch(messageRepositoryProvider);
return repo.fetchById(id);
}
// Usage in a widget:
// final msg = ref.watch(messageProvider(42));Stateful logica: de Notifier-klasse
Voor veranderlijke state met methoden annoteer je een klasse die de gegenereerde basisklasse _$ClassName uitbreidt. Je overschrijft build() om de beginwaarde van de state te retourneren; de generator koppelt automatisch een NotifierProvider voor je aan.
Binnen methoden wijzig je state en worden luisteraars automatisch opnieuw opgebouwd. Hiermee vervang je de oude combinatie van Notifier en een handmatige NotifierProvider-declaratie door één geannoteerde klasse.
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter.g.dart';
@riverpod
class Counter extends _$Counter {
@override
int build() => 0;
void increment() => state++;
void reset() => state = 0;
}Asynchrone Notifiers
Als je build() een Future retourneert, genereert de generator een AsyncNotifier. De beschikbare state is een AsyncValue<T> die laad-, gegevens- en foutstatussen automatisch bijhoudt.
Om de state na een asynchrone actie bij te werken, wijs je AsyncValue.guard(...) toe aan state — deze voert je asynchrone code uit en vangt succes of fouten op zonder handmatige try/catch.
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'todos.g.dart';
@riverpod
class Todos extends _$Todos {
@override
Future<List<String>> build() async {
return ref.watch(todoRepositoryProvider).fetchAll();
}
Future<void> add(String title) async {
state = const AsyncLoading();
state = await AsyncValue.guard(() async {
await ref.read(todoRepositoryProvider).create(title);
return ref.read(todoRepositoryProvider).fetchAll();
});
}
}Gegenereerde providers gebruiken
Gegenereerde providers gebruik je precies zoals handmatige providers — het gegenereerde symbool is <name>Provider voor functies of <ClassName>Provider voor Notifier-klassen.
ref.watch(counterProvider)→ de huidige waarde van de state.ref.read(counterProvider.notifier)→ de Notifier-instantie, om methoden zoalsincrement()aan te roepen.- Bij asynchrone providers retourneert watch een
AsyncValuedie je met.when(...)afhandelt.
class CounterView extends ConsumerWidget {
const CounterView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Column(
children: [
Text('Count: $count'),
ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Text('Add'),
),
],
);
}
}In leven houden en afhankelijkheden
Gegenereerde providers worden standaard automatisch verwijderd — hun state wordt verwijderd wanneer er niet meer naar wordt geluisterd. Met twee annotatieopties houd je controle:
@Riverpod(keepAlive: true)— houdt de provider in leven, ook zonder luisteraars (gebruik dit voor singleton-objecten die app-breed beschikbaar zijn, zoals een Dio-client).@Riverpod(dependencies: [...])— declareert overschrijvingen binnen een scope voor het afbakenen van providers. De meeste apps hebben dit niet nodig.
De versie met hoofdletter, @Riverpod(...), is gewoon de configureerbare variant van de afkorting @riverpod met kleine letters.
import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'http.g.dart';
@Riverpod(keepAlive: true)
Dio dio(Ref ref) {
return Dio(BaseOptions(baseUrl: 'https://api.example.com'));
}Pure Dart: waarom de logica testbaar is
Een groot voordeel van codegeneratie is dat de bodies van je providers gewone Dart-functies en -klassen zijn — gemakkelijk te begrijpen en als unit te testen. Hieronder staat een zelfstandige illustratie van dezelfde state++-mutatielogica die een gegenereerde Notifier zou uitvoeren, zonder imports van Flutter of Riverpod.
Dit soort pure logica houd je precies binnen een gegenereerde @riverpod-klasse, zodat die eenvoudig te testen blijft.
class Counter {
int state = 0;
void increment() => state++;
void reset() => state = 0;
}
void main() {
final counter = Counter();
counter.increment();
counter.increment();
counter.increment();
print('After 3 increments: ${counter.state}');
counter.reset();
print('After reset: ${counter.state}');
}Snelle controle
Je annoteert een functie die Future<List<Product>> retourneert met @riverpod. Welk soort provider genereert riverpod_generator en hoe gebruik je die in een widget?
Samenvatting
Je hebt geleerd hoe riverpod_generator boilerplate voor providers verwijdert:
- Voeg
riverpod_annotation(runtime) enriverpod_generatorenbuild_runner(ontwikkeling) toe, plus eenpart '<file>.g.dart';-directive. - Annoteer een functie voor alleen-lezenwaarden of afgeleide waarden, of een klasse die
_$Nameuitbreidt voor stateful Notifiers. - Het retourtype kiest de provider:
T→ synchroon,Future<T>→ asynchroon (AsyncValue),Stream<T>→ stream. - Voeg gewone parameters toe na
refin plaats van.family. - Voer
dart run build_runner watchuit om bij elke opslag opnieuw te genereren. - Providers worden standaard automatisch verwijderd; gebruik
@Riverpod(keepAlive: true)voor singleton-objecten die app-breed beschikbaar zijn.
Leer Dart met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 22
- Lessen
- 88
Veelgestelde vragen
Is de les “Codegeneratie met riverpod_generator en @riverpod” gratis?
Ja — je kunt hier op het web alle 3 lessen van het leerpad Mobiele ontwikkeling met Flutter, waaronder “Codegeneratie met riverpod_generator en @riverpod”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Mobiele ontwikkeling met Flutter bevat in totaal 4 lessen.
Wat leer ik in “Codegeneratie met riverpod_generator en @riverpod”?
Gebruik de annotaties van riverpod_generator om typeveilige providers zonder boilerplate te genereren. Je oefent met Mobiele ontwikkeling met Flutter door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met Mobiele ontwikkeling met Flutter te beginnen?
Ervaring vooraf is niet nodig. Mobiele ontwikkeling met Flutter op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.
Hoe lang duurt de les “Codegeneratie met riverpod_generator en @riverpod”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over Mobiele ontwikkeling met Flutter?
Ja. Elke les over Mobiele ontwikkeling met Flutter bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Van Provider naar Riverpod: legacy-state migreren
- Codegeneratie met riverpod_generator en @riverpod
- AsyncNotifier- en FutureProvider-gegevenspijplijnen
- Providers scopen, overschrijven en ProviderObserver