Flutter Cubit: Simplified State Management with BLoC Pattern
Last Sync: Today
flutterBeginner
Flutter Cubit: Simplified State Management with BLoC Pattern
If you're looking for a simple yet powerful state management solution in Flutter, Cubit is an excellent choice. It's a lightweight alternative to full Bloc, using methods instead of events to emit states. This guide will teach you everything you need to know to start using Cubit in your Flutter apps.
What is Cubit?
Cubit is a simplified version of the BLoC pattern. It extends BlocBase and uses methods to emit states. You define a state class, a Cubit class, and then call methods that emit new states. There are no events to define, making it perfect for simple to moderate complexity flows.
Let's build a simple counter Cubit. First, define the state class (immutable) and then the Cubit.
DARTRead-only
1
import'package:equatable/equatable.dart';classCounterStateextendsEquatable{
final int value;constCounterState(this.value);
@override
List<Object?>getprops=>[value];}
Cubit can also handle asynchronous work. Use try-catch and emit loading, success, and error states.
DARTRead-only
1
abstract classDataStateextendsEquatable{}classDataInitialextendsDataState{}classDataLoadingextendsDataState{}classDataLoadedextendsDataState{
final String data;constDataLoaded(this.data);
@override List<Object?>getprops=>[data];}classDataErrorextendsDataState{
final String message;constDataError(this.message);
@override List<Object?>getprops=>[message];}classDataCubitextendsCubit<DataState>{DataCubit():super(DataInitial());
Future<void>fetchData() async {emit(DataLoading());try{
final result =await api.getData();emit(DataLoaded(result));}catch(e){emit(DataError(e.toString()));}}}
Side Effects with BlocListener
Use BlocListener to perform side effects (navigation, snackbars) without rebuilding the UI.
DARTRead-only
1
BlocListener<DataCubit, DataState>(listener:(context, state){if(state is DataError){
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content:Text(state.message)),);}},child:...);
Cubit vs Bloc: When to Use Cubit
Simple UI state – counters, toggles, forms, etc.
No need for event transformation – debouncing, throttling, or complex stream operations.
Prefer minimal boilerplate – just methods, no event classes.
Small to medium apps where complexity is low.
Best Practices
Use Equatable for states – Prevents unnecessary rebuilds.
Keep states immutable – Always create new state instances.
Place BlocProvider at the appropriate level – Higher for shared cubits, lower for scoped ones.
Use BlocBuilder only where needed – Granular rebuilds improve performance.
Handle loading/error states – Always provide feedback to the user.
Keep business logic in the cubit – UI should only call methods and react to states.
Common Mistakes
❌ Mutating state properties – Leads to bugs and no UI updates.
✅ Always create a new state with emit.
❌ Calling emit after cubit is closed – Throws an error.
✅ Check isClosed before emitting if needed.
❌ Creating BlocProvider inside build – Recreates the cubit on every rebuild, losing state.
✅ Use BlocProvider outside build.
❌ Not using Equatable – Causes unnecessary rebuilds because states are never considered equal.
✅ Extend Equatable and list props.
❌ Using BlocBuilder for side effects – Can cause infinite loops.
✅ Use BlocListener.
Testing Cubit
Cubits are easy to test. Just create an instance, call methods, and assert states.
DARTRead-only
1
test('increment increases count',(){
final cubit =CounterCubit();
cubit.increment();expect(cubit.state,CounterState(1));});
Conclusion
Cubit offers a simple, intuitive way to manage state in Flutter apps. With its method-based approach and integration with flutter_bloc, you can build reactive UIs with minimal boilerplate. Start with Cubit, and when your app grows, you can always introduce Bloc for advanced flows.
Which widget is used to provide a Cubit to the widget tree?
A
BlocBuilder
B
BlocProvider
C
BlocListener
D
CubitProvider
Q3
of 3
Why is Equatable recommended for Cubit states?
A
It makes states serializable
B
It prevents unnecessary UI rebuilds by enabling value-based equality
C
It allows copyWith methods
D
It automatically emits states
Frequently Asked Questions
Is Cubit a replacement for Bloc?
No, Cubit is a simplified version. Bloc offers more features like event transformation, debouncing, and advanced stream handling. Choose Cubit for simpler cases, Bloc for complex ones.
Do I need to use Equatable with Cubit?
It's highly recommended. Without it, BlocBuilder may rebuild even when the state hasn't logically changed, because the default == compares references.
Can I use Cubit with multiple screens?
Yes, provide the Cubit at a higher level in the widget tree (e.g., using BlocProvider in a parent widget) so that all child screens can access it.
How do I handle async operations in Cubit?
Define a method that is async, use try-catch, and emit loading, success, and error states at appropriate times.
How do I test a Cubit that uses async methods?
Use expectLater or await the method call and then check the final state. You can also use blocTest from the bloc_test package, which works for both Cubit and Bloc.