
Flutter offers various state management solutions, and one of the most powerful is the Bloc (Business Logic Component) pattern, which is based on event-driven state management. Bloc is widely used because it helps separate business logic from UI, making applications scalable and testable.
In this blog, we will explore:
- What Bloc and Cubit are.
- The difference between Bloc and Cubit.
- How to implement Bloc and Cubit in a Flutter project.
- Three full example codes demonstrating Bloc and Cubit.
What is Bloc?
Bloc is a state management library that helps manage complex states efficiently using events and streams. It follows a unidirectional data flow:
- Event: A user action (e.g., button click) triggers an event.
- Bloc: The event is processed in the business logic layer, and a new state is emitted.
- State: The UI listens for changes and updates accordingly.
What is Cubit?
Cubit is a simplified version of Bloc that does not use events. Instead, it directly emits new states. It is ideal for simpler state management scenarios.
To use Bloc and Cubit, first add the dependency to your pubspec.yaml
file:
dependencies: flutter: sdk: flutter flutter_bloc: ^8.1.3
Then, run:
flutter pub get
Example 1: Counter Using Cubit
This is a simple example of how Cubit works by managing a counter state.
import 'package:flutter/material.dart'; 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); } void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( create: (context) => CounterCubit(), child: MaterialApp(home: CounterPage()), ); } } class CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Cubit Counter")), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ BlocBuilder<CounterCubit, int>( builder: (context, count) { return Text('$count', style: TextStyle(fontSize: 40)); }, ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ FloatingActionButton( onPressed: () => context.read<CounterCubit>().increment(), child: Icon(Icons.add), ), SizedBox(width: 20), FloatingActionButton( onPressed: () => context.read<CounterCubit>().decrement(), child: Icon(Icons.remove), ), ], ), ], ), ), ); } }
Example 2: Counter Using Bloc
This example demonstrates how Bloc handles state changes with events.
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; abstract class CounterEvent {} class Increment extends CounterEvent {} class Decrement extends CounterEvent {} class CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0); @override Stream<int> mapEventToState(CounterEvent event) async* { if (event is Increment) { yield state + 1; } else if (event is Decrement) { yield state - 1; } } } void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( create: (context) => CounterBloc(), child: MaterialApp(home: CounterPage()), ); } } class CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Bloc Counter")), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ BlocBuilder<CounterBloc, int>( builder: (context, count) { return Text('$count', style: TextStyle(fontSize: 40)); }, ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ FloatingActionButton( onPressed: () => context.read<CounterBloc>().add(Increment()), child: Icon(Icons.add), ), SizedBox(width: 20), FloatingActionButton( onPressed: () => context.read<CounterBloc>().add(Decrement()), child: Icon(Icons.remove), ), ], ), ], ), ), ); } }
Example 3: Todo App with Bloc
This example demonstrates how Bloc can be used to manage a to-do list application.
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class TodoCubit extends Cubit<List<String>> { TodoCubit() : super([]); void addTask(String task) => emit([...state, task]); void removeTask(String task) => emit(state.where((t) => t != task).toList()); } void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( create: (context) => TodoCubit(), child: MaterialApp(home: TodoPage()), ); } } class TodoPage extends StatelessWidget { final TextEditingController controller = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Todo List")), body: Column( children: [ TextField(controller: controller), ElevatedButton( onPressed: () { context.read<TodoCubit>().addTask(controller.text); controller.clear(); }, child: Text("Add Task"), ), Expanded( child: BlocBuilder<TodoCubit, List<String>>( builder: (context, tasks) { return ListView.builder( itemCount: tasks.length, itemBuilder: (context, index) { return ListTile( title: Text(tasks[index]), trailing: IconButton( icon: Icon(Icons.delete), onPressed: () => context.read<TodoCubit>().removeTask(tasks[index]), ), ); }, ); }, ), ), ], ), ); } }
Leave a Comment