اختيار مكان وجود الحالة
جزء من قسم إدارة الحالة في رحلة 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,التحدي
صعباجعل مفتاح الوضع الداكن يغيّر التطبيق بأكمله:
- أضف
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;
});
},
),
],
),
);
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس إدارة الحالة
1رفع الحالة إلى المكوّن الأب
تمرير الحالة إلى مكوّن فرعيتمرير دالة رد نداء إلى مكوّن فرعيدوال رد النداء التي ترسل قيمةرفع الحالة إلى المكوّن الأب4Riverpod
Riverpod: ProviderScopeConsumerWidget و ref.watchRiverpod: StateProviderRiverpod: Notifierالمزوّدات التي تراقب المزوّداتاختيار مكان وجود الحالةتدرّب بنفسك: مترجم Flutter عبر الإنترنت