Riverpod: Notifier
Часть раздела Управление состоянием путешествия по Flutter на Coddy. Урок 19 из 27.
StateProvider подходит для одного числа. Если состояние подчиняется правилам, например это список с методами добавления и удаления элементов, напиши Notifier: класс, который хранит состояние и методы, изменяющие его.
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 возвращает начальное состояние — пустой список. Внутри класса state — это текущее значение. GuestList.new — конструктор класса, передаваемый провайдеру, чтобы он мог создать notifier.
Главное правило: присваивай state новое значение и никогда не изменяй старое. Riverpod уведомляет виджеты только тогда, когда state присваивается другой объект. state.add(name) изменяет список на месте, поэтому state по-прежнему указывает на тот же список, и никто об этом не узнаёт. [...state, name] создаёт новый список: три точки добавляют в него все прежние имена, а затем в конец добавляется новое.
Виджет вызывает метод через notifier: ref.read(guestsProvider.notifier).add('Ada'). Эта страница — ConsumerStatefulWidget, виджет с состоянием, потому что он также хранит текстовый контроллер. У его State есть поле ref.
Задание
СложноВ списке гостей отображаются имена из guestsProvider, но кнопка Add только очищает поле.
- Добавь в
GuestListметодadd(String name), который присваиваетstateновый список с именем в конце. - Сделай так, чтобы кнопка
Addвызывала его с текстом из поля до очистки поля.
Попробуйте сами
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)),
],
),
),
],
),
);
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Управление состоянием
Потренируйтесь самостоятельно: Онлайн-компилятор Flutter