Mobiele ontwikkeling met Flutter · Les

Van Provider naar Riverpod: legacy-state migreren

Zet bestaande ChangeNotifier- en Provider-code om naar Riverpod's compile-veilige providergrafiek.

Les 1 van 413 stappen

Van Provider naar Riverpod: legacy-state migreren is een gratis Mobiele ontwikkeling met Flutter-les op CoddyKit. Dit is les 1 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 migreren naar Riverpod?

Het klassieke pakket provider loste dependency injection in Flutter op, maar heeft duidelijke zwakke punten die je waarschijnlijk al in productie bent tegengekomen:

  • Crashes tijdens runtime — als je context.read<T>() aanroept voor een type dat nooit is aangeboden, wordt tijdens runtime een ProviderNotFoundException gegenereerd, niet tijdens het compileren.
  • Koppeling aan BuildContext — je kunt providers alleen uitlezen waar je een BuildContext hebt.
  • Geen combinatie — het is omslachtig en foutgevoelig om de ene ChangeNotifier van een andere afhankelijk te maken.

Riverpod is een herschrijving door dezelfde auteur. Providers zijn globale declaraties op het hoogste niveau die de compiler kan controleren, zodat een ontbrekende afhankelijkheid een compileerfout wordt. In deze les leer je stap voor stap hoe je een verouderde app met ChangeNotifier + Provider migreert naar Riverpod 2.0.

De verouderde code die we migreren

Hier zie je een typische verouderde tellerfunctie met ChangeNotifier. Deze stelt een waarde en een methode beschikbaar die notifyListeners() aanroept. Dit patroon zetten we stap voor stap om.

Let op dat de status (_count) en de logica voor wijzigingen samen in een klasse staan die ChangeNotifier uitbreidt.

// LEGACY — provider package
import 'package:flutter/foundation.dart';

class CounterModel extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void reset() {
    _count = 0;
    notifyListeners();
  }
}

Hoe het verouderde model werd gekoppeld

In de oude opzet registreer je het model hoog in de widgetstructuur met ChangeNotifierProvider en lees je het uit via context.watch / context.read. Het probleem: als je vergeet het model te registreren, compileert de app zonder problemen en crasht die pas wanneer dat scherm wordt geopend.

// LEGACY wiring
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => CounterModel(),
      child: const MyApp(),
    ),
  );
}

// In a widget:
final count = context.watch<CounterModel>().count;
context.read<CounterModel>().increment();

Stap 1 — Riverpod installeren en ProviderScope toevoegen

Voeg flutter_riverpod toe aan pubspec.yaml. De belangrijkste wijziging in de koppeling is dat je je app in een ProviderScope wikkelt. Dit is de container die de status van elke provider opslaat — het vervangt de nesteling van ChangeNotifierProvider-widgets aan de basis.

  • Oud: veel provider-widgets rond MyApp.
  • Nieuw: één ProviderScope. Providers worden zelf als globale declaraties gedefinieerd, niet in de structuur.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

Stap 2 — ChangeNotifier wordt Notifier

Riverpod 2.0 introduceert Notifier als de moderne, typeveilige vervanging voor ChangeNotifier. De verschillen:

  • Je bewaart de status in één veld met de naam state in plaats van in privévelden met getters.
  • Je roept nooit notifyListeners() aan — door state opnieuw toe te wijzen, worden luisteraars automatisch opnieuw opgebouwd.
  • De methode build() retourneert de beginstatus.

Hier is de gemigreerde teller als Notifier<int>:

import 'package:flutter_riverpod/flutter_riverpod.dart';

class CounterNotifier extends Notifier<int> {
  @override
  int build() => 0; // initial state

  void increment() => state = state + 1;
  void reset() => state = 0;
}

final counterProvider = NotifierProvider<CounterNotifier, int>(
  CounterNotifier.new,
);

Stap 3 — Status in de gebruikersinterface uitlezen met ConsumerWidget

Vervang context.watch/context.read door een WidgetRef. De duidelijkste aanpak is om van je widget een ConsumerWidget te maken, die een parameter ref aan build toevoegt.

  • ref.watch(provider) — abonneer je en bouw opnieuw op bij wijzigingen (gebruik dit in build).
  • ref.read(provider.notifier) — haal de notifier op om methoden aan te roepen (gebruik dit in callbacks).
class CounterScreen extends ConsumerWidget {
  const CounterScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Scaffold(
      body: Center(child: Text('Count: $count')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

watch tegenover read — de migratievalkuil

De meest voorkomende migratiefout is de verkeerde toegangsmethode gebruiken. Koppel de oude API zorgvuldig aan de nieuwe:

  • context.watch<T>() → ref.watch(provider)  (bouwt de gebruikersinterface opnieuw op)
  • context.read<T>() in een callback → ref.read(provider.notifier)  (geen nieuwe opbouw)

Regel: roep ref.watch nooit aan binnen een onPressed-callback — dit werkt niet zoals verwacht en kan onnodige nieuwe opbouw veroorzaken. Gebruik ref.read voor eenmalige acties en ref.watch voor waarden die in de gebruikersinterface worden weergegeven.

Stap 4 — Asynchrone status migreren (FutureProvider)

In verouderde apps worden gegevens vaak in een ChangeNotifier geladen met handmatige booleans zoals isLoading/error. Riverpod vervangt dit allemaal door AsyncValue en een FutureProvider — laad- en foutstatussen worden voor je gemodelleerd.

De gebruikersinterface gebruikt vervolgens AsyncValue.when om gegevens, laden en fouten volledig af te handelen.

final userProvider = FutureProvider<User>((ref) async {
  final repo = ref.watch(userRepositoryProvider);
  return repo.fetchCurrentUser();
});

// In a ConsumerWidget:
final asyncUser = ref.watch(userProvider);
return asyncUser.when(
  data: (user) => Text(user.name),
  loading: () => const CircularProgressIndicator(),
  error: (e, st) => Text('Error: $e'),
);

Stap 5 — Providers combineren (geen ProxyProvider meer)

In het oude pakket vereiste het afhankelijk maken van het ene model van het andere een ProxyProvider en een zorgvuldige volgorde. In Riverpod roept een provider eenvoudig ref.watch aan op een andere provider in de eigen body. Afhankelijkheden zijn expliciet, typeveilig en reactief.

Hieronder wordt een afgeleide provider automatisch opnieuw berekend wanneer cartProvider verandert — zonder handmatig abonnement en zonder ketens van notifyListeners.

final cartProvider =
    NotifierProvider<CartNotifier, List<Item>>(CartNotifier.new);

// Derived state — recomputes when the cart changes
final cartTotalProvider = Provider<double>((ref) {
  final items = ref.watch(cartProvider);
  return items.fold(0.0, (sum, item) => sum + item.price);
});

Een zelfstandig voorbeeld van reactieve afleiding in Dart

Je hebt Flutter niet nodig om het kernidee van Riverpod te begrijpen: afgeleide waarden worden opnieuw berekend op basis van hun invoer. Dit fragment in puur Dart modelleert dezelfde winkelmandberekening met fold als in cartTotalProvider, zodat je de berekening kunt uitvoeren en controleren zoals een online beoordelaar dat zou doen.

class Item {
  final String name;
  final double price;
  Item(this.name, this.price);
}

double cartTotal(List<Item> items) =>
    items.fold(0.0, (sum, item) => sum + item.price);

void main() {
  final cart = [
    Item('Coffee', 3.50),
    Item('Bagel', 2.25),
    Item('Juice', 4.00),
  ];
  print('Items: ${cart.length}');
  print('Total: \$${cartTotal(cart).toStringAsFixed(2)}');
}

Stap 6 — Strategie voor stapsgewijze migratie

Je herschrijft zelden een hele app in één keer. Een veilig, stapsgewijs plan:

  • Eenmalig omwikkelen: voeg meteen ProviderScope toe aan de basis — dit kan naast het oude pakket provider bestaan.
  • Begin bij de bladeren: migreer zelfstandige functies (instellingen, thema, tellers) vóór functies met veel onderlinge koppelingen.
  • Gebruik zo nodig een brug: een Riverpod-provider kan verouderde gegevens uitlezen en een verouderde widget kan blijven bestaan totdat het bijbehorende scherm is omgezet.
  • Widgets omzetten: wijzig StatelessWidget→ConsumerWidget en StatefulWidget→ConsumerStatefulWidget wanneer je elk scherm aanpast.
  • Verwijder de oude ChangeNotifierProvider en de afhankelijkheid provider pas wanneer niets er meer naar verwijst.

Korte controle — watch tegenover read

Test je begrip van het meest foutgevoelige onderdeel van de migratie.

Samenvatting — van Provider naar Riverpod

Je hebt een verouderde functie met ChangeNotifier + Provider gemigreerd naar Riverpod 2.0:

  • De app in één ProviderScope gewikkeld in plaats van geneste provider-widgets.
  • ChangeNotifier omgezet naar een Notifier met een state-veld en zonder notifyListeners().
  • context.watch/read vervangen door ref.watch (weergave) en ref.read(provider.notifier) (acties) binnen een ConsumerWidget.
  • Asynchrone logica gemodelleerd met FutureProvider + AsyncValue.when, en providers gecombineerd via ref.watch in plaats van ProxyProvider.
  • Een leaf-first, incrementele aanpak gevolgd, zodat beide systemen tijdens de overgang naast elkaar kunnen bestaan.

Het resultaat: een provider-grafiek die compileerveilig is, waarbij een ontbrekende afhankelijkheid een buildfout veroorzaakt in plaats van een crash in productie.

Gratis beginnen

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 “Van Provider naar Riverpod: legacy-state migreren” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad Mobiele ontwikkeling met Flutter, waaronder “Van Provider naar Riverpod: legacy-state migreren”, 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 “Van Provider naar Riverpod: legacy-state migreren”?

Zet bestaande ChangeNotifier- en Provider-code om naar Riverpod's compile-veilige providergrafiek. 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 1 van 4.

Hoe lang duurt de les “Van Provider naar Riverpod: legacy-state migreren”?

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

  1. Van Provider naar Riverpod: legacy-state migreren
  2. Codegeneratie met riverpod_generator en @riverpod
  3. AsyncNotifier- en FutureProvider-gegevenspijplijnen
  4. Providers scopen, overschrijven en ProviderObserver
← Terug naar Mobiele ontwikkeling met Flutter