flutter
/

Setting Up Bloc in Flutter: Installation and Basic Configuration

Last Sync: Today

On this page

11
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Setting Up Bloc in Flutter: Installation and Basic Configuration

If you're new to Flutter state management, Bloc is one of the most powerful and scalable approaches. In this guide, you'll learn how to install and set up Bloc step by step with a simple working example.

How Bloc Works (Quick Overview)

Bloc separates your UI from business logic. The UI sends events, the Bloc processes them and produces states, and the UI reacts to those states. This makes your code predictable and testable.

What You'll Need

Before installing the BLoC package, make sure you have a Flutter project created. You can create one using flutter create my_app. Then open the project in your favourite editor.

Step 1: Add Dependencies

Open your pubspec.yaml file and add the following dependencies:

YAMLRead-only
1
dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^9.0.0
  equatable: ^2.0.0    # Optional but highly recommended

Then run flutter pub get in your terminal to fetch the packages.

Step 2: Create Your First Bloc

Create a folder structure like this to keep your code organised:

TEXTRead-only
1
lib/
├── bloc/
│   └── counter/
│       ├── counter_bloc.dart
│       ├── counter_event.dart
│       └── counter_state.dart
DARTRead-only
1
import 'package:equatable/equatable.dart';

abstract class CounterEvent extends Equatable {
  const CounterEvent();

  @override
  List<Object> get props => [];
}

class CounterIncrementPressed extends CounterEvent {}

class CounterDecrementPressed extends CounterEvent {}
DARTRead-only
1
import 'package:equatable/equatable.dart';

class CounterState extends Equatable {
  final int value;

  const CounterState(this.value);

  @override
  List<Object> get props => [value];
}
DARTRead-only
1
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_event.dart';
import 'counter_state.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(const CounterState(0)) {
    on<CounterIncrementPressed>((event, emit) {
      emit(CounterState(state.value + 1));
    });

    on<CounterDecrementPressed>((event, emit) {
      emit(CounterState(state.value - 1));
    });
  }
}

Step 3: Provide the Bloc

Wrap your widget tree with BlocProvider to make the bloc available. Usually this is done in main.dart or in a dedicated App widget.

DARTRead-only
1
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/counter/counter_bloc.dart';
import 'home_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Bloc Demo',
      home: BlocProvider(
        create: (_) => CounterBloc(),
        child: const HomePage(),
      ),
    );
  }
}

Step 4: Use the Bloc in the UI

In your HomePage, use BlocBuilder to rebuild when the state changes, and context.read<CounterBloc>() to add events.

DARTRead-only
1
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/counter/counter_bloc.dart';
import 'bloc/counter/counter_event.dart';

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Bloc Counter')),
      body: Center(
        child: BlocBuilder<CounterBloc, CounterState>(
          builder: (context, state) {
            return Text('Value: ${state.value}', style: const TextStyle(fontSize: 24));
          },
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => context.read<CounterBloc>().add(CounterIncrementPressed()),
            child: const Icon(Icons.add),
          ),
          const SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => context.read<CounterBloc>().add(CounterDecrementPressed()),
            child: const Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

Optional: Add Bloc Observer for Logging

You can enable logging of all bloc events and states by creating a simple observer and setting it in main.dart.

DARTRead-only
1
import 'package:flutter_bloc/flutter_bloc.dart';

class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');
  }
}

// In main() before runApp:
// Bloc.observer = SimpleBlocObserver();

Bloc vs Cubit

Cubit is a simpler version of Bloc that doesn't use events. You call methods directly on the Cubit to emit states. Use Cubit for simple state management and Bloc for complex flows with many event types.

Real-World Usage

The counter example is a starting point. Here’s how Bloc is used in real apps:

  • Login form – validation, loading state, error messages
  • API calls – fetch data with loading and error handling
  • Shopping cart – add/remove items, calculate total
  • Multi‑step forms – preserve state across screens

Common Mistakes

  • ❌ Forgetting Equatable – states and events won't be compared correctly, causing unnecessary rebuilds.
  • ❌ Emitting the same state instance – UI won’t update if the state object is identical.
  • ❌ Creating Bloc inside build – use BlocProvider or BlocProvider.value to avoid recreation.
  • ❌ Using the wrong BuildContext – ensure you have access to the correct provider.
  • ❌ Not handling loading/error states – users need feedback during async operations.

What's Next?

Now that you have a working Bloc setup, explore more advanced topics:

  • BlocConsumer – combine builder and listener in one widget.
  • BlocProvider.value – share an existing bloc with a new subtree.
  • Testing – use bloc_test to write unit tests for your blocs.
  • Architecture – see how to structure larger apps with Bloc.

Next, learn how Bloc architecture helps structure large applications.

Test Your Knowledge

Q1
of 3

Which package is used to simplify state equality in Bloc?

A
provider
B
equatable
C
flutter_hooks
D
get_it
Q2
of 3

What is the purpose of BlocProvider?

A
To create a new bloc instance
B
To make a bloc available to the widget tree
C
To listen to state changes
D
To emit states
Q3
of 3

Which widget should you use to rebuild the UI when the state changes?

A
BlocProvider
B
BlocListener
C
BlocBuilder
D
BlocConsumer

Frequently Asked Questions

Do I need Equatable for Bloc in Flutter?

No, but it’s highly recommended. Without Equatable, you must override == and hashCode manually to avoid unnecessary rebuilds. Equatable simplifies that and is the standard practice in the Bloc community.

Can I use Bloc with Cubit in the same Flutter app?

Yes, flutter_bloc includes both Bloc and Cubit. Cubit is a simpler state management approach without events. You can mix them in the same app, using Bloc for complex flows and Cubit for simpler ones.

How do I handle dependencies (like a repository) inside a bloc?

Pass them via the constructor. For example, MyBloc(this.repository). Then use dependency injection (like get_it) to provide the repository when creating the bloc inside BlocProvider.

Why is my BlocBuilder not rebuilding in Flutter?

Check that your state class extends Equatable (or overrides == and hashCode) and that you emit new state objects. Also ensure you are using the correct BlocProvider context and that the bloc instance is not being recreated.

Previous

bloc architecture

Next

bloc cubit vs bloc

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.