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 فهو مُنشئ الصنف، ويُمرَّر إلى الموفّر ليتمكّن من إنشاء المُعلِم.
القاعدة الأساسية: أعطِ state قيمة جديدة، ولا تغيّر القيمة القديمة أبدًا. يُخبر Riverpod عناصر الواجهة فقط عندما تُسنَد إلى state قيمة لكائن مختلف. تغيّر state.add(name) القائمة في مكانها، لذا تظل state هي القائمة نفسها ولا يعلم أحد بذلك. تنشئ [...state, name] قائمة جديدة: تنشر النقاط الثلاث كل اسم قديم فيها، ثم يُضاف الاسم الجديد في النهاية.
يستدعي عنصر واجهة مستخدم طريقةً عبر المُعلِم: ref.read(guestsProvider.notifier).add('Ada'). هذه الصفحة من النوع ConsumerStatefulWidget، وهو النوع ذو الحالة، لأنها تحتفظ أيضًا بوحدة تحكم نصية. يحتوي State الخاص بها على ref كحقل.
التحدي
صعبتعرض قائمة الضيوف الأسماء في guestsProvider، لكن Add لا يفعل سوى مسح الحقل.
- أضف طريقة
add(String name)إلىGuestListلتعيين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 عبر الإنترنت