في هذا الدرس سنتعلم Riverpod وطريقة التعامل معه في ادارة حالة البيانات في التطبيق، هذ الدرس لن يكون للمقارنة بين مكتبات ادارة حالة التطبيق، شرحت سابقا عن Bloc والان سكون الشرح باذن الله عن Riverpod.
عند زيارتك الصفحة الخاصة بريفربود ستجد هذه الرسالة "Provider, but different" اذا كنت لم تتعامل مع Provider من قبل فهو ببساطة مكتبة لادارة بيانات التطبيق تقوم بتعريف متغير "Global" يمكنك التواصل معه من اي مكان في التطبيق، لكن لا تخف من "Global" لانه سيكون "immutable". ولن يؤثر على اداء التطبيق باذن الله.
الموقع الخاص بالمكتبة يقدم لكن انواع عديده من الباكجز ولكن سنقوم باختيار Riverpod بدون Hook واذا كان لديك معرفه سابقة في Hook فالاختلاف باذن الله لن يكون كبير،.
تهيئة المشروع:
على افتراض انك انشئت مشروع جديد قم باضافة التالي الى pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^0.12.4
الان بعد تحمل المكتبة سنقوم باضافة التطبيق داخل ProviderScope حتى نستيطع قراءة Provider وايضا هي المكان المخصص لحفظ البيانات. او حالة التطبيق.
void main() {
runApp(ProviderScope(child: MyApp()));
}
الان اصبح المشروع جاهز للتعامل مع Riverpod، سنقوم باستعراض جميع المفاهيم المتعلقة Riverpod كل واحدة على حدة. وهم بالترتيب.
- Provider
- State
- Future
Provider
Provider ياتي باشكال مختلفة ولكن سنقوم بالتعرف عل الشكل العام له، مع مثال بسيط يعرض نص اهلا بالعالم، بداية سنقوم بانشاء Provider كـGlobal وطريقة تعريفة مثل اي دالة.
final myProvider = Provider((ref) => "اهلا بالعالم");
myProvider: متغيرfinal، هذا المتغير الذي سنقوم بالتعامل معه لمعرفة الحالة لديه، مع ملاحظة ان هذا الملاحظ غير قابل للتغير.Provider: ابسط نوع من انواعprovider، يحمل قيمة لايمكن ان تتغير.- دالة تنشئ الحالة المشتركة : دائما هذه الدالة تحمل باريمتر
refالذي يسمح لنا بقراء حالة المزودين الاخرين، سنقوم بملاحظة هذه الوظيفة لاحقا في الدرس.
قراءة الـProvider :
سنقوم بانشاء Widget ونقرأ البيانات من Provider،
class HelloWorld extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
String helloWorld = watch<String>(myProvider);
return Scaffold(
appBar: AppBar(),
body: Center(child: Text(helloWorld)),
);
}
}
- تلاحظ ان
Widgetوريث منConsumerWidgetوهو يعتبرStatelessWidgetبحيث يسمح لنا بمراقيةProviderوارجاع البيانات. - ايضا دالة
buildاضيف لها باريمترScopedReaderباسمwatchيراقب الـProviderويقوم باعادة بناء الواجهة عند الحاجة. - بعد ذلك عرفنا متغير نصي
helloWorldيستخدمwatchوداخلةmyProviderالذي قمنا بتعريفه سابقاً. - واخير وضعنا المتغير النصي في
Textلعرضه على الشاشة.
الان تعلمنا مهوم Provider لكن لو اردنا تغيير البيانات ?.
استخدام State Provider والجمع بين الـProviders:
في كثير من الحالات تحتاج لحعل Providers يعتمدون على قيمة بعض، مثلا لو كان لديك قائمة بالمدن وتريد عرض مثلا حالة الطقس للمدينة المختارة، فانت تحتاج الى Provider لعرض قائمة المدن مثلا وProvider آخر لجلب بيانات المدينة المختارة. في هذه الحالة سنقوم بعملية جمع للاثنين كما ستلاحظ الان.
في المثال التالي بانشاء زرين كل واحد يقوم بتوليد قيمة مختلفة والـProvider الاخر يحصل عليها ويعيد لنا قيمة أخرى.
final selectThemeProvider = StateProvider<String>((ref) {
return '';
});
final changeThemeProvider = Provider<bool>((ref) {
final selectTheme = ref.watch(selectThemeProvider);
return selectTheme.state == 'dark';
});
انشئنا StateProvider حتى نقوم بتغيير الحالة له واضفنا له قيمة نصية فارغة، ثم قمنا بتعريف changeThemeProvider الذي سيقوم بدورة بمراقية state للـProvider السابق وبناء على الحالة سيعيد قيمة True او false. الان سنقوم بانشاء Widget للتعامل معهم.
class SelectThemeWidget extends ConsumerWidget {
@override
// 1
Widget build(BuildContext context, ScopedReader watch) {
// 2
final bool isDark = watch<bool>(changeThemeProvider);
return Scaffold(
appBar: AppBar(
title: Text('Providers'),
),
body: Container(
// 3
color: isDark ? Colors.black : Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ElevatedButton(
onPressed: () {
// 4
context.read(selectThemeProvider).state = 'light';
},
child: Text("وضع النهار")),
ElevatedButton(
onPressed: () {
// 5
context.read(selectThemeProvider).state = 'dark';
},
child: Text("وضع الليل"))
],
)),
);
}
}
- دالة
buildاضفنا لهاScopedReaderلمتابعة الـProvider. - انشئنا متغير
boolحتى نقوم بمعرفة المتغير اذا كان وضع الليلي او النهار. - بناء على قيمة المتغير نقوم بتغيير خلفية الـ
Containerاسود او ابيض. - استخدمنا
contextلقراءةproviderوتغيير القيمة له. - نفس المذكور في النقطة لرابعة.
الان سنقوم بالتعامل مع المثال الشائع لادارة حالة التطبيق وهو Counter App سنقوم باخذ طريقتين للتعامل معه ونوضح الفرق بين الطريقتين.
الفرق بين State Provider و Change Notifier Provider:
يعتبر من اهم المميزات لتغيير القيم للتطبيق، يوجد طريقتين لتغيير الحالة للتطبيق، ،سنقوم بشرحها جميعاً باستخدام المثال البسيط وهو مثال العداد، في الكود التالي ستلاحظ يوجد ترقيم للاكواد وستجد شرحها في بعد الكود مباشرة،
final counterProvider = StateProvider((ref) => 0);
تلاحظ انشأنا StateProvider وهذا يعني ان القيمة ستقوم بالتغير ووضعنا القيمة المبدئية 0، بعد ذلك سنقوم بانشاء Widget للتعامل مع الحالة وبنفس الطريقة سيكون الـWidget وريث من ConsumerWidget.
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
// 1
int counter = watch(counterProvider).state;
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
// 2
context.read(counterProvider).state++;
},
),
body: Center(child: Text("$counter")));
}
}
- مشاهدة الحالة من
counterProvider. - تغيير قيمة الحالة باستخدام
++.
لكن ماذا لو كان التطبيق كبير، هل ستقوم بالتعامل بنفس الطريقة؟ ? بالطبع لا ستقوم بالتأكيد بجعل تطبيقك اكثر قابلية للفهم والتطوير والصيانة وذلك عن طريق استخدام ChangeNotifier سنقوم باستخام نفس الــprovider ولكن مع بعض التغييرات، في البداية سنقوم بانشاء كائن ChangeNotifier.
class CounterNotifier extends ChangeNotifier {
int count;
CounterNotifier([this.count = 0]);
void increment() {
count++;
notifyListeners();
}
}
الان سنقوم بتغيير على كود counterProvider السابق كتابتها لتكون بهذ الشكل:
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
// 1
int counter = watch(counterProvider).count;
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
// 2
context.read(counterProvider).increment();
},
),
body: Center(child: Text("$counter")));
}
}
- قراءة قيمة
count. - استخدمنا الدالة المضافة في
CounterNotifierلزيادة الرقم.
هذا الكود يعطيك نفس نتيجة الكود السابق.
في الدرس القادم بإذن الله سنتعلم
- FutureProvider
- StateProvider
لا تنسى تقييم الدرس











