Menu
CoddyTech
flag Ar iconالعربيةdown icon

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 كحقل.

challenge icon

التحدي

صعب

تعرض قائمة الضيوف الأسماء في 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)),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس إدارة الحالة

تدرّب بنفسك: مترجم Flutter عبر الإنترنت