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

اختيار مكان وجود الحالة

جزء من قسم إدارة الحالة في رحلة Flutter على Coddy. الدرس 21 من 27.

يتحرك مفتاح الوضع الداكن في شاشة الإعدادات، لكن لا شيء يصبح داكنًا. يحتفظ بقيمته باستخدام setState، ولا يستطيع رؤيتها سوى المفتاح. يُضبط المظهر في مكان بعيد، داخل MyApp. هذه القيمة تخص التطبيق بأكمله.

كل قيمة في التطبيق تحتاج إلى مكانها الخاص. اسأل من يستخدمها:

  • عنصر واجهة مستخدم واحد (نص حقل، لوحة مفتوحة، فهرس علامة تبويب): استخدم setState في ذلك العنصر.
  • بضعة عناصر واجهة مستخدم متقاربة (زر والتسمية المجاورة له): ارفعها إلى العنصر الأب ومرّرها إلى الأسفل.
  • عناصر واجهة مستخدم كثيرة أو عدة شاشات (المستخدم، عربة التسوق، الإعدادات): استخدم نموذجًا مشتركًا مع Provider أو Riverpod.

Provider أم Riverpod؟ كلاهما يؤدي الغرض. Provider أبسط قليلًا ويضع النماذج في شجرة عناصر واجهة المستخدم، حيث يمكن العثور عليها عبر context. أما Riverpod فيُبقي الموفّرات خارج الشجرة، لذا لا تحتاج القيمة إلى context، ويمكن للموفّرات مراقبة بعضها بعضًا. اختر أحدهما للتطبيق واستخدمه في كل مكان.

هنا يلائمنا StateProvider: قيمة bool واحدة، يقرأها MyApp لاختيار المظهر، ويغيّرها المفتاح:

final darkModeProvider = StateProvider((ref) => false);

// في MyApp، أصبح الآن ConsumerWidget:
themeMode: ref.watch(darkModeProvider) ? ThemeMode.dark : ThemeMode.light,
challenge icon

التحدي

صعب

اجعل مفتاح الوضع الداكن يغيّر التطبيق بأكمله:

  • أضف final darkModeProvider = StateProvider((ref) => false); في الأعلى.
  • حوّل MyApp إلى ConsumerWidget بحيث يتبع themeMode المزوّد.
  • حوّل SettingsScreen إلى ConsumerWidget: يعرض المفتاح قيمة المزوّد ويضبطها عند تغيّرها.

جرّب بنفسك

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      themeMode: ThemeMode.light,
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Notes'),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const SettingsScreen()),
              );
            },
          ),
        ],
      ),
      body: const Center(child: Text('No notes yet')),
    );
  }
}

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool dark = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Settings')),
      body: ListView(
        children: [
          SwitchListTile(
            title: const Text('Dark mode'),
            value: dark,
            onChanged: (value) {
              setState(() {
                dark = value;
              });
            },
          ),
        ],
      ),
    );
  }
}
quiz iconاختبر نفسك

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

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

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