When several widgets need the same state, they should not each maintain separate copies. Riverpod provides values and services through providers. A Notifier owns mutable state here. ref.watch observes it, while ref.read triggers an action. This makes the direction visible: the interface reads state and calls a named action.
01Add Riverpod
Run flutter pub add flutter_riverpod:3.3.1 in the learning project. ProviderScope wraps the application. ConsumerWidget supplies its build method with a WidgetRef for reading providers.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = NotifierProvider<EntryCounter, int>(EntryCounter.new);
class EntryCounter extends Notifier<int> {
@override
int build() => 0;
void add() => state++;
}
void main() =>
runApp(const ProviderScope(child: MaterialApp(home: CounterScreen())));
class CounterScreen extends ConsumerWidget {
const CounterScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) => Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Entries: ${ref.watch(counterProvider)}'),
FilledButton(
onPressed: () => ref.read(counterProvider.notifier).add(),
child: const Text('Add entry'),
),
],
),
),
);
}
Run / check
flutter analyze
flutter run -d chrome02Define a state owner
EntryCounter extends Notifier<int>. build supplies the initial value. add is the named action. The provider creates the notifier. The interface therefore owns no separate storage rule.
03Distinguish watch from read
ref.watch(counterProvider) causes a new build when values change. ref.read(counterProvider.notifier).add() calls the action without establishing a new subscription inside the callback. Vehicle File similarly wires repositories and services.
04Check the ownership boundary
Add a second text using ref.watch on the same provider. Both display the same value. Two different providers would own separate states instead. Hot restart resets our in-memory state.
Every click increments the same observed counter.
Your exercise
Add reset to the notifier and a button that calls it.
Show the solution and explanation
void reset() => state = 0 keeps the change in the notifier. The button uses ref.read(counterProvider.notifier).reset(). The interface continues observing the same provider.
Project context and sources
The example is simplified for learning. The linked source version shows its use in the project and the checks applied there.