كتابة طلب على الرفرفة بالاشتراك مع Redux

صورة


مرحبا بالجميع! في هذه المقالة ، أود أن أوضح لك كيفية إنشاء تطبيق Flutter باستخدام Redux. إذا كنت لا تعرف ماهية Flutter ، فسيكون هذا بمثابة SDK مفتوح المصدر لإنشاء تطبيقات الجوال من Google. يتم استخدامه لتطوير تطبيقات Android و iOS ، وهو أيضًا الطريقة الوحيدة لتطوير تطبيقات Google Fuchsia.

إذا كنت معتادًا على Flutter وترغب في إنشاء تطبيق مصمم جيدًا وسهل الاختبار ولديه سلوك يمكن التنبؤ به للغاية ، تابع قراءة هذه المقالة وستكتشف قريبًا!

ولكن قبل أن نبدأ في كتابة التطبيق نفسه. دعنا نتعرف قليلاً على النظرية ، لنبدأ بشرح ماهية Redux.

ما هو الإعادة؟


Redux هي بنية تم إنشاؤها أصلاً للغة JavaScript وتستخدم في التطبيقات التي يتم إنشاؤها باستخدام أطر تفاعلية (مثل React Native أو Flutter). Redux هي نسخة مبسطة من بنية Flux التي أنشأها Facebook. في الأساس ، تحتاج إلى معرفة ثلاثة أشياء:

  1. المصدر الوحيد للحقيقة - يتم تخزين حالة التطبيق بالكامل في مكان واحد فقط (يُسمى store ).
  2. الحالة للقراءة فقط / الحالة للقراءة فقط - لتغيير حالة التطبيق ، تحتاج إلى إرسال إجراءات (إجراء) ، وبعد ذلك سيتم إنشاء حالة جديدة
  3. يتم إجراء تغييرات باستخدام وظائف خالصة / وظائف خالصة - وظيفة خالصة (للبساطة ، إنها وظيفة بدون آثار جانبية) تأخذ تطبيق الحالة الحالي والإجراء ويعيد الحالة الجديدة للتطبيق

ملاحظة: يتمثل أحد الآثار الجانبية للوظيفة في القدرة ، في عملية إجراء العمليات الحسابية الخاصة بها: على قراءة وتعديل قيم المتغيرات العامة ، وتنفيذ عمليات الإدخال / الإخراج ، والاستجابة للمواقف الاستثنائية ، والاتصال بمعالجاتها. إذا قمت باستدعاء دالة ذات تأثير جانبي مرتين مع نفس مجموعة قيم وسيطات الإدخال ، فقد يحدث أن يتم إرجاع قيم مختلفة كنتيجة. وتسمى هذه الوظائف الدوال غير الحتمية ذات الآثار الجانبية.

تبدو رائعة ، ولكن ما هي فوائد هذا الحل؟

  • لدينا سيطرة على الولاية / الولاية - وهذا يعني أننا نعرف بالضبط سبب تغيير الحالة ، وليس لدينا حالة مكررة ، ويمكننا بسهولة مراقبة تدفق البيانات
  • المخفض هو وظائف خالصة يسهل اختبارها - يمكننا تمرير الحالة ، والإجراء إلى المدخلات ، والتحقق مما إذا كانت النتيجة صحيحة
  • التطبيق منظم بشكل واضح - لدينا طبقات مختلفة للإجراءات ، والنماذج ، ومنطق الأعمال ، وما إلى ذلك - حتى تعرف بالضبط أين تضيف ميزة جديدة أخرى
  • إنها بنية رائعة للتطبيقات الأكثر تعقيدًا - ليس عليك تمرير الحالة عبر شجرة العرض بالكامل من الأب إلى الطفل
  • وهناك واحد آخر ...

استرجاع وقت السفر


Redux لديه فرصة واحدة مثيرة للاهتمام - السفر عبر الزمن! باستخدام Redux والأدوات ذات الصلة ، يمكنك تتبع حالة التطبيق الخاص بك بمرور الوقت ، والتحقق من الحالة الفعلية وإعادة إنشائها في أي وقت. انظر هذه الميزة في العمل:

صورة

استعادة الحاجيات مع مثال بسيط


جميع القواعد المذكورة أعلاه تجعل تدفق البيانات في Redux أحادي الاتجاه. لكن ماذا يعني ذلك؟ في الممارسة العملية ، يتم كل هذا باستخدام الإجراءات ، ومخفضات ، وتخزين الدول . دعنا نتخيل تطبيقًا يعرض عدادًا:

صورة

  1. يحتوي التطبيق الخاص بك على حالة معينة عند بدء التشغيل (عدد النقرات ، وهو 0)
  2. بناءً على هذه الحالة ، يتم عرض المشاهدة .
  3. إذا نقر المستخدم على الزر ، فسيتم إرسال الإجراء (على سبيل المثال ، IncrementCounter)
  4. بعد ذلك ، يتلقى الإجراء مخفضًا يعرف الحالة السابقة (العداد 0) ، ويستقبل الإجراء (IncrementCounter) ويمكنه إرجاع حالة جديدة (العداد الآن 1)
  5. تطبيقنا لديه حالة جديدة (العداد هو 1)
  6. بناءً على الحالة الجديدة ، يتم إعادة رسم العرض ، والذي يعرض الحالة الحالية

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

صورة

دعونا ننشئ تطبيقًا صغيرًا ونتعرف على تنفيذ منهج Redux أثناء العمل ، وسيتم تسمية التطبيق باسم " قائمة التسوق "

سوف نرى كيف يعمل Redux في الممارسة العملية. سنقوم بإنشاء تطبيق ShoppingCart بسيط. سيكون للتطبيق ميزات مثل:

  • مضيفا المشتريات
  • سيكون من الممكن وضع علامة على الشراء عند اكتماله
  • وهذا كل شيء في الأساس

سيبدو التطبيق كالتالي:

صورة

لنبدأ كتابة التعليمات البرمجية!

مطلب


في هذه المقالة ، لن أعرض إنشاء واجهة المستخدم لهذا التطبيق. يمكنك التعرف على الكود الذي أعددته لك قبل متابعة الغوص Redux . بعد ذلك سنستمر في كتابة التعليمات البرمجية وإضافة Redux إلى التطبيق الحالي.

ملاحظة: إذا لم تستخدم Flutter من قبل ، أقترح عليك تجربة Flutter Codelabs من Google .

التحضير الأولي


لبدء استخدام Redux for Flutter ، نحتاج إلى إضافة تبعيات إلى ملف pubspec.yaml :

flutter_redux: ^0.5.2 

يمكنك أيضًا التحقق من الإصدار الحالي من هذه التبعية بالانتقال إلى صفحة flutter_redux .

في وقت كتابة هذا التقرير ، كان الإصدار ، flutter_redux 0.6.0

نموذج


يجب أن يكون تطبيقنا قادراً على التحكم في إضافة وتعديل العناصر ، لذلك سنستخدم نموذج CartItem بسيط لتخزين حالة عنصر واحد. كل حالة التطبيق الخاصة بنا ستكون مجرد قائمة بـ CartItems. كما ترون ، CartItem هو مجرد كائن.

 class CartItem { String name; bool checked; CartItem(this.name, this.checked); } 

أولا ، نحن بحاجة إلى إعلان الإجراءات. الإجراء هو ، في الواقع ، أي نية يمكن التذرع بها لتغيير حالة التطبيق. في جوهرها ، سيكون هناك إجراءان لإضافة وتغيير عنصر:

 class AddItemAction { final CartItem item; AddItemAction(this.item); } class ToggleItemStateAction { final CartItem item; ToggleItemStateAction(this.item); } 

ثم نحتاج إلى إخبار طلبنا بما يجب فعله بهذه الإجراءات . لهذا السبب يتم استخدام المخفضات - فهي تأخذ فقط الحالة الحالية للتطبيق والإجراء (حالة التطبيق والإجراء) ، ثم إنشاء حالة جديدة وإعادتها. سيكون لدينا طريقتين المخفض :

 List<CartItem> appReducers(List<CartItem> items, dynamic action) { if (action is AddItemAction) { return addItem(items, action); } else if (action is ToggleItemStateAction) { return toggleItemState(items, action); } return items; } List<CartItem> addItem(List<CartItem> items, AddItemAction action) { return List.from(items)..add(action.item); } List<CartItem> toggleItemState(List<CartItem> items, ToggleItemStateAction action) { return items.map((item) => item.name == action.item.name ? action.item : item).toList(); } 

الأسلوب appReducers () يفوض الإجراء إلى الأساليب المناسبة. كلا أساليب addItem () و toggleItemState () تُرجع قوائم جديدة - هذه هي حالتنا / حالتنا الجديدة. كما ترون ، لا يجب عليك تغيير القائمة الحالية . بدلاً من ذلك ، نقوم بإنشاء قائمة جديدة في كل مرة.

StoreProvider


الآن وبعد أن أصبح لدينا إجراءات ومخفضات ، نحتاج إلى توفير مكان لتخزين حالة التطبيق . في Redux ، يطلق عليه store وهو مصدر الحقيقة الوحيد للتطبيق.

 void main() { final store = new Store<List<CartItem>>( appReducers, initialState: new List()); runApp(new FlutterReduxApp(store)); } 

لإنشاء متجر ، نحتاج إلى اجتياز طريقة المخفضات والحالة الأولية. إذا أنشأنا متجرًا ، فيجب أن نمرره إلى StoreProvider لإخبار تطبيقنا بأنه يمكن استخدام المتجر من قبل أي شخص يريد طلب الحالة الحالية للتطبيق.

 class FlutterReduxApp extends StatelessWidget { final Store<List<CartItem>> store; FlutterReduxApp(this.store); @override Widget build(BuildContext context) { return new StoreProvider<List<CartItem>>( store: store, child: new ShoppingCartApp(), ); } } 

في المثال أعلاه ، يعتبر ShoppingCartApp ( ) الأداة الرئيسية لتطبيقنا.

StoreConnector


لدينا حاليا كل شيء ما عدا ... في الواقع إضافة وتغيير العناصر للشراء. كيف نفعل ذلك؟ لجعل هذا ممكنًا ، نحتاج إلى استخدام StoreConnector . هذه طريقة للحصول على المتجر وإرساله بعض الإجراءات أو مجرد الحصول على الحالة الحالية.

أولاً ، نريد الحصول على البيانات الحالية وعرضها كقائمة على الشاشة:

 class ShoppingList extends StatelessWidget { @override Widget build(BuildContext context) { return new StoreConnector<List<CartItem>, List<CartItem>>( converter: (store) => store.state, builder: (context, list) { return new ListView.builder( itemCount: list.length, itemBuilder: (context, position) => new ShoppingListItem(list[position])); }, ); } } 

يلتف التعليمة البرمجية أعلاه ListView.builder مع StoreConnector . يمكن لـ StoreConnector قبول الحالة الحالية (وهي قائمة بالعناصر ) وبمساعدة وظائف الخريطة ، يمكننا تحويلها إلى أي شيء. ولكن في حالتنا ، ستكون الحالة نفسها (قائمة) ، لأننا هنا نحتاج إلى قائمة تسوق.

بعد ذلك ، في وظيفة المنشئ ، نحصل على قائمة - والتي هي في الأساس قائمة CartItems من المتجر ، والتي يمكننا استخدامها لإنشاء ListView .

حسنا ، رائع - لدينا بيانات. الآن ، كيفية تعيين بعض البيانات؟

لهذا ، سنستخدم أيضًا StoreConnector ، ولكن بطريقة مختلفة قليلاً.

 class AddItemDialog extends StatelessWidget { @override Widget build(BuildContext context) { return new StoreConnector<List<CartItem>, OnItemAddedCallback>( converter: (store) { return (itemName) => store.dispatch(AddItemAction(CartItem(itemName, false))); }, builder: (context, callback) { return new AddItemDialogWidget(callback); }); } }typedef OnItemAddedCallback = Function(String itemName); 

لنلقِ نظرة على الكود استخدمنا StoreConnector ، كما في المثال السابق ، ولكن هذه المرة بدلاً من مطابقة قائمة CartItems مع نفس القائمة ، سنفعل تحويل خريطة إلى OnItemendedCallback . وبالتالي ، يمكننا تمرير وظيفة رد الاتصال إلى AddItemDialogWidget والاتصال بها عندما يضيف المستخدم عنصرًا جديدًا:

 class AddItemDialogWidgetState extends State<AddItemDialogWidget> { String itemName; final OnItemAddedCallback callback; AddItemDialogWidgetState(this.callback); @override Widget build(BuildContext context) { return new AlertDialog( ... actions: <Widget>[ ... new FlatButton( child: const Text('ADD'), onPressed: () { ... callback(itemName); }) ], ); } } 

الآن في كل مرة ينقر فيها المستخدم على زر ADD ، ترسل وظيفة رد الاتصال إجراء AddItemAction () .

الآن يمكننا أن نجعل تنفيذ مماثل للغاية لتغيير حالة عنصر.

 class ShoppingListItem extends StatelessWidget { final CartItem item; ShoppingListItem(this.item); @override Widget build(BuildContext context) { return new StoreConnector<List<CartItem>, OnStateChanged>( converter: (store) { return (item) => store.dispatch(ToggleItemStateAction(item)); }, builder: (context, callback) { return new ListTile( title: new Text(item.name), leading: new Checkbox( value: item.checked, onChanged: (bool newValue) { callback(CartItem(item.name, newValue)); }), ); }); } } 

كما في المثال السابق ، نستخدم StoreConnector لعرض قائمة لوظيفة رد الاتصال OnStateChanged . الآن في كل مرة تتغير فيها العلامة (في أسلوب onChanged) ، تقوم دالة رد الاتصال بإطلاق الحدث ToggleItemStateAction .

ملخص


هذا كل شئ! في هذه المقالة ، أنشأنا تطبيقًا بسيطًا يعرض قائمة تسوق ومغمور قليلاً في استخدام بنية Redux. في تطبيقنا ، يمكننا إضافة عناصر وتغيير حالتها. تعد إضافة ميزات جديدة إلى هذا التطبيق سهلة مثل إضافة إجراءات ومخفضات جديدة.

هنا يمكنك العثور على الكود المصدري لهذا التطبيق ، بما في ذلك تطبيق Time Travel :

آمل أن تستمتع هذا المنصب!

Source: https://habr.com/ru/post/ar481624/


All Articles