Von Provider zu Riverpod: Migration eines Legacy-States
Überführen Sie bestehenden ChangeNotifier- und Provider-Code in Riverpods beim Kompilieren sicheren Provider-Graphen.
Von Provider zu Riverpod: Migration eines Legacy-States ist eine kostenlose Flutter Mobile Development-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Flutter Mobile Development-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Flutter Mobile Development-Kurs umfasst insgesamt 4 Lektionen.
Teile dieser Lektion wurden noch nicht übersetzt und werden auf Englisch angezeigt.
Why Migrate to Riverpod?
The classic provider package solved dependency injection in Flutter, but it has real weaknesses you have probably hit in production:
- Runtime crashes — calling
context.read<T>()for a type that was never provided throws aProviderNotFoundExceptionat runtime, not compile time. - BuildContext coupling — you can only read providers where you have a
BuildContext. - No combining — depending on one
ChangeNotifierfrom another is awkward and error-prone.
Riverpod is a rewrite by the same author. Providers are top-level globals that the compiler can verify, so a missing dependency becomes a compile error. This lesson walks you through migrating a legacy ChangeNotifier + Provider app to Riverpod 2.0, piece by piece.
The Legacy Code We Are Migrating
Here is a typical legacy counter feature using ChangeNotifier. It exposes a value and a method that calls notifyListeners(). This is the pattern we will convert step by step.
Notice that the state (_count) and the mutation logic live together inside a class that extends ChangeNotifier.
// 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();
}
}How the Legacy Model Was Wired Up
In the old setup you register the model with ChangeNotifierProvider high in the widget tree, then read it via context.watch / context.read. The problem: if you forget to register it, the app compiles fine and crashes only when that screen opens.
// LEGACY wiring
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const MyApp(),
),
);
}
// In a widget:
final count = context.watch<CounterModel>().count;
context.read<CounterModel>().increment();Step 1 — Install Riverpod and Add ProviderScope
Add flutter_riverpod to pubspec.yaml. The single most important wiring change is to wrap your app in a ProviderScope. This is the container that stores the state of every provider — it replaces the nest of ChangeNotifierProvider widgets at the root.
- Old: many provider widgets wrapping
MyApp. - New: one
ProviderScope. Providers themselves are declared as globals, not in the tree.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(
const ProviderScope(
child: MyApp(),
),
);
}Step 2 — ChangeNotifier becomes Notifier
Riverpod 2.0 introduces Notifier as the modern, type-safe replacement for ChangeNotifier. The differences:
- You hold state in a single
statefield instead of private fields + getters. - You never call
notifyListeners()— reassigningstaterebuilds listeners automatically. - The
build()method returns the initial state.
Here is the migrated counter as a 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,
);Step 3 — Read State in the UI with ConsumerWidget
Replace context.watch/context.read with a WidgetRef. The cleanest path is to make your widget a ConsumerWidget, which adds a ref parameter to build.
ref.watch(provider)— subscribe and rebuild on change (use inbuild).ref.read(provider.notifier)— get the notifier to call methods (use 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 vs read — The Migration Trap
The most common migration bug is using the wrong access method. Map the old API to the new one carefully:
context.watch<T>()→ref.watch(provider)(rebuilds the UI)context.read<T>()in a callback →ref.read(provider.notifier)(no rebuild)
Rule: never call ref.watch inside an onPressed callback — it will not behave as expected and can cause unnecessary rebuilds. Use ref.read for one-off actions, ref.watch for values displayed in the UI.
Step 4 — Migrating Async State (FutureProvider)
Legacy apps often load data inside a ChangeNotifier with manual isLoading/error booleans. Riverpod replaces all of that with AsyncValue and a FutureProvider — loading and error states are modeled for you.
The UI then uses AsyncValue.when to render data, loading, and error branches exhaustively.
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'),
);Step 5 — Combining Providers (No More ProxyProvider)
In the old package, making one model depend on another required ProxyProvider and careful ordering. In Riverpod, a provider simply calls ref.watch on another provider inside its body. Dependencies are explicit, type-safe, and reactive.
Below, a derived provider recomputes automatically whenever cartProvider changes — no manual subscription, no notifyListeners chains.
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);
});A Standalone Taste of Reactive Derivation in Dart
You do not need Flutter to understand Riverpod's core idea: derived values recompute from their inputs. This pure-Dart snippet models the same fold-the-cart logic used in cartTotalProvider, so you can run and verify the math an online judge would compute.
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)}');
}Step 6 — Incremental Migration Strategy
You rarely rewrite a whole app at once. A safe, incremental plan:
- Wrap once: add
ProviderScopeat the root immediately — it coexists with the oldproviderpackage. - Leaf-first: migrate self-contained features (settings, theme, counters) before tangled ones.
- Bridge if needed: a Riverpod provider can read legacy data, and a legacy widget can stay until its screen is converted.
- Convert widgets: change
StatelessWidget→ConsumerWidgetandStatefulWidget→ConsumerStatefulWidgetas you touch each screen. - Delete the old
ChangeNotifierProviderand theproviderdependency only when nothing references them.
Quick Check — watch vs read
Test your understanding of the most error-prone part of the migration.
Recap — From Provider to Riverpod
You migrated a legacy ChangeNotifier + Provider feature to Riverpod 2.0:
- Wrapped the app in a single ProviderScope instead of nested provider widgets.
- Turned
ChangeNotifierinto a Notifier with astatefield and nonotifyListeners(). - Swapped
context.watch/readforref.watch(display) andref.read(provider.notifier)(actions) inside a ConsumerWidget. - Modeled async with FutureProvider +
AsyncValue.when, and combined providers viaref.watchinstead ofProxyProvider. - Followed a leaf-first, incremental path so the two systems coexist during the transition.
The payoff: a compile-safe provider graph where a missing dependency is a build error, not a production crash.
Häufig gestellte Fragen
Ist die Lektion „Von Provider zu Riverpod: Migration eines Legacy-States“ kostenlos?
Ja — der vollständige Text von „Von Provider zu Riverpod: Migration eines Legacy-States“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Flutter Mobile Development-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Flutter Mobile Development-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Von Provider zu Riverpod: Migration eines Legacy-States“?
Überführen Sie bestehenden ChangeNotifier- und Provider-Code in Riverpods beim Kompilieren sicheren Provider-Graphen. Du übst Flutter Mobile Development mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Flutter Mobile Development zu starten?
Keine Vorkenntnisse erforderlich. Flutter Mobile Development auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.
Wie lange dauert die Lektion „Von Provider zu Riverpod: Migration eines Legacy-States“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Flutter Mobile Development-Lektion Code schreiben und ausführen?
Ja. Jede Flutter Mobile Development-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Von Provider zu Riverpod: Migration eines Legacy-States
- Codegenerierung mit riverpod_generator und @riverpod
- Daten-Pipelines mit AsyncNotifier und FutureProvider
- Provider-Scoping, Überschreibungen und ProviderObserver