Bloc وCubit في Flutter : هل شعرت يومًا أن تطبيقك أصبح معقدًا جدًا لدرجة يصعب فيها تتبّع التحديثات والبيانات؟
عندما يصل تطبيقك إلى تلك المرحلة، يصبح استخدام أدوات مثل setState() أو حتى Provider غير كافٍ.
هنا يظهر الحل السحري: Bloc — اختصارًا لـ Business Logic Component — وهو أسلوب هندسي يعيد النظام والوضوح إلى تطبيقك.
ما هو Bloc؟
نمط Bloc هو طريقة لفصل منطق الأعمال (Business Logic) عن واجهة المستخدم (UI).
أي أنه يجعل الكود منظمًا بحيث:
- الواجهة فقط تعرض البيانات.
- بينما يقوم الـ Bloc بإدارة الأحداث والتغييرات خلف الكواليس.
وبهذه الطريقة، يصبح التطبيق سهل الصيانة، قابلًا للاختبار، ويمكن لأي مطور جديد فهمه بسرعة.
Bloc وCubit: ما الفرق بينهما؟
في الواقع، Cubit هو النسخة الأخف من Bloc.
كلاهما تابعان لحزمة flutter_bloc، لكن الاختلاف في آلية العمل:
| الخاصية | Bloc | Cubit |
|---|---|---|
| يعتمد على Events و States | ✅ نعم | ❌ لا |
| أبسط في الإنشاء | ❌ أكثر تعقيدًا | ✅ أسهل |
| مناسب للمشاريع الكبيرة | ✅ جدًا | 🔸 متوسّط |
| عدد الملفات عادة أكثر | ✅ | ❌ أقل |
Bloc يستخدم أحداثًا (Events) تُرسل إليه، فيقوم بمعالجتها وإصدار حالات جديدة (States).
أما Cubit، فيُغيّر الحالة مباشرة من خلال دوال بسيطة.
تركيب Bloc: كيف يعمل؟
يمكن تخيله كدورة مستمرة:
Event → Bloc → State → UI → (تفاعل المستخدم) → Event جديد...
كل شيء يبدأ عندما يحدث حدث (Event) — مثل ضغطة زر أو تحميل بيانات —
يقوم الـ Bloc بمعالجته، ثم يُصدر حالة (State) جديدة لتنعكس على الواجهة.
مثال عملي: عدّاد باستخدام Cubit
الخطوة 1: إضافة الحزمة
dependencies:
flutter_bloc: ^8.0.0
الخطوة 2: إنشاء Cubit
import 'package:flutter_bloc/flutter_bloc.dart';
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
هنا أنشأنا Cubit يُدير عدّادًا بسيطًا ويبدأ بالقيمة 0.
الخطوة 3: ربطه بالتطبيق
void main() {
runApp(
BlocProvider(
create: (_) => CounterCubit(),
child: MyApp(),
),
);
}
الخطوة 4: استخدام الحالة في الواجهة
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Cubit Counter')),
body: Center(
child: BlocBuilder<CounterCubit, int>(
builder: (context, count) => Text(
'$count',
style: TextStyle(fontSize: 40),
),
),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () => context.read<CounterCubit>().increment(),
child: Icon(Icons.add),
),
SizedBox(width: 8),
FloatingActionButton(
onPressed: () => context.read<CounterCubit>().decrement(),
child: Icon(Icons.remove),
),
],
),
),
);
}
}
النتيجة:
واجهة مرتبة، كود واضح، وتغييرات الحالة منظمة تمامًا بدون أي فوضى.
الآن، Bloc الكامل (بالأحداث والحالات)
لنفترض أنك تريد تطبيق تسجيل دخول.
في Bloc، نقوم عادة بثلاث خطوات:
- إنشاء الأحداث (Events):
abstract class LoginEvent {}
class LoginSubmitted extends LoginEvent {
final String email;
final String password;
LoginSubmitted(this.email, this.password);
}
- إنشاء الحالات (States):
abstract class LoginState {}
class LoginInitial extends LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {}
class LoginFailure extends LoginState {
final String error;
LoginFailure(this.error);
}
- بناء الـ Bloc
class LoginBloc extends Bloc<LoginEvent, LoginState> {
LoginBloc() : super(LoginInitial()) {
on<LoginSubmitted>((event, emit) async {
emit(LoginLoading());
try {
await Future.delayed(Duration(seconds: 2)); // محاكاة طلب API
emit(LoginSuccess());
} catch (e) {
emit(LoginFailure(e.toString()));
}
});
}
}
الآن أصبح لديك نظام تسجيل دخول منظم وواضح، يمكن اختباره بسهولة دون الحاجة إلى واجهة المستخدم.
متى تختار Bloc أو Cubit؟
| الحالة | Bloc | Cubit |
|---|---|---|
| تطبيق بسيط | ❌ غير ضروري | ✅ ممتاز |
| تطبيق متوسط | 🔸 ممكن | ✅ مناسب جدًا |
| تطبيق كبير به حالات معقدة | ✅ الأفضل | 🔸 قد يكون محدودًا |
| فريق تطوير متعدد | ✅ مثالي | 🔸 أقل وضوحًا في بعض الأحيان |
إذا كنت تبني تطبيقًا صغيرًا، استخدم Cubit.
أما إن كان تطبيقك ضخمًا ويحتوي على تدفقات بيانات معقدة وأحداث متعددة، فالخيار الاحترافي هو Bloc.
لماذا Bloc مهم؟
لأنه يعلّمك التفكير المنهجي في بناء التطبيقات.
فبدلًا من أكواد متفرقة، تصبح كل تفاعلات المستخدم تمر عبر مسار واحد واضح، مما يجعل الكود قابلًا للتوسعة والتصحيح بسهولة.
إنه ليس مجرد “نمط لإدارة الحالة”، بل هو إطار تفكير هندسي يجعل مشروعك مستدامًا على المدى الطويل.
تذكّر دائمًا أن الهدف ليس استخدام الأداة الأحدث، بل اختيار النمط الذي يناسب حجم مشروعك وطبيعة بياناتك.
ففي النهاية، إدارة الحالة ليست كودًا فقط… بل أسلوب تفكير.

