استخدام BLOC مع تطبيقات Flutter

من اكثر التحديات المواجهه لمبرمجين تطبيقات Flutter هو ادارة الحالة للتطبيق “State Management” ويزداد تعقيد كلما كان التطبيق كبير ويحتوي على الكثير من الشاشات والاوامر والعمليات. ساقوم بشرح ادارة حالة التطبيق الاكثر شيوعاً والمفضلة لمطوري Flutter.

ماهو BLoC ?

اختصاراً Business Logic Component وهو من تطوير مبرمجين شركة Google والذي تم الاعالن عنه في GOOGLE I/O 2018 وهو يعتمد على Reactive Programming للتعامل مع تدفق البيانات للتطبيق .

وهو وسيط بين مصدر البيانات من ناحية وواجهات التطبيق بحيث يقوم بالتنبه للتغييرات من مصدر البيانات “كقواعد البيانات مثلاً” و يقوم بتحديثها في واجهة المستخدم

في هذا الدرس سنقوم باستخدام مكتبة تساعدنا في التعامل مع BLoc بشكل سهل وبسيط بعيداً عن تعقيدات rxDart. المكتبة مدعومة ويقوم مطورها بتحديثها بشكل كامل واضافة مميزات جديدة في كل عملية تحديث مع اصلاح الاخطاء اذا وجدت

انشاء تطبيق جديد واضافة الباكج.

انشئ تطبيق Flutter جديد باسم counter_bloc وقم باضافة الباكج في ملف pubspec.yaml.

flutter create counter_bloc
flutter create counter_bloc
name: flutter_counter
description: A new Flutter project.
version: 1.0.0+1

environment:
  sdk: ">=2.0.0-dev.68.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^0.21.0
  meta: ^1.1.6

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

التطبيق بسيط سيتكون من زرين واحد مهمته زيادة الرقم والآخر يقوم بانقاص الرقم ونص لعرض قيمة العدد.

Counter Event | اوامر

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

enum CounterEvent { increment, decrement }

لانحتاج الى استخدام اوامر معقدة في البداية حتى يتضح لك الفكرة من Bloc بعد ذلك ساقوم بشرح تطبيق اعقد باذن الله

Counter State | حالات

نلاحظ اننا هنا نتعامل مع ارقام وعلى اثر ذلك لانحتاج الى انشاء كائن للحالة.

Counter BLoC

الان سنقوم بكتابة اهم جزئية في التطبيق وهو الوسيط الذي يقوم بالتعامل مع الاوامر وتحديث الحالات على واجهة التطبيق.

class CounterBloc extends Bloc<CounterEvent, int> {
  @override
  int get initialState => 0;

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.decrement:
        yield currentState - 1;
        break;
      case CounterEvent.increment:
        yield currentState + 1;
        break;
    }
  }
}

عند البداية في كتابة الكائن المسؤول عن BLoc سيقوم باضافة دالتين الأولى هي

int get initialState => 0;

هذه الدالة تقوم باخذ الحالة المبدئية للتطبيق وفي هذه الحالة يكون 0 لان الحلات عندنا من نوع عدد صحيح للتعامل مع الداد.

@override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.decrement:
        yield currentState - 1;
        break;
      case CounterEvent.increment:
        yield currentState + 1;
        break;
    }
  } 

Stream يستمع الى التغييرات في التطبيق ويقوم بتحديث الحالة على حسب الامر المعطى فتلاحظ هناك انقاص عدد وزيادة عدد ويقوم بتحديث التطبيق عند القيام بتغيير الامر والنتيجة.

تطبيق Counter

الان بعد الانتهاء من الجزء الخاص بBLoc سنقوم باضافته مع واجهة التطبيق الخاصة بالتطبيق

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: BlocProvider<CounterBloc>(
        builder: (context) => CounterBloc(),
        child: CounterPage(),
      ),
    );
  }
} 

BlocPrivider من مكتبة flutter_bloc وضعناه في بداية التطبيق حتى يقوم بالتعامل مع جميع الوجهات في التطبيق . يفضل عدم وضع BlocProvider في بداية التطبيق الا في حالة تغيير اللغة او التصميم الخاص بالتطبيق وايضاً لعمليات التحقق من تسجيل دخول المستخدم

ايضاً لا نحتاج الى استخدام StateFullWidget مع التطبيق الخاص بنا

شاشة الـCounter

الان انتهينا من جزئ كبير من التطبيق يتبقى علينا ان نقوم بوضع الاوامر لزيادة وانقاص العدد

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final CounterBloc counterBloc = BlocProvider.of<CounterBloc>(context);

    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: BlocBuilder<CounterBloc, int>(
        builder: (context, count) {
          return Center(
            child: Text(
              '$count',
              style: TextStyle(fontSize: 24.0),
            ),
          );
        },
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {
                counterBloc.dispatch(CounterEvent.increment);
              },
            ),
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.remove),
              onPressed: () {
                counterBloc.dispatch(CounterEvent.decrement);
              },
            ),
          ),
        ],
      ),
    );
  }
}

نستطيع الوصول الى counterBloc لاننا قمنا باضافة شاشة العداد داخل BlocProvider.

استخدمنا BlocBuilder لاعادة بناء الواجة الخاصة بالتطبيق عند تغيير قيمة العداد

نستطيع اضافة bloc كبراميتر اختياري اذا لم نقم بتعريف نوع Bloc عند كتابة BlocBuilder 

معلومات اضافية

  1. يفضل لكل شاشة BLoC خاص بها.
  2. في حال لديك اكثر من Widget مرتبطة في بعض قم بفصل BLoCs واستخدام Subscribe للتبع الحالة.
  3. لاتقم بتحديث BLoC معتمد على شاشة اخرى الا مع التاكد من بقاء البيانات

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

4 أفكار عن “استخدام BLOC مع تطبيقات Flutter”

    1. السبب من استخدام BLoC هو لإدارة حالة التطبيق وفصل UI عن Logic, بحيث يكون اسهل للاختبار وأيضا للتعديل لا حقاً