Riverpod: Notifier
Part of the State Management section of Coddy's Flutter journey. Lesson 19 of 27.
A StateProvider is fine for one number. When the state comes with rules, such as a list with methods to add and remove, write a Notifier: a class that holds the state and the methods that change it.
class GuestList extends Notifier<List<String>> {
@override
List<String> build() => [];
void add(String name) {
state = [...state, name];
}
}
final guestsProvider =
NotifierProvider<GuestList, List<String>>(GuestList.new);build returns the first state, an empty list. Inside the class, state is the current value. GuestList.new is the class's constructor, handed to the provider so it can create the notifier.
The key rule: give state a new value, never change the old one. Riverpod tells the widgets only when state is set to a different object. state.add(name) changes the list in place, so state is still the same list and nobody hears about it. [...state, name] builds a new list: the three dots spread every old name into it, then the new one goes at the end.
A widget calls a method through the notifier: ref.read(guestsProvider.notifier).add('Ada'). This page is a ConsumerStatefulWidget, the stateful kind, because it also keeps a text controller. Its State has ref as a field.
Challenge
HardThe guest list shows the names in guestsProvider, but Add only clears the field.
- Add a method
add(String name)toGuestListthat setsstateto a new list with the name at the end. - Make
Addcall it with the field's text, before it clears the field.
Try it yourself
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class GuestList extends Notifier<List<String>> {
@override
List<String> build() => [];
}
final guestsProvider = NotifierProvider<GuestList, List<String>>(GuestList.new);
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: PartyPage(),
);
}
}
class PartyPage extends ConsumerStatefulWidget {
const PartyPage({super.key});
@override
ConsumerState<PartyPage> createState() => _PartyPageState();
}
class _PartyPageState extends ConsumerState<PartyPage> {
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
final guests = ref.watch(guestsProvider);
return Scaffold(
appBar: AppBar(title: Text('Guests: ${guests.length}')),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: TextField(
controller: controller,
decoration: const InputDecoration(labelText: 'Guest name'),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () {
controller.clear();
},
child: const Text('Add'),
),
],
),
),
Expanded(
child: ListView(
children: [
for (final name in guests) ListTile(leading: const Icon(Icons.person), title: Text(name)),
],
),
),
],
),
);
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in State Management
Practice on your own: Online Flutter compiler