Block Pattern

Part 3 – Flutter BLoC Tutorial: Core Concepts, Events, States & BLoC Widgets Explained

📘 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),
            ),
          ],
        ),
      ),
    );
  }
}
  

counter bloc


🚀 Coming Up Next:

“Creating a Full Counter App using Events and BLoC” — Let’s implement everything in one real app!

Leave a Reply

Your email address will not be published. Required fields are marked *