Understanding the Differences Between Bloc and Riverpod State Management in Flutter with Examples

Shivam Kumar Nayak
5 min readOct 18, 2023

--

flutter

Introduction:

In the world of Flutter app development, choosing the right state management solution is crucial. It directly impacts your app’s efficiency and maintainability. Two popular state management options in the Flutter ecosystem are “Bloc” and “Riverpod.” In this article, we’ll delve into these two approaches, highlighting their differences and providing practical examples.

What is Bloc State Management?

Bloc, short for Business Logic Component, is a popular state management library for Flutter. It employs a unidirectional data flow pattern and is built on Dart’s stream API. Bloc ensures a clear separation between the UI layer and the business logic, making it a strong choice for scalable applications.

Key Features of Bloc:

  1. Unidirectional Data Flow: Bloc enforces a strict one-way data flow from the UI to the business logic and back to the UI, ensuring an organized structure.
  2. Reactive Programming: Utilizing Dart’s Stream API, Bloc handles asynchronous data effectively.
  3. Event-Driven Architecture: In Bloc, you initiate events (user interactions, data fetching, etc.) that the Bloc processes to produce new states.
  4. Strong Typing: Bloc is strongly typed, providing compile-time safety and reducing the risk of runtime errors.

What is Riverpod State Management?

Riverpod is another state management solution in the Flutter world, known for its simplicity and ease of use. It builds on the Provider package and offers a different perspective compared to Bloc.

Key Features of Riverpod:

  1. Provider-Based: Riverpod is founded on the Provider package and enables efficient dependency management and global state sharing.
  2. Declarative Syntax: Riverpod promotes a declarative style, making it easier to define and manage your app’s state and dependencies.
  3. No Boilerplate: Riverpod reduces boilerplate code, resulting in a more concise and expressive codebase.
  4. Global State Management: It simplifies the sharing and management of global states throughout your app.

Differences Between Bloc and Riverpod:

  1. Data Flow:
  • Bloc follows a unidirectional data flow with events and states.
  • Riverpod offers a more direct and declarative approach to state management.

2. Complexity:

  • Bloc provides a structured and complex architecture.
  • Riverpod offers a simpler and less boilerplate-heavy approach.

3. Dependency Management:

  • Bloc might require additional packages or manual setup for dependency management.
  • Riverpod seamlessly integrates with the Provider package for straightforward dependency management.

4. Global State Handling:

  • Bloc focuses primarily on individual widget state management.
  • Riverpod streamlines global state sharing and management.

Practical Examples:

Example 1: Counter App with Bloc

  1. Bloc Setup: Before using Bloc, you’ll need to set up the package in your Flutter project. You can do this by adding flutter_bloc to your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.0.0 # Use the latest version

Then, run flutter pub get to fetch the package.

2. Create the CounterBloc:

import 'package:flutter_bloc/flutter_bloc.dart';

// Define the possible events for the counter
enum CounterEvent { increment, decrement }

// Create a Bloc class for the counter
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0);

@override
Stream<int> mapEventToState(CounterEvent event) async* {
if (event == CounterEvent.increment) {
// Increase the counter when 'increment' event is received
yield state + 1;
} else if (event == CounterEvent.decrement) {
// Decrease the counter when 'decrement' event is received
yield state - 1;
}
}
}

Explanation:

  • In this example, we create a CounterBloc class that extends Bloc<CounterEvent, int>. This means it handles events of type CounterEvent and emits states of type int.
  • The initial state of the counter is set to 0.
  • The mapEventToState method defines how events affect the state. When the 'increment' event is received, it increments the counter's state by 1, and when 'decrement' is received, it decrements the counter's state by 1.

3. Using the Bloc in the UI:

Now, let’s use the CounterBloc in the UI.

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

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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (context) => CounterBloc(),
child: MyHomePage(),
),
);
}
}

class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counterBloc = BlocProvider.of<CounterBloc>(context);

return Scaffold(
appBar: AppBar(
title: Text('Counter App with Bloc'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Counter Value:',
),
BlocBuilder<CounterBloc, int>(
builder: (context, state) {
return Text(
'$state',
style: TextStyle(fontSize: 24),
);
},
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
onPressed: () {
counterBloc.add(CounterEvent.increment);
},
child: Icon(Icons.add),
),
SizedBox(width: 20),
FloatingActionButton(
onPressed: () {
counterBloc.add(CounterEvent.decrement);
},
child: Icon(Icons.remove),
),
],
),
],
),
),
);
}
}

Explanation:

  • In the UI, we use the BlocProvider to provide the CounterBloc to the widget tree. This makes the CounterBloc accessible to the MyHomePage widget.
  • We use BlocBuilder to rebuild the UI whenever the state in CounterBloc changes. This keeps the counter value in sync with the UI.
  • The two FloatingActionButton widgets allow us to trigger the 'increment' and 'decrement' events when clicked. This demonstrates how events in Bloc can update the UI.

Example 2: Counter App with Riverpod

  1. Riverpod Setup: Before using Riverpod, add it to your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
riverpod: ^0.15.0 # Use the latest version

Then, run flutter pub get to fetch the package.

2. Create the Counter Provider:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = StateProvider<int>((ref) => 0);

void incrementCounter() {
ref.read(counterProvider).state++;
}

void decrementCounter() {
ref.read(counterProvider).state--;
}

Explanation:

  • In this example, we define a counterProvider using Riverpod's StateProvider. It initializes the counter with a value of 0.
  • We also create functions incrementCounter and decrementCounter to increment and decrement the counter, respectively.

3. Using Riverpod in the UI:

Now, let’s use the counterProvider in the UI.

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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
home: MyHomePage(),
),
);
}
}

class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App with Riverpod'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Counter Value:',
),
Consumer(
builder: (context, watch, child) {
final counter = watch(counterProvider);

return Text(
'${counter.state}',
style: TextStyle(fontSize: 24),
);
},
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
onPressed: () {
incrementCounter();
},
child: Icon(Icons.add),
),
SizedBox(width: 20),
FloatingActionButton(
onPressed: () {
decrementCounter();
},
child: Icon(Icons.remove),
),
],
),
],
),
),
);
}
}

Explanation:

  • In the UI, we use ProviderScope to create a scope for the Riverpod providers.
  • We access the counterProvider using the Consumer widget, which listens to changes in the provider's state and automatically updates the UI.
  • The two FloatingActionButton widgets call the incrementCounter and decrementCounter functions when clicked, which in turn updates the state managed by Riverpod.

Conclusion:

Both Bloc and Riverpod are effective state management options for Flutter applications, but they come with different philosophies and complexities. Bloc provides a structured, event-driven architecture, while Riverpod simplifies state management with a declarative syntax and global state handling. Your choice depends on your project’s needs, team familiarity, and personal preferences. Understanding these differences empowers you to make an informed decision and create efficient and maintainable Flutter apps.

--

--

Shivam Kumar Nayak

Passionate software developer crafting elegant solutions to empower users and drive technological innovation.