استرجاع الأسماك - مكتبة جديدة للرجوع رفرفة

في نهاية عام 2018 ، قدمت Google ، بمساعدة من مجتمع المصادر المفتوحة ، هدية عظيمة لمطوري الأجهزة المحمولة من خلال إصدار أول نسخة مستقرة من إطار تطوير الأجهزة المحمولة عبر النظام الأساسي Flutter.


ومع ذلك ، عند تطوير تطبيقات كبيرة أكبر قليلاً من Hello Worlds المكونة من صفحة واحدة ، فقد يواجه المطورون حالة من عدم اليقين. كيفية كتابة الطلب؟ إطار العمل صغير جدًا ، لا يوجد حتى الآن قاعدة كافية من الأمثلة الجيدة ذات المصدر المفتوح ، والتي يمكن من خلالها فهم إيجابيات وسلبيات استخدام أنماط مختلفة ، لفهم ما يجب استخدامه في هذه الحالة بالذات وما لا.


يتم حفظ الموقف من خلال حقيقة أن Flutter لديه درجة معينة من التشابه مع React و React Native ، مما يعني أنه يمكنك التعلم من بعض تجربة البرمجة في الأخير. ربما كان هذا بسبب ظهور مكتبات مثل Flutter Flux و Flutter Hooks و MobX بالإضافة إلى العديد من تطبيقات Redux مرة واحدة. لفترة طويلة ، كان الإصدار الأكثر شعبية براين إيجان يسمى Flutter Redux .


ومع ذلك ، قبل شهرين تم مشاهدة الالتزام الأول بواسطة مكتبة Fish Redux ، التي تم نشرها تحت اسم Alibaba. اكتسبت المكتبة في وقت قصير شعبية كبيرة ، بالفعل في اليوم الأول قبل تنفيذ برايان من حيث عدد النجوم ، وفي اليوم الثاني كانت أسرع مرتين.


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


فما هو الفرق الكبير بين إصدار Fish'a من براين؟ رفرفة Redux هو إطار إدارة الدولة. Fish هو إطار تطبيق يضع Redux في مركزه كأساس لإدارة الدولة. أي يحل السمك بعض المهام الإضافية ولا يقتصر على state management .


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


العلاقة بين المخفض والأثر والعرض في المكون


صورة


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

دولة


على سبيل المثال ، خذ الفرس. دع هناك حقل واحد فقط في حالته ، والذي سيشير إلى العدد المثالي للنقرات.


 class ClickerState implements Cloneable<ClickerState> { int count = 0; @override ClickerState clone() { return ClickerState() ..count = count; } } 

يجب أن تكون الدول ثابتة وغير قابلة للتغيير. يمكن الحفاظ على مناعة الدولة بسهولة من خلال تطبيق واجهة Cloneable. في المستقبل ، عندما تحتاج إلى إنشاء حالة جديدة ، يمكنك ببساطة استخدام طريقة clone() .


المخفض


جوهر المخفض هو الرد على بعض الإجراءات من خلال إعادة حالة جديدة. يجب ألا يرتكب المخفض أي آثار جانبية.


سنكتب مخفضًا بسيطًا من شأنه أن يزيد عددًا بعدد ما عند استلام الإجراء المقابل (أقل قليلاً منه).


 ClickerState clickerReducer(ClickerState state, Action action) { //        Action,      . if (action.type == Actions.increase) { // ..       ,   ,       Count. return state.clone() ..count = state.count + action.payload; //        /payload/ count. // payload   . } // if (action.type == ...) { ... } //        . return state; } 

أيضا ، يمكن كتابة هذا المخفض في النموذج التالي:


 Reducer<ClickerState> buildClickerReducer() { asReducer({ Actions.increase: (state, action) => state.clone() ..count = state.count + action.payload, //Actions.anotherAction: ... }); } 

عمل


الإجراء - فئة في مكتبة FishRedux تحتوي على حقلين:
Object type - Object type الإجراء ، وعادة ما يكون كائن التعداد
dynamic payload - معلمة الإجراء ، اختيارية.


مثال:


 enum Actions { increase } //     class ActionsCreate { //      static Action increase(int value) => Action(Actions.increase, payload: value); } 

رأي


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


إن وظيفة الإرسال ضرورية لإرسال الإجراءات: إجراء ، قمنا بإنشائه سابقًا.
يحتوي ViewService على BuildContext الحالي (من مكتبة الرفرفة القياسية) ويوفر طرقًا لإنشاء التبعيات ، ولكن عنهم لاحقًا.


مثال:


 Widget clickerView(ClickerState state, Dispatch dispatch, ViewService viewService) { return RaisedButton( child: Text(state.count.toString()), onPressed: () => dispatch(ActionsCreate.increase(1)) //         ); } 

عنصر


سنقوم بتجميع مكوننا من كل هذا:


 class ClickerComponent extends Component<ClickerState> { ClickerComponent() : super( reducer: clickerReducer, view: clickerView, ); } 

كما ترون ، لا يتم استخدام التأثير في مثالنا ، لأنه ليس من الضروري. التأثير هو وظيفة يجب أن تؤدي جميع الآثار الجانبية. لكن دعنا نتوصل إلى قضية لا يمكنك الاستغناء عنها. على سبيل المثال ، قد يكون هذا زيادة في عددنا برقم عشوائي من خدمة random.org.


تأثير مثال التنفيذ
 import 'package:http/http.dart' as http; //   http   Effect<ClickerState> clickerEffect() { return combineEffects({ Actions.increaseRandomly: increaseRandomly, }); } Future<void> increaseRandomly(Action action, Context<ClickerState> context) async { final response = await http.read('https://www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain'); //   random.org.      1  10. final value = int.parse(response); context.dispatch(ActionsCreate.increase(value)); } //   increaseRandomly enum Actions { increase, /* new */ increaseRandomly } class ActionsCreate { static Action increase(int value) => Action(Actions.increase, payload: value); static Action increaseRandomly() => const Action(Actions.increaseRandomly); // new } //  ,        . Widget clickerView(ClickerState state, Dispatch dispatch, ViewService viewService) { return Column( mainAxisSize: MainAxisSize.min, children: [ RaisedButton( //   child: Text(state.count.toString()), onPressed: () => dispatch(ActionsCreate.increase(1)) ), RaisedButton( //  child: const Text('Increase randomly'), onPressed: () => dispatch(ActionsCreate.increaseRandomly()) ), ] ); } //     class ClickerComponent extends Component<ClickerState> { ClickerComponent() : super( reducer: clickerReducer, view: clickerView, effect: clickerEffect() ); } 

صفحة


يوجد ملحق للمكون يسمى الصفحة <T ، P>. تحتوي الصفحة على حقلين إضافيين:
T initState(P params) - دالة تُرجع الحالة الأولية. سيتم استدعاء عندما يتم إنشاء الصفحة.
List<Middleware<T>> middleware - قائمة الوسيطة - الوظائف التي سيتم استدعاء قبل المخفض.
وأيضا طريقة واحدة:
Widget buildPage(P params) - الذي يجمع الصفحة في عنصر واجهة مستخدم يعمل.


لنقم بإنشاء الصفحة الرئيسية للتطبيق:


 class MainPage extends Page<void, void> { MainPage(): super( initState: (dynamic param) {}, view: (state, dispatch, viewService) => Container(), ); } 

تعمل الصفحة على توسيع أحد مكوناته ، مما يعني أنه يمكن أن يتضمن المخفض والتأثير وكل شيء آخر في المكون العادي.


في المثال ، تم إنشاء صفحة فارغة لا تحتوي على حالة ولا مخفضات أو تأثيرات. سوف نصلح هذا لاحقا.


كل هذا في شكل مختلف قليلاً وفي تطبيق Brian Egan's Flutter Redux ، بالإضافة إلى تطبيقات Redux الأخرى. دعنا ننتقل إلى الميزة الرئيسية للمكتبة الجديدة - التبعيات.


تبعيات


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


تحتاج أولاً إلى إضافة الحالة إلى صفحتنا:


 class MainState implements Cloneable<MainState> { ClickerState clicker; @override MainState clone() { return MainState() ..clicker = clicker; } static MainState initState(dynamic params) { return MainState() ..clicker = ClickerState(); } } 

الآن يمكننا كتابة رابط:


 class ClickerConnector extends ConnOp<MainState, ClickerState> { @override ClickerState get(MainState state) => state.clicker; //        . @override void set(MainState state, ClickerState subState) => state.clicker = subState; } 

هذا كل شيء. كل شيء جاهز لإضافة مكون لدينا:


 class MainPage extends Page<MainState, void> { MainPage(): super( initState: MainState.initState, dependencies: Dependencies( slots: { 'clicker': ClickerComponent().asDependent(ClickerConnector()), //    // 'clicker': ClickerComponent() + ClickerConnector(), }, ), view: (state, dispatch, viewService) { //   clicker-. final clickerWidget = viewService.buildComponent('clicker'); return Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ Center( child: clickerWidget, //   ) ], ) ); }, ); } 

وبالتالي ، يمكنك الآن إنشاء تطبيق عمل كامل عن طريق إضافة التعليمات البرمجية التالية إلى main.dart :


 void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override Widget build(BuildContext context) => MaterialApp(home: MainPage().buildPage(null)); } 

كل رمز مفصول الملف متاح هنا . لديك تجربة تطوير جيدة مع رفرفة.

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


All Articles