📘 3. Core Concepts in Flutter BLoC
🔁 Events and States
🧩 What is an Event?
An Event represents an action or intention from the user or system, such as a button press, form submission, or a page scroll.
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
📦 What is a State?
A State is the current status of your app’s UI or data. When events are triggered, the state changes and UI updates.
abstract class CounterState {}
class CounterInitial extends CounterState {
final int count = 0;
}
class CounterValue extends CounterState {
final int count;
CounterValue(this.count);
}
🆚 BLoC Class vs Cubit Class
🔷 Cubit (Simplified)
Use Cubit when your logic is simple and you want direct control over the state.
class CounterCubit extends Cubit {
CounterCubit() : super(0);
void increment() => emit(state + 1);
}
🔶 BLoC (Event-driven)
Use BLoC when your logic needs to handle multiple events and conditions.
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on((event, emit) => emit(state + 1));
on((event, emit) => emit(state - 1));
}
}
🧱 BlocProvider
BlocProvider
injects your BLoC or Cubit into the widget tree so that it can be accessed by child widgets.
BlocProvider(
create: (context) => CounterBloc(),
child: MyHomePage(),
)
🔁 BlocBuilder
Rebuilds a widget based on the new state.
BlocBuilder<CounterBloc, int>(
builder: (context, state) {
return Text('Count: $state');
},
)
🎧 BlocListener
Listens for state changes to perform side effects like showing SnackBars, dialogs, or navigation — without rebuilding the UI.
BlocListener<CounterBloc, int>(
listener: (context, state) {
if (state == 5) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Reached 5!')),
);
}
},
child: MyHomePage(),
)
🔀 BlocConsumer
Combines both BlocBuilder
and BlocListener
.
BlocConsumer<CounterBloc, int>(
listener: (context, state) {
if (state == 10) {
print('Hit milestone!');
}
},
builder: (context, state) {
return Text('Counter: $state');
},
)
🧠 Summary Table
Concept | Purpose |
---|---|
Event | Action from user/system |
State | Current status of the app |
Cubit | Simpler, direct state logic |
BLoC | Handles multiple event types |
BlocProvider | Injects BLoC/Cubit |
BlocBuilder | Rebuilds UI on state |
BlocListener | Performs side effects |
BlocConsumer | Combines Builder + Listener |
📦 Full Example Structure
lib/
├── bloc/
│ ├── counter_bloc.dart
│ ├── counter_event.dart
│ ├── counter_state.dart
├── main.dart
📄 counter_event.dart
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
📄 counter_state.dart
abstract class CounterState {
final int count;
const CounterState(this.count);
}
class CounterInitial extends CounterState {
CounterInitial() : super(0);
}
class CounterValue extends CounterState {
const CounterValue(int count) : super(count);
}
⚙️ counter_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_event.dart';
import 'counter_state.dart';
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterInitial()) {
on((event, emit) {
emit(CounterValue(state.count + 1));
});
on((event, emit) {
emit(CounterValue(state.count - 1));
});
}
}
📱 main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/counter_bloc.dart';
import 'bloc/counter_event.dart';
import 'bloc/counter_state.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter BLoC Counter',
home: BlocProvider(
create: (_) => CounterBloc(),
child: CounterPage(),
),
);
}
}
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counterBloc = context.read();
return Scaffold(
appBar: AppBar(title: Text('🔁 BLoC Counter Example')),
body: Center(
child: BlocConsumer<CounterBloc, CounterState>(
listener: (context, state) {
if (state.count == 5) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('🎉 You reached 5!')),
);
}
if (state.count == 10) {
debugPrint('🚀 You hit 10!');
}
},
builder: (context, state) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Current Count:', style: TextStyle(fontSize: 24)),
Text('${state.count}', style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold)),
],
);
},
),
),
floatingActionButton: Padding(
padding: const EdgeInsets.only(left: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
FloatingActionButton(
heroTag: 'decrement',
onPressed: () => counterBloc.add(DecrementEvent()),
child: Icon(Icons.remove),
),
FloatingActionButton(
heroTag: 'increment',
onPressed: () => counterBloc.add(IncrementEvent()),
child: Icon(Icons.add),
),
],
),
),
);
}
}
🚀 Coming Up Next:
“Creating a Full Counter App using Events and BLoC” — Let’s implement everything in one real app!