بنية الرفرفة: المزود مقابل BLoC
نشرت: 2020-04-17تخلق تطبيقات الكتابة باستخدام Flutter فرصًا رائعة لاختيار الهندسة المعمارية. كما هو الحال غالبًا ، أفضل إجابة على السؤال "أيهما يجب أن أختار؟" هو "هذا يعتمد". عندما تحصل على هذه الإجابة ، يمكنك التأكد من أنك وجدت خبيرًا في البرمجة.
في هذه المقالة ، سنتعرف على أكثر الشاشات شيوعًا في تطبيقات الأجهزة المحمولة وننفذها في أكثر معماريتين Flutter شهرة: Provider و BLoC . نتيجة لذلك ، سوف نتعرف على إيجابيات وسلبيات كل حل ، مما سيساعدنا في اختيار بنية Flutter المناسبة للوحدة أو التطبيق التالي.
مقدمة موجزة عن هندسة Flutter
يعد اختيار بنية مشروع تطوير Flutter ذا أهمية كبيرة ، ويرجع ذلك أساسًا إلى حقيقة أننا نتعامل مع نموذج برمجة تعريفية أقل استخدامًا. يغير هذا تمامًا نهج إدارة الحالة التي كان مطورو Android أو iOS على دراية بها ، ويكتبون الكود بشكل إلزامي. ليس من السهل الحصول على البيانات المتوفرة في مكان ما في التطبيق في مكان آخر. ليس لدينا إشارات مباشرة إلى وجهات النظر الأخرى في الشجرة ، والتي يمكننا من خلالها الحصول على وضعها الحالي.
ما هو المزود في Flutter
كما يوحي الاسم ، الموفر هو بنية Flutter التي توفر نموذج البيانات الحالي للمكان الذي نحتاجه فيه حاليًا. يحتوي على بعض البيانات ويخطر المراقبين عند حدوث تغيير. في Flutter SDK ، يسمى هذا النوع ChangeNotifier . لكي يكون الكائن من النوع ChangeNotifier متاحًا لعناصر واجهة مستخدم أخرى ، نحتاج إلى ChangeNotifierProvider . توفر الأشياء المرصودة لجميع أحفادها. الكائن القادر على تلقي البيانات الحالية هو المستهلك ، الذي يحتوي على مثيل ChangeNotifier في معلمة وظيفة البناء الخاصة به والتي يمكن استخدامها لتغذية العروض اللاحقة بالبيانات.
ما هو BLoC في Flutter
مكونات منطق الأعمال هي بنية Flutter تشبه إلى حد كبير الحلول الشائعة في الأجهزة المحمولة مثل MVP أو MVVM. يوفر فصل طبقة العرض عن قواعد منطق الأعمال. هذا تطبيق مباشر للنهج التصريحي الذي يؤكد Flutter بشدة على أي UI = f (state) . BLoC هو مكان تذهب إليه الأحداث من واجهة المستخدم. ضمن هذه الطبقة ، كنتيجة لتطبيق قواعد العمل على حدث معين ، تستجيب BLoC بحالة معينة ، والتي تعود بعد ذلك إلى واجهة المستخدم. عندما تتلقى طبقة العرض حالة جديدة ، فإنها تعيد بناء عرضها وفقًا لما تتطلبه الحالة الحالية.
هل تشعر بالفضول حيال تطوير Flutter؟
انظر إلى حلولناكيفية إنشاء قائمة في Flutter
ربما تكون القائمة القابلة للتمرير واحدة من أكثر طرق العرض شيوعًا في تطبيقات الهاتف المحمول. لذلك ، قد يكون اختيار بنية Flutter الصحيحة أمرًا بالغ الأهمية هنا. من الناحية النظرية ، فإن عرض القائمة نفسها ليس بالأمر الصعب. يصبح الموقف أكثر تعقيدًا عندما نضيف ، على سبيل المثال ، القدرة على تنفيذ إجراء معين على كل عنصر. يجب أن يتسبب ذلك في حدوث تغيير في أماكن مختلفة في التطبيق. في قائمتنا ، سنتمكن من تحديد كل عنصر من العناصر ، وسيتم عرض كل عنصر من العناصر المحددة في قائمة منفصلة على شاشة مختلفة.

لذلك ، علينا تخزين العناصر التي تم تحديدها ، بحيث يمكن عرضها على شاشة جديدة. بالإضافة إلى ذلك ، سنحتاج إلى إعادة إنشاء العرض في كل مرة يتم فيها النقر على مربع الاختيار ، لإظهار الاختيار / إلغاء التحديد بالفعل.
نموذج عنصر القائمة يبدو بسيطًا جدًا:
فئة SocialMedia { معرف int عنوان السلسلة رمز السلسلة منطقية مفضل ؛ SocialMedia ( {@ required this.id، مطلوب هذا العنوان @ ، @ مطلوب this.iconAsset ، this.isFavourite = false}) ؛ مجموعة باطلة (قيمة منطقية مفضلة) { this.isFavourite = isFavourite؛ } }
كيفية إنشاء قائمة مع المزود
في نمط الموفر ، يجب تخزين النموذج أعلاه في كائن. يجب أن يوسع الكائن ChangeNotifier ليتمكن من الوصول إلى SocialMedia من مكان آخر في التطبيق.
تقوم فئة SocialMediaModel بتوسيع ChangeNotifier { القائمة النهائية <SocialMedia> _socialMedia = [/ * بعض كائنات الوسائط الاجتماعية * /] ؛ UnmodifiableListView <SocialMedia> الحصول على المفضلة { return UnmodifiableListView (_socialMedia.where ((item) => item.isFavourite)) ؛ } UnmodifiableListView <SocialMedia> احصل على كل { إرجاع UnmodifiableListView (_socialMedia) ؛ } void setFavourite (int itemId ، bool isChecked) { _socialMedia .firstWhere ((item) => item.id == itemId) .setFavourite (isChecked) ؛ notifyListeners () ، }
أي تغيير في هذا الكائن ، والذي سيتطلب إعادة بناء على العرض ، يجب الإشارة إليه باستخدام notifyListeners () . في حالة طريقة setFavourite () لتوجيه Flutter لإعادة عرض جزء واجهة المستخدم ، سيراقب ذلك التغيير في هذا الكائن.
الآن يمكننا الانتقال إلى إنشاء القائمة. لملء ListView بالعناصر ، سنحتاج للوصول إلى كائن SocialMediaModel ، الذي يخزن قائمة بجميع العناصر. يمكنك القيام بذلك بطريقتين:
- Provider.of <ModelType> (سياق ، استمع: خطأ)
- مستهلك
الأول يوفر الكائن المرصود ويسمح لنا بتحديد ما إذا كان الإجراء الذي يتم تنفيذه على الكائن يجب أن يعيد إنشاء عنصر واجهة المستخدم الحالي ، باستخدام معلمة الاستماع . سيكون هذا السلوك مفيدًا في حالتنا.
تمتد فئة SocialMediaListScreen إلى StatelessWidget { SocialMediaListScreen () ، @تجاوز بناء الأداة (سياق BuildContext) { var socialMedia = Provider.of <SocialMediaModel> (سياق ، استمع: خطأ) ؛ عودة ListView ( الأطفال: socialMedia.all .map ((item) => CheckboxSocialMediaItem (العنصر: العنصر)) .لإدراج()، ) ؛ } }
نحتاج إلى قائمة بجميع وسائل التواصل الاجتماعي ولكن ليست هناك حاجة لإعادة بناء القائمة بأكملها. دعنا نلقي نظرة على شكل عنصر واجهة مستخدم القائمة.
يمتد class CheckboxSocialMediaItem إلى StatelessWidget { عنصر SocialMedia النهائي ؛ CheckboxSocialMediaItem ({Key key،required this.item}): super (key: key)؛ @تجاوز بناء الأداة (سياق BuildContext) { عودة الحشو ( المساحة المتروكة: const EdgeInsets.all (Dimens.paddingDefault) ، طفل: صف ( الأطفال: [ المستهلك <SocialMediaModel> ( باني: (سياق ، نموذج ، طفل) { عودة خانة الاختيار ( القيمة: item.isFavourite ، onChanged: (isChecked) => model.setFavourite (item.id، isChecked) ، ) ؛ } ، ) ، SocialMediaItem ( العنصر: العنصر ، ) ] ، ) ، ) ؛ } }
نستمع إلى التغيير في قيمة مربع الاختيار ونقوم بتحديث النموذج بناءً على حالة الاختيار. يتم تعيين قيمة مربع الاختيار نفسها باستخدام خاصية من نموذج البيانات. هذا يعني أنه بعد التحديد ، سيغير النموذج الحقل isFavourite إلى true . ومع ذلك ، لن يقدم العرض هذا التغيير حتى نعيد إنشاء مربع الاختيار. هنا ، يأتي كائن المستهلك مع المساعدة. يوفر الكائن المرصود ويعيد بناء جميع أحفاده بعد تلقي معلومات حول التغيير في النموذج.
يجدر وضع المستهلك فقط عندما يكون من الضروري تحديث عنصر واجهة المستخدم لتجنب إعادة بناء العروض غير الضرورية. يرجى ملاحظة أنه إذا ، على سبيل المثال ، سيؤدي تحديد مربع الاختيار إلى تشغيل بعض الإجراءات الإضافية مثل تغيير عنوان العنصر ، فسيتعين نقل المستهلك إلى أعلى في شجرة عنصر واجهة المستخدم ، حتى يصبح أحد الوالدين للأداة المسؤولة عن عرض العنوان . خلاف ذلك ، لن يتم تحديث طريقة عرض العنوان.
سيبدو إنشاء شاشة وسائط اجتماعية مفضلة مشابهًا. سوف نحصل على قائمة بالعناصر المفضلة باستخدام المزود .
class FavouritesListScreen يوسع StatelessWidget { المفضلةListScreen () ، @تجاوز بناء الأداة (سياق BuildContext) { var list = Provider.of <SocialMediaModel> (السياق ، الاستماع: خطأ) .favourites ؛ عودة ListView ( الأطفال: قائمة .map ((item) => المساحة المتروكة ( المساحة المتروكة: const EdgeInsets.all (Dimens.paddingDefault) ، الطفل: SocialMediaItem (العنصر: العنصر))) .لإدراج()، ) ؛ } }
عندما يتم استدعاء طريقة البناء ، سيعيد المزود القائمة الحالية لوسائل التواصل الاجتماعي المفضلة.
كيفية إنشاء قائمة باستخدام BLoC
في تطبيقنا البسيط ، لدينا شاشتان حتى الآن. سيكون لكل منهم كائن BLoC خاص به. ومع ذلك ، ضع في اعتبارك أن العناصر المحددة على الشاشة الرئيسية ستظهر في قائمة الوسائط الاجتماعية المفضلة. لذلك ، يجب علينا بطريقة ما نقل أحداث تحديد مربع الاختيار خارج الشاشة. الحل هو إنشاء كائن BLoC إضافي سيتعامل مع الأحداث التي تؤثر على حالة العديد من الشاشات. دعنا نسميها BLoC العالمي. بعد ذلك ، ستستمع كائنات BLoC المخصصة للشاشات الفردية للتغييرات في حالات BLoC العالمية وتستجيب وفقًا لذلك.
قبل إنشاء كائن BLoC ، يجب أن تفكر أولاً في الأحداث التي سيتمكن العرض من إرسالها إلى طبقة BLoC وما الحالات التي ستستجيب لها. في حالة BLoC العالمية ، ستكون الأحداث والحالات على النحو التالي:
فئة مجردة SocialMediaEvent {} يمتد class CheckboxChecked إلى SocialMediaEvent { يتم فحص منطقية نهائية ؛ معرف العنصر النهائي ؛ CheckboxChecked (this.isChecked، this.itemId) ؛ } فئة مجردة SocialMediaState {} يمتد فئة ListPresented إلى SocialMediaState { القائمة النهائية قائمة <SocialMedia> ؛ ListPresented (this.list) ؛ }
يجب أن يكون حدث CheckboxChecked في BLoC العام ، لأنه سيؤثر على حالة العديد من الشاشات - وليس شاشة واحدة فقط. عندما يتعلق الأمر بالحالات ، لدينا حالة تكون القائمة فيها جاهزة للعرض. من وجهة نظر BLoC العالمية ، ليست هناك حاجة لإنشاء المزيد من الحالات. يجب أن تعرض كلتا الشاشتين القائمة ويجب أن تهتم بها بيانات BLoCs الفردية المخصصة للشاشة المحددة. سيبدو تنفيذ BLoC العالمي نفسه كما يلي:
class SocialMediaBloc توسع Bloc <SocialMediaEvent، SocialMediaState> { مستودع SimpleSocialMediaRepository النهائي ؛ SocialMediaBloc (this.repository) ؛ @تجاوز SocialMediaState get initialState => ListPresented (repository.getSocialMedia) ؛ @تجاوز دفق <SocialMediaState> mapEventToState (حدث SocialMediaEvent) غير متزامن * { إذا (الحدث هو CheckboxChecked) { العائد _mapCheckboxCheckedToState (حدث) ؛ } } SocialMediaState _mapCheckboxCheckedToState (حدث CheckboxChecked) { final updatedList = (الحالة كـ ListPresented) .list ؛ قائمة محدثة .firstWhere ((item) => item.id == event.itemId) .setFavourite (event.isChecked) ؛ إرجاع ListPresented (updatedList) ؛ } }
الحالة الأولية هي ListPresented - نفترض أننا تلقينا بالفعل بيانات من المستودع. نحتاج فقط للرد على حدث واحد - CheckboxChecked . لذلك سنقوم بتحديث العنصر المحدد باستخدام طريقة setFavourite وإرسال القائمة الجديدة الملفوفة في حالة ListPresented .
نحتاج الآن إلى إرسال حدث CheckboxChecked عند الضغط على مربع الاختيار. للقيام بذلك ، سنحتاج إلى مثيل SocialMediaBloc في مكان يمكننا من خلاله إرفاق رد الاتصال onChanged . يمكننا الحصول على هذا المثيل باستخدام BlocProvider - يبدو مشابهًا للمزود من النمط الذي تمت مناقشته أعلاه. لكي يعمل مثل BlocProvider ، أعلى في شجرة عناصر واجهة المستخدم ، يجب تهيئة كائن BLoC المطلوب. في مثالنا ، سيتم إجراؤها بالطريقة الرئيسية:
void main () => runApp (BlocProvider ( إنشاء: (سياق) { إرجاع SocialMediaBloc (SimpleSocialMediaRepository ()) ، } ، التابع: ArchitecturesSampleApp ()))؛
بفضل هذا ، في رمز القائمة الرئيسية ، يمكننا بسهولة الاتصال بـ BLoC باستخدام BlocProvider.of () وإرسال حدث إليها باستخدام طريقة الإضافة :
تمتد فئة SocialMediaListScreen إلى StatefulWidget { _SocialMediaListState createState () => _SocialMediaListState () ؛ } تمدد class _SocialMediaListState الحالة <SocialMediaListScreen> { @تجاوز بناء الأداة (سياق BuildContext) { إرجاع BlocBuilder <SocialMediaListBloc ، SocialMediaListState> ( باني: (سياق ، حالة) { إذا (الحالة هي MainListLoaded) { عودة ListView ( الأطفال: state.socialMedia .map ((item) => CheckboxSocialMediaItem ( العنصر: العنصر ، onCheckboxChanged: (isChecked) => BlocProvider.of <SocialMediaBloc> (سياق) .add (CheckboxChecked (isChecked، item.id)) ، )) .لإدراج()، ) ؛ } آخر { مركز العودة (الطفل: نص (Strings.emptyList)) ؛ } } ، ) ؛ } }
لدينا بالفعل انتشار حدث CheckboxChecked إلى BLoC ، ونعرف أيضًا كيف ستستجيب BLoC لمثل هذا الحدث. ولكن في الواقع ... ما الذي سيؤدي إلى إعادة بناء القائمة مع مربع الاختيار المحدد بالفعل؟ لا يدعم BLoC العالمي حالات القائمة المتغيرة ، لأنه يتم معالجتها بواسطة كائنات BLoC فردية مخصصة للشاشات. الحل المذكور سابقًا هو الاستماع إلى BLoC عالمي لتغيير الحالة والاستجابة وفقًا لهذه الحالة. أدناه ، BLoC مخصص لقائمة الوسائط الاجتماعية الرئيسية مع مربع اختيار:

فئة SocialMediaListBloc بتوسيع Bloc <SocialMediaListEvent، SocialMediaListState> { النهائي SocialMediaBloc mainBloc ؛ SocialMediaListBloc ({@ required this.mainBloc}) { mainBloc.listen ((حالة) { إذا (الحالة هي ListPresented) { إضافة (ScreenStart (state.list)) ؛ } }) ؛ } @تجاوز SocialMediaListState get initialState => MainListEmpty () ؛ @تجاوز دفق <SocialMediaListState> mapEventToState ( حدث SocialMediaListEvent) غير متزامن * { التبديل (event.runtimeType) { شاشة الحالة العائد MainListLoaded ((الحدث مثل ScreenStart) .list) ؛ فترة راحة؛ } } }
عندما ترجع SocialMediaBloc الحالة ListPresented ، سيتم إخطار SocialMediaListBloc . لاحظ أن ListPresented ينقل قائمة. إنه الذي يحتوي على معلومات محدثة حول التحقق من العنصر باستخدام مربع الاختيار.
وبالمثل ، يمكننا إنشاء BLoC مخصص لشاشة الوسائط الاجتماعية المفضلة:
class FavouritesListBloc توسع Bloc <FavouritesListEvent، FavouritesListSate> { النهائي SocialMediaBloc mainBloc ؛ FavouritesListBloc ({@ required this.mainBloc}) { mainBloc.listen ((حالة) { إذا (الحالة هي ListPresented) { add (FavouritesScreenStart (state.list)) ؛ } }) ؛ } @تجاوز FavouritesListSate get initialState => FavouritesListEmpty () ؛ @تجاوز دفق <FavouritesListSate> mapEventToState (حدث FavouritesListEvent) غير متزامن * { إذا (الحدث هو FavouritesScreenStart) { var favouritesList = event.list.where ((item) => item.isFavourite) .toList () ؛ العائد المفضل ListLoaded (favouritesList) ؛ } } }
يؤدي تغيير الحالة في BLoC العالمية إلى إطلاق حدث FavouritesScreenStart مع القائمة الحالية. بعد ذلك ، يتم تصفية العناصر التي تحمل علامة واحدة على أنها مفضلة ويتم عرض هذه القائمة على الشاشة.
كيفية إنشاء نموذج مع العديد من الحقول في Flutter
قد تكون النماذج الطويلة صعبة ، خاصةً عندما تفترض المتطلبات متغيرات مختلفة للتحقق من الصحة ، أو بعض التغييرات على الشاشة بعد إدخال النص. في شاشة المثال ، لدينا نموذج يتكون من عدة حقول وزر "NEXT". سيتم التحقق من صحة الحقول تلقائيًا وسيتم تعطيل الزر حتى يصبح النموذج صالحًا تمامًا. بعد النقر فوق الزر ، سيتم فتح شاشة جديدة بالبيانات المدخلة في النموذج.
يتعين علينا التحقق من صحة كل حقل والتحقق من تصحيح النموذج بالكامل لضبط حالة الزر بشكل صحيح. بعد ذلك ، يجب تخزين البيانات التي تم جمعها للشاشة التالية.

كيفية إنشاء نموذج مع العديد من الحقول مع الموفر
في تطبيقنا ، سنحتاج إلى ChangeNotifier ثاني ، مخصص لشاشات المعلومات الشخصية. لذلك يمكننا استخدام MultiProvider ، حيث نقدم قائمة بكائنات ChangeNotifier . ستكون متاحة لجميع أحفاد MultiProvider .
class ArchitecturesSampleApp يمتد StatelessWidget { مستودع SimpleSocialMediaRepository النهائي ؛ ArchitecturesSampleApp ({Key key، this.repository}): super (key: key)؛ @تجاوز بناء الأداة (سياق BuildContext) { إرجاع MultiProvider ( الموفرون: [ ChangeNotifierProvider <SocialMediaModel> ( إنشاء: (سياق) => SocialMediaModel (مستودع) ، ) ، ChangeNotifierProvider <PersonalDataNotifier> ( إنشاء: (سياق) => PersonalDataNotifier () ، ) ] ، طفل: MaterialApp ( العنوان: Strings.architecturesSampleApp ، debugShowCheckedModeBanner: خطأ ، المنزل: StartScreen () ، المسارات: <String ، WidgetBuilder> { Routes.socialMedia: (السياق) => SocialMediaScreen () ، Routes.favourites: (السياق) => FavouritesScreen () ، Routes.personalDataForm: (السياق) => PersonalDataScreen () ، Routes.personalDataInfo: (السياق) => PersonalDataInfoScreen () } ، ) ، ) ؛ } }
في هذه الحالة ، سيعمل PersonalDataNotifier كطبقة منطق عمل - سيقوم بالتحقق من صحة الحقول ، ولديه حق الوصول إلى نموذج البيانات لتحديثه ، وتحديث الحقول التي سيعتمد عليها العرض.
النموذج نفسه عبارة عن واجهة برمجة تطبيقات لطيفة جدًا من Flutter ، حيث يمكننا إرفاق عمليات التحقق تلقائيًا باستخدام مدقق الخاصية وحفظ البيانات من النموذج إلى النموذج باستخدام رد الاتصال onSaved . سنقوم بتفويض قواعد التحقق إلى PersonalDataNotifier وعندما يكون النموذج صحيحًا ، سنقوم بتمرير البيانات المدخلة إليه.
أهم شيء على هذه الشاشة هو الاستماع للتغيير في كل مجال وتمكين الزر أو تعطيله ، اعتمادًا على نتيجة التحقق من الصحة. سنستخدم رد الاتصال عند التغيير من كائن النموذج . في ذلك ، سوف نتحقق أولاً من حالة التحقق ثم نمررها إلى PersonalDataNotifier .
استمارة( المفتاح: _formKey ، autovalidate: صحيح ، onChanged: () => _onFormChanged (personalDataNotifier) ، طفل: void _onFormChanged (PersonalDataNotifier personalDataNotifier) { var isValid = _formKey.currentState.validate () ، PersonalDataNotifier.onFormChanged (isValid) ، }
في PersonalDataNotifier ، سنقوم بإعداد متغير isFormValid . سنقوم بتعديله (لا تنس استدعاء notifyListeners () ) وفي طريقة العرض ، سنقوم بتغيير حالة الزر اعتمادًا على قيمته. تذكر الحصول على مثيل Notifier مع المعلمة listen: true - وإلا فلن يتم إعادة بناء وجهة نظرنا وستظل حالة الزر دون تغيير.
var personalDataNotifier = Provider.of <PersonalDataNotifier> (سياق ، استمع: صحيح) ؛
في الواقع ، نظرًا لحقيقة أننا نستخدم personalDataNotifier في أماكن أخرى ، حيث لا تكون إعادة تحميل العرض ضرورية ، فإن السطر أعلاه ليس هو الأمثل ويجب أن يتم ضبط معلمة الاستماع على false . الشيء الوحيد الذي نريد إعادة تحميله هو الزر ، حتى نتمكن من لفه في المستهلك الكلاسيكي:
المستهلك <PersonalDataNotifier> ( builder: (السياق ، المخطر ، الطفل) { عودة RaisedButton ( الطفل: نص (Strings.addressNext) ، مضغوط: notifier.isFormValid ؟ / * الإجراء عند تمكين الزر * / : لا شيء، اللون: الألوان. معطل اللون: Colors.grey ، ) ؛ } ، )
بفضل هذا ، لا نجبر المكونات الأخرى على إعادة التحميل في كل مرة نستخدم فيها منبهًا.
في طريقة العرض التي تعرض البيانات الشخصية ، لن تكون هناك مشاكل أخرى - لدينا حق الوصول إلى PersonalDataNotifier ومن هناك ، يمكننا تنزيل النموذج المحدث.
كيفية إنشاء نموذج مع العديد من الحقول باستخدام BLoC
بالنسبة للشاشة السابقة ، احتجنا إلى كائنين من كائنات BLoC . لذلك عندما نضيف "شاشة مزدوجة" أخرى ، سيكون لدينا أربعة معًا. كما في حالة الموفر ، يمكننا التعامل معه باستخدام MultiBlocProvider ، والذي يعمل بشكل متماثل تقريبًا.
باطل main () => runApp ( MultiBlocProvider (الموفرون: [ BlocProvider ( إنشاء: (سياق) => SocialMediaBloc (SimpleSocialMediaRepository ()) ، ) ، BlocProvider ( إنشاء: (سياق) => SocialMediaListBloc ( mainBloc: BlocProvider.of <SocialMediaBloc> (سياق))) ، BlocProvider ( إنشاء: (السياق) => PersonalDataBloc () ، ) ، BlocProvider ( إنشاء: (السياق) => PersonalDataInfoBloc ( mainBloc: BlocProvider.of <PersonalDataBloc> (سياق)) ، ) ] ، التابع: ArchitecturesSampleApp ()) ، ) ؛
كما هو الحال في نمط BLoC ، من الأفضل أن تبدأ بالحالات والإجراءات المحتملة.
فئة مجردة PersonalDataState {} تعمل فئة NextButtonDisabled على توسيع PersonalDataState {} تقوم فئة NextButtonEnabled بتوسيع PersonalDataState {} تعمل فئة InputFormCorrect على توسيع PersonalDataState { نموذج البيانات الشخصية النهائي ؛ InputFormCorrect (this.model) ، }
ما يتغير على هذه الشاشة هو حالة الزر. لذلك نحن بحاجة إلى دول منفصلة لذلك. بالإضافة إلى ذلك ، ستسمح لنا حالة InputFormCorrect بإرسال البيانات التي جمعها النموذج.
فئة مجردة PersonalDataEvent {} تعمل فئة FormInputChanged على توسيع PersonalDataEvent { منطقي النهائي صالح ؛ FormInputChanged (this.isValid) ، } يمتد فئة FormCorrect إلى PersonalDataEvent { البيانات الشخصية النهائية لنموذج البيانات ؛ FormCorrect (this.formData) ؛ }
الاستماع إلى التغييرات في النموذج أمر بالغ الأهمية ، ومن هنا حدث FormInputChanged . عندما يكون النموذج صحيحًا ، سيتم إرسال الحدث FormCorrect .
عندما يتعلق الأمر بالتحقق من الصحة ، هناك فرق كبير هنا إذا قارنته بالمزود. إذا كنا نرغب في إرفاق كل منطق التحقق من الصحة في طبقة BLoC ، فسيكون لدينا الكثير من الأحداث لكل حقل من الحقول. بالإضافة إلى ذلك ، قد تتطلب العديد من الدول طريقة العرض لإظهار رسائل التحقق من الصحة.
هذا ، بالطبع ، ممكن ولكنه سيكون مثل قتال ضد TextFormField API بدلاً من استخدام فوائده. لذلك ، إذا لم تكن هناك أسباب واضحة ، يمكنك ترك عمليات التحقق في طبقة العرض.
ستعتمد حالة الزر على الحالة المرسلة إلى طريقة العرض بواسطة BLoC :
BlocBuilder <PersonalDataBloc، PersonalDataState> ( باني: (سياق ، حالة) { عودة RaisedButton ( الطفل: نص (Strings.addressNext) ، عند الضغط: الحالة هي NextButtonEnabled ؟ / * الإجراء عند تمكين الزر * / : لا شيء، اللون: الألوان. معطل اللون: Colors.grey ، ) ؛ })
ستكون معالجة الأحداث وتعيينها إلى الدول في PersonalDataBloc على النحو التالي:
@تجاوز دفق <PersonalDataState> mapEventToState (حدث PersonalDataEvent) غير متزامن * { إذا (الحدث هو FormCorrect) { العائد InputFormCorrect (event.formData) ؛ } else if (event is FormInputChanged) { العائد mapFormInputChangedToState (حدث) ، } } PersonalDataState mapFormInputChangedToState (حدث FormInputChanged) { إذا (event.isValid) { إرجاع NextButtonEnabled () ؛ } آخر { إرجاع NextButtonDisabled () ؛ } }
أما بالنسبة للشاشة التي تحتوي على ملخص للبيانات الشخصية ، فالوضع مشابه للمثال السابق. سيقوم BLoC المرفق بهذه الشاشة باسترداد معلومات النموذج من BLoC لشاشة النموذج.
فئة PersonalDataInfoBloc لتوسيع Bloc <PersonalDataInfoEvent، PersonalDataInfoState> { النهائي PersonalDataBloc mainBloc ؛ PersonalDataInfoBloc ({@ required this.mainBloc}) { mainBloc.listen ((حالة) { إذا (الحالة هي InputFormCorrect) { إضافة (PersonalDataInfoScreenStart (state.model)) ؛ } }) ؛ } @تجاوز PersonalDataInfoState get initialState => InfoEmpty () ؛ @تجاوز دفق <PersonalDataInfoState> mapEventToState (حدث PersonalDataInfoEvent) غير متزامن * { إذا (الحدث هو PersonalDataInfoScreenStart) { العائد InfoLoaded (event.model) ؛ } } }
بنية الرفرفة: ملاحظات يجب تذكرها
الأمثلة أعلاه كافية لإظهار أن هناك اختلافات واضحة بين البنيتين. يفصل BLoC طبقة العرض عن منطق الأعمال جيدًا. وهذا يستلزم إعادة استخدام أفضل وقابلية للاختبار. يبدو أنه للتعامل مع الحالات البسيطة ، تحتاج إلى كتابة رمز أكثر من الموفر . كما تعلم ، في هذه الحالة ، ستصبح بنية Flutter أكثر فائدة مع زيادة تعقيد التطبيق.
هل تريد إنشاء تطبيق موجه نحو المستقبل لعملك؟
دعونا الحصول على اتصاليفصل المزود أيضًا واجهة المستخدم عن المنطق جيدًا ولا يفرض إنشاء حالات منفصلة مع كل تفاعل للمستخدم ، مما يعني أنه غالبًا لا يتعين عليك كتابة كمية كبيرة من التعليمات البرمجية للتعامل مع حالة بسيطة. لكن هذا يمكن أن يسبب مشاكل في الحالات الأكثر تعقيدًا.
انقر هنا للتحقق من المشروع بأكمله.