Using Bloc for Complex State Management in Flutter

Introduction

State management is a critical aspect of building scalable and maintainable Flutter applications. When dealing with complex state logic, Bloc (Business Logic Component) is one of the most efficient and structured solutions. Bloc is based on the Cubit and Bloc patterns, making it a powerful choice for managing app states effectively.

In this article, we will dive deep into Bloc and Cubit, understand their differences, and explore how to implement them in a Flutter project with practical examples.

What is Bloc?

Bloc is a predictable state management library that helps separate business logic from UI, following the BLoC (Business Logic Component) pattern. It allows developers to manage state using Streams and Events, ensuring a unidirectional data flow.

Key Features of Bloc

  • Separation of Concerns: Keeps business logic separate from UI.
  • Scalability: Well-suited for large applications.
  • Testability: Easily test business logic with unit tests.
  • Event-Driven: UI reacts based on events.
  • Unidirectional Data Flow: Ensures consistent state transitions.

What is Cubit?

Cubit is a simplified version of Bloc that removes the complexity of events and instead relies on direct state manipulation. It is part of the flutter_bloc package and is ideal for simple state management.

Key Features of Cubit

  • Simpler than Bloc: Does not require events, only state changes.
  • Less Boilerplate: Ideal for managing simple UI state.
  • Easy to Use: Uses functions to update state instead of handling events.

Bloc vs. Cubit: Understanding the Difference

FeatureBlocCubit
Uses Events?✅ Yes❌ No
Uses Streams?✅ Yes✅ Yes
ComplexityHighLow
BoilerplateMoreLess
Best ForComplex appsSimple state changes

Installing Bloc and Cubit in a Flutter Project

To get started, add the following dependency to your pubspec.yaml:

dependencies:
  flutter_bloc: ^8.0.0
  equatable: ^2.0.3

Run:

flutter pub get

Implementing Cubit with Equatable in Flutter

Let’s start with a simple counter example using Cubit.

Creating a Counter Cubit with State

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';

// Defining State
class CounterState extends Equatable {
  final int counter;
  const CounterState(this.counter);

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

// Creating Cubit
class CounterCubit extends Cubit<CounterState> {
  CounterCubit() : super(const CounterState(0));

  void increment() => emit(CounterState(state.counter + 1));
  void decrement() => emit(CounterState(state.counter - 1));
}

Using Cubit in a Widget

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_cubit.dart';

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Cubit Counter")),
      body: Center(
        child: BlocBuilder<CounterCubit, CounterState>(
          builder: (context, state) {
            return Text('Counter: ${state.counter}', style: TextStyle(fontSize: 24));
          },
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => context.read<CounterCubit>().increment(),
            child: Icon(Icons.add),
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => context.read<CounterCubit>().decrement(),
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

Implementing Bloc with Equatable in Flutter

For more complex state management, let’s use Bloc.

Creating a Counter Bloc with Events and State

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';

// Defining Events
abstract class CounterEvent extends Equatable {
  @override
  List<Object> get props => [];
}

class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

// Defining State
class CounterState extends Equatable {
  final int counter;
  const CounterState(this.counter);

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

// Creating Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(const CounterState(0)) {
    on<Increment>((event, emit) => emit(CounterState(state.counter + 1)));
    on<Decrement>((event, emit) => emit(CounterState(state.counter - 1)));
  }
}

Using Bloc in a Widget

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_bloc.dart';

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

Conclusion

Bloc and Cubit provide powerful and scalable solutions for Flutter state management. Using Equatable helps optimize state comparisons, improving performance.

Key Takeaways:

  • Cubit is a simpler alternative to Bloc, requiring less boilerplate.
  • Bloc is best for complex state management using events.
  • Equatable ensures efficient state comparison, reducing unnecessary rebuilds.

Call to Action

If you’re building a large Flutter app, Bloc is a must-learn. Start implementing Bloc or Cubit today and optimize your state management! Follow our blog for more in-depth tutorials.

More on State Management in Flutter: