bloc ! امتي تحتاجه - المميزات والعيوب - الاساسيات - مثال
موضوع البلوك متقدم ولو انت ضعيف في اساسيات dart وبالاخص جزء ال OOP الافضل تزاكر الجزء ده وتفهم يعني ايه وراثه علي الاقل لاننا هنستخدمها هنا في المقاله دي وحتي لو مش ناوي تستخدم بلوك لازال فهمك للاساسيات مهم علشان تقدر تستوعب وتطور من نفسك في المواضيع المتقدم
قبل ماتكمل قراءة لازم تكون عارف يعني ايه state management فلو انت عارف تخطي الجزء ده وابدا علي طول من عند جزء البلوك
يعني ايه state management
لو ترجمنا المصطلح حرفيا فمعناه " تحكم في الحاله " طب حاله ايه الي هنتحكم بيها ؟ وليه ؟
تحكم في الحاله يعني مثلا انت عندك حاجه مرسومه علي الشاشه فيكن مثل Text او Icon او اي Widget
الودجتس دي ليها خصائص لون , حجم خط , طول , عرض . ظل , .....الخ
مجموع الخصائص دي مع بعضو اسمهم حاله "state" لل Widget بتاعتك
فليكن الشكل ده مثلا
دلوقتي خصاص ال Text ده فين ؟ انت حطيتها في TextStyle وراح لل build في return ومبقاش عندك اي صلاحية تعدل عليه وده الكود الي عمل الشكل فوق
طب ماحنا ممكن نستخدم StateFullWidget ونخزن ال style في متغير ولما نحب نغير نديلو القيمة الجديده ونعمل setState
انا محتاجك توصل للنقطة دي بقي 😀😀😀😀
كدا انت عملت حاجه اسمها local state حاله الاستايل النص الي ظاهر علي الشاشه بقي متخزنه عندك محليا علي مستوي ال Widget بتاعتك بس وال build بتعرضو فقط .
وهنا تظهر الحاجه لحلول متقدمه وتنظيم اكتر علشان تقدر تتحكم في الحاله بتاعتك ومن هنا بدا مصطلح State management
ممكن يجي في دماغك سوال انا حليت المشكلة بتاعتك دي ب setState تنظيم ايه واداره ايه اي الي هتخليني استخدم مكتبه زي بلوك او غيرها لما المشكلة خلاص اتحلت بدونهم
حل ال setState هو الصح لو الحاله متخزنه عندك في ال Widget ومفيش حد بيوصل ليها من مكان تاني بس في الحالات الي هقولها دلوقتي setState اسوا قرار ممكن تستخدمه في state management
- لو انت محتاج تنقل ال state من widget للثانية ( lifting state up)
- لو انت محتاج تعمل listen علي الـ state الي متخزنه في مكان تاني اتغيرت تعمل build تاني علشان تعرض ال ui بتاعتك بالشكل الجديد بعد التغير
- عندك اكتر من child في ال build ولو استخدمت setState كلهم هيبقو مضطرين يعملو build تاني ودي مكلفه من ناحية البطارية واداء التطبيق والوقت ومع التكرار هتبقي مجهده جدا للجها
وعلشان كدا بقولك ده اسواء حل علي المدي البعيد الكلام ده وتقريبا كدا اتضحلك دلوقتي احنا محتاجين طريقه كويسة نعمل بيها state management ليه ؟
- تنظيم للكود
- تحكم اكتر علشان هتبقي عارف مين هيعمل build وامتي وميظهرلكش حاجات غريبة ( un expected rebuilds ) 😅
- التطبيق هيستهلك موارد اقل وبطارية اقل
- التطبيق هيبقي سريع لانك مش هتفرض عليه build بدون سبب وهيوفرلك مكان في الذاكرة ( no memory leaks )
طيب دلوقتي في اكتر من مكتبة لموضوع ال state management ده هنستخدم ايه منهم وليه ؟
الموقع الرسمي لفلاتر ذكر المكتبات المتاحه ليك List of state management approaches
وواضح من العنوان اني هتكلم عن bloc
بداية جزء البلوك :
اول حاجه يعني ايه بلوك ؟
البلوك (flutter_bloc) مكتبة بداها Felix Angelov علشان تسهلك التحكم في الحاله الموقع الرسمي للمكتبة
اهم العقبات الي في بلوك
- هتكتب كود كتير ( بس المشكلة دي محلولة لو نزلت اضافه للادتور بتاعك سواء اندرويد استوديو او vs code او انتلج هتوفرلك كود البلوك كامل بكلك ماوس)
- معتمد بشكل تام علي ال stream ومعظم الناس بتخاف منها ( لو معندكش مشكلة مع الاستريم يبقي قشطة )
اهم المميزات الي في البلوك
- بينظم الكود بشكل كبير وبيمنعك تقع في مشاكل ويخليك تتجنبها
- تقدر تحدد لكل widget امتي تعمل build وامتي لا حتي لو في تغير في الحاله بتاعتك state
- صاحبة مبيطلعش ارقام مزورة ويقعد يقول البكدج بتاعتي احسن من غيري
- سهل تعمله testing بسهوله
- سهل تعمل undo/redo لل events
ازاي البلوك بيشتغل او هيسهل عليا التحكم في الحاله ؟
فكره البلوك ان هو عباره عن Object تقدر تقول كدا ليه مدخل واحد ومخرج واحد حاجه بتدخل من ناحية ويحصل ليها معالجه وبعدين تخرج بشكل تاني من الناحية التانية !
طب ايه نوع الحجات الي بتدخل ؟ وايه نوع الحجات الي بتخرج ومين بيعمل التحول وبناء عليه ايه ؟
تعال نجرب دلوقتي بنفسنا نحول ال counter بتاع فلاتر الي موجود في كل بروجكت جديد ونخليه يبقي ب البلوك بدل setState
اول حاجه استخدم المكتبة
flutter_bloc: ^6.1.1
دلوقتي البروجكت هيبقي متقسم كدا
- main ودي هيبقي فيها ال MaterialApp وال Ui بتاعتنا وال Widgets الي هتتعرض علي الشاشه علشان منعملش ملفات كتير
- Bloc بتحتوي علي كود البلوك نفسو
- events بتحتوي علي الحجات الي بتدخل للبلوك
- states بتحتوي علي شكل الحجات الي بتخرج من البلوك
نبدا باول ملف ال counter_event.dart وده هيبقي فيه ال events بتاعتك
يعني ايه Event بقي ؟
بالعربي كدا قبل مانكتب كود اي عمليه هتروح للبلوك هنسميها event يعني مثلا المستخدم كتب البريد وكلمة المرور وعاوزين نبعت الداتا دي للسيرفير ( ليه البلوك الي يكلم السيرفير دي هنتطرق ليها بعدين بس ده الصح وخلينا علي كدا ) هنبعت SignInEvent للبلوك نقولو اليوزر ضغط علي زر الدخول وخد دي البريد وكلمة المرور الي كتبهم
ف دلوقتي احنا شلنا اي كود (logic) من ال UI وده الصح ونقلناهم في ال bloc وده الصح بردو
كل المطلوب من ال UI لما ال المستخدم يضغط علي زر هي تبعت ال event بتاع الزرار ده للبلوك فقط !
طب كدا بالعربي اتمني تكون فهمت فكره ال event
بالكود بقي ال event عباره عن class عادي غالبا هيبقي مفهوش اي متغير مجرد كلاس فاضي ليه اسم معين بيرمز للايفنت ده
لكن لو الايفنت متعلق ببيانات معينه هنعمل معاه ال متغيرات بتاعته
دلوقتي علشان بدايه مع البلوك هنخلي الايفنت مجرد class فاضي
وفي المثال الي بنحاول نطبقه معندناش events غير اننا نزود العداد مع كل ضغطة من المستخدم فقط يبقي Event واحد الي هنكتبو وهيكون بالشكل ده
للتوضيح : اول سطر ده طريقه مختلفه لاستخدام ال imports فلو انت مش هتعرف تستخدمها تجاهلها وانا هقولك تحت تعوضها بالطريقه العاديه ازاي متلقلش !
- السطر رقم 7 فيه ال abstract class الي هيبقي parent لكل ال events بتاعتك
البلوك محتاج مكونين اساسين ال parent بتاع الايفنت وهو الي قدامك ده بس كدا مش اكتر ونفس الكلام ال parent بتاع ال state وهيبقي نفس الطريق الاسم فقط هيتغير وده اساسي مهما كان عدد ال events في البلوك بتاعك لازم تحتاج واحد
السطر رقم 9 فيه ال Increment مجرد class عادي بيورث من الايفنت بتاع البلوك وكدا خلصنا ال events
نضوح الامور بقي علشان منتوهش 😵😵
ليه عملنا parent وورثنا منو وهو فاضي استفدنا ايه ؟
الاستفاده من الحركه دي ان البلوك لازم ياخد نوع معين يسمح بمرور ال events منو فلما حددنا النوع ده وورثنا منو كدا عرفنا ايه نوع ال events الي البلوك بتاعنا يقدر يستقبلها علشان منقعش في غلطاط ونبعت للبلوك حجات مينفعش نبعتا كدا ال vs Code او اندرويد استوديو هيقولوك لا كدا مينفعش من قبل ماتشغل وتقعد تدور علي الغلطة فين
والصور ده هتوضحلك بشكل سريع كدا ازاي هنستخدم ال event الي عملناه علشان تكون مستوعب واحنا بنبني باقي البلوك
عملنا كائن من البلوك وبعتنالو الايفينت الي عملنا بس كدا دي طريقه ارسال event للبلوك ولو عندك ايفنت فيه متغيرات حطها هنا وانت بتبعت الايفنت للبلوك
نعمل ال States بتاعت البلوك بردو بنفس الطريقه علشان نبدا بعدها في البلوك نفسو
- عملنا هنا parent علشان نحدد اي نوع ال state لي هتطلع من البلوك
- عملنا CounterInitial علشان محتاجين حاله تكون جاهزه اول مالشاشه تظهر للمستخدم مفيش لسه events ومفيش اي حاجه اتعملت فالحاله دي البلوك هيبعتها لينا علشان نعرف في ال ui ان دي البداية ونعرض اللازم للبداية تقدر تسميها اي اسم براحتك مش شرط الاسم ده
- عملنا CounterChanged وجواه متغير من نوع int في المستقبل هيعبر عن قيمه العدد الي هيظهر في الشاشه للسمتخدم
- استخدمنا final لان ال events وال states من نوع immutable وده تنظيم اكتر للكود علشان يجبرك متغير في ال event او state اي حاجه بعد مايتعملو مجرد تعرف منهم نوعهم ولو فيهم قيمه تاخدها منهم دورهم نقل البيانات من والي البلوك فقط
حاول بس تواكب معايا وكل شي هيبقي منطقي تدريجيا
دلوقتي هنبدا في كود البلوك نفسو
نوضح بقي الصورة دي :
- فاكر موضوع ال imports انت ممكن هنا تشيل السطر 4 و 5 وتعملهم import عادي والفكره من عملهم بالطريقه دي انك لما تحب تستخدم البلوك هتستخدمو بسطر واحد بس مش 3 سطور لل imports
- class عادي بيورث من Bloc وفيه نوعين الاول الايفنت الي عملنا والتاني ال state الي علمناه
- الكونستراكتور بينادي علي ال super ويحدد ايه الي state الي تتبعت اول حاله
- السطر 9 في المتغير بتاع العداد وفي الكود بتاع فلاتر ده بيبقي متخزن في state بتاعتك وده كان بيصعب موضوع الوصول ليها من اي مكان لكن البلوك تقدر توصل ليه من اي مكان معاك فيه context ( انك توصل للمتغير مباشرة مش الطريقه الصح للبلوك وهتعرف دلوقتي هنوصل ليها ازاي )
- الداله دي مهمه جدا وهي المحور الاساسي في ال bloc -mapEventToState لو تتذكر فوق قولنا ان الداتا بتدخل من نوع من ناحية (event) وبيحصل ليها معالجه mapEventToState وتخرج من الناحية التانية (state)
- الكود الي بيحصل بيشوف نوع ال event الي جاي ايه وبناء عليه بيخرج state لاي حد مستني يستلم ال state دي
- ال ++ قبل ال _count علشان يزود بعدين يبعت القيمة لو عكسناها ال ui هتبقي متاخره بقيمه عدد 1 عن القيمة الحقيقه للعداد
- yield معاناها return ال state دي لاي حد عامل listen بس كمل بعدها مترجعش لو عاوز توضيح اكتر اقرال عن ال (dart streams) انت كدا خلصت البلوك فاضل بس تستخدمو في ال ui بتاعتك
قبل ما تبدا في ال ui محتاج اعرفك علي Widgets جايه مع flutter_bloc
- BlocProvider ودي وظيفتها تديلها context ونوع البلوك الي انت محتاجه وهي هتجبهولك لحد عندك ( مبنية بالبروفيدر ) لكن استخدامها بسيط جدا وسطر كود واحد بس
- BlocListner دي بتحطها في ال Widget Tree بتاعتك وظيفتها تعمل Listen ولما يحصل ان State جديده تطلع من البلوك تقولك خد اهي 😀😀😀😀 ودي تستخدمها لما تكون عاوز تعمل حاجه زي dialog / toast / scaffold/ علشان تنبة المستخدم مثلا او كدا ومش متعلقه بال build بتاع ال ui باي شكل من الاشكال .
- BlocBuilder وباين من اسمها انها مسئولة عن ال build لما يحصل اي state جديده تطلع من البلوك بتديلهالك وانت تعمل if else علي ال state الجديدة وتقرر تعمل build ل ايه بالي يناسبك في حالتنا هنا في المثال لو ال state هي ال inital هنطلع تكست مكتوب فيه اضغط علي ال fab علشان تبدا ولو الحاله هي CounterChanged يعني المستخدم ضغط علي الزر هنطلعلو قيمة العداد الي جاي مع الحاله دي
- BlocConsumer: وده عباره عن builder و listner مع بعض علشان لما تحب تستخدم الاتنين تستخدم Widget واحده بدل اتنين
امسك نفسك وكمل معايا للاخر فاضل حبه 😁😁😁
انت كدا جاهز تستلم ال state من ال bloc
فاضل بس نضيف ال bloc لل widget tree ونعمل الزرار الي هيبعت ال event لما نضغط علي ال fap
علشان تضيف ال bloc لل widget tree هنستخدم BlocProvider ونحطها فوق ال materialApp ولو عندك تطبيق فيه اكتر من بلوك استخدم MultiBlocProvider وده بيستقل List مش بلوك واحد
السطر رقم 9 كدا البلوك بقي في Widget tree وهنقدر نجيبو من اي مكان باستخدام ال context
دلوقتي ال Ui محتاج تعمل Listen وهنا هنستخدم BlocBuilder
علشان تعمل Listen و Build لما يحصل حاله جديده