Simplifying Async Functionality with Flutter Bloc (Cubit) State Management
In this article, we'll explore how to implement asynchronous functionality using Flutter Bloc and Cubit, after previously using Riverpod 2.0 for state management. We'll cover the key concepts of Flutter Bloc, subtitles, and detailed context to help you easily integrate async functionality into your Flutter projects.
What is Flutter Bloc and Cubit?
Flutter Bloc is a reactive state management library for Flutter, based on the BLoC (Business Logic Component) pattern. It helps manage state efficiently and facilitates clean architecture in Flutter applications. Cubit, a simpler version of Bloc, is recommended for simpler use cases. Both Flutter Bloc and Cubit aim to provide a powerful solution for state management, especially for handling asynchronous operations in a reactive way.
Getting Started with Flutter Bloc (Cubit)
To use Flutter Bloc (Cubit) for state management in your Flutter project, you need to add the bloc package for Flutter as a dependency. Add the following to your pubspec.yaml file:
dependencies:
flutter_bloc: ^8.0.1
Run flutter pub get to fetch the package. Create a new folder named bloc in your lib directory, and start defining your blocs/cubits inside this folder for better organization.
Implementing Async Functionality with Flutter Bloc (Cubit)
Employing async functionality in Flutter projects using Flutter Bloc (Cubit) involves merely a few straightforward steps. Firstly, declare your Stream or Future within the bloc/cubit. Secondly, dispatch events from your UI to communicate with the bloc/cubit. Lastly, use the BlocBuilder and BlocListener widgets in your UI to listen, react, and display the updated state from the bloc/cubit efficiently.
Creating a Bloc or Cubit
In our bloc/cubit, we need to define an event and a state for our async functionality. Let's assume a simple use-case where we fetch data from an API and display it in our UI upon completion.
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
abstract class FetchDataEvent {}
class FetchData extends FetchDataEvent {}
class FetchDataSuccess extends FetchDataEvent {}
class FetchDataFailure extends FetchDataEvent {}
class DataBloc extends Bloc {
DataBloc() : super(DataInitial());
@override
Stream mapEventToState(FetchDataEvent event) async* {
switch (event.runtimeType) {
case FetchData:
yield DataLoading();
try {
// Perform your async data fetching here
// Once successful, yield a success state
yield FetchDataSuccess();
} catch (e) {
// In case of an error, yield a failure state
yield FetchDataFailure();
}
break;
default:
throw UnimplementedError();
}
}
}
enum DataState { initial, loading, success, failure }
We can follow a similar pattern for the Cubit:
import 'dart:async';
import 'package:bloc/bloc.dart';
abstract class FetchDataState {}
class DataInitial extends FetchDataState {}
class DataLoading extends FetchDataState {}
class DataSuccess extends FetchDataState {
final String data;
DataSuccess(this.data);
}
class DataFailure extends FetchDataState {}
class DataCubit extends Cubit {
DataCubit() : super(DataInitial());
Future<void> fetchData() async {
emit(DataLoading());
try {
// Perform your async data fetching here
// Once successful, emit a success state
emit(DataSuccess('Data received!'));
} catch (e) {
emit(DataFailure());
}
}
}
UI Integration
For clean UI integration, use BlocBuilder for reactive UI updates and BlocListener for side-effects. In the example below, a button dispatches the FetchData event, and BlocBuilder updates the UI based on the response.
BlocBuilder<DataBloc, DataState>(
builder: (context, state) {
if (state is DataInitial) {
return Text('Initial State');
}
if (state is DataLoading) {
return CircularProgressIndicator();
}
if (state is FetchDataSuccess) {
return Text('Data Received!');
}
if (state is FetchDataFailure) {
return Text('Failed to fetch data');
}
return Text('Unknown state');
},
),
- Flutter Bloc (Cubit) is a powerful state management solution for Flutter, ideally for handling asynchronous operations.
- Add the
flutter_blocpackage and organize your blocs/cubits in a separate folder for a clean architecture. - Implement async functionality through emitting, listening, and reacting to
StreamorFuture-based events. - Use
BlocBuilderfor UI updates andBlocListenerfor side-effects, ensuring efficient and reactive Flutter UIs.