Answer
Overview
Cubit is a simplified version of BLoC — both are from the
text
flutter_blocBLoC — Event-Driven
BLoC requires: Events → Bloc → States
dart// Events abstract class CounterEvent {} class Increment extends CounterEvent {} class Decrement extends CounterEvent {} class Reset extends CounterEvent {} // BLoC class CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { on<Increment>((event, emit) => emit(state + 1)); on<Decrement>((event, emit) => emit(state - 1)); on<Reset>((event, emit) => emit(0)); } } // Dispatch events context.read<CounterBloc>().add(Increment());
Cubit — Method-Driven (No Events)
Cubit skips the event layer — you call methods directly:
dart// Cubit — no events needed class CounterCubit extends Cubit<int> { CounterCubit() : super(0); void increment() => emit(state + 1); void decrement() => emit(state - 1); void reset() => emit(0); } // Call methods directly context.read<CounterCubit>().increment();
Key Differences
| Feature | BLoC | Cubit |
|---|---|---|
| Events | ✅ Required | ❌ No events |
| Boilerplate | High (events + states + bloc) | Low (just methods + states) |
| Traceability | ✅ Full of event history | ❌ Less traceable |
| Complex async | ✅ text | ✅ Methods with async |
| Event transformation | ✅ text | ❌ Manual |
| Best for | Complex flows, many triggers | Simple state changes |
Same Widget Code for Both
dart// BlocBuilder works the same for both! BlocBuilder<CounterBloc, int>( builder: (context, count) => Text('$count'), ) BlocBuilder<CounterCubit, int>( builder: (context, count) => Text('$count'), )
When to Use Which
| Use Case | Recommendation |
|---|---|
| Counter, toggle, loading flag | Cubit |
| Simple CRUD list | Cubit |
| Login with multiple states | BLoC or Cubit |
| Complex checkout flow with 5+ event types | BLoC |
| Need event transformation (debounce, concurrency) | BLoC |
| Background sync with multiple triggers | BLoC |
Real-World Example — Auth with Both
dart// As Cubit class AuthCubit extends Cubit<AuthState> { AuthCubit() : super(AuthInitial()); Future<void> login(String email, String password) async { emit(AuthLoading()); try { final user = await authRepo.signIn(email, password); emit(AuthSuccess(user)); } catch (e) { emit(AuthFailure(e.toString())); } } } // As BLoC — same logic but with event types class AuthBloc extends Bloc<AuthEvent, AuthState> { AuthBloc() : super(AuthInitial()) { on<LoginRequested>(_onLogin); on<LogoutRequested>(_onLogout); on<GoogleSignInRequested>(_onGoogleSignIn); // Easier to manage 3+ actions separately as events } }
Rule of thumb: Start with Cubit. Migrate to BLoC if you need event transformation, concurrency control, or complex multi-trigger logic. Both share the same widget API — migration is easy.