Setting up BLoC Architecture for Flutter Applications
BLoC (Business Logic Component) is the officially recommended state management pattern from the Flutter team. Based on event and state streams: UI sends Event, BLoC processes it and emits State. The flutter_bloc library from Felix Angelov has become standard in large Flutter projects.
How BLoC Works in Practice
// Events
abstract class ProfileEvent {}
class ProfileLoaded extends ProfileEvent {
final String userId;
ProfileLoaded(this.userId);
}
class ProfileRefreshed extends ProfileEvent {}
// States
abstract class ProfileState {}
class ProfileInitial extends ProfileState {}
class ProfileLoading extends ProfileState {}
class ProfileSuccess extends ProfileState {
final UserProfile profile;
ProfileSuccess(this.profile);
}
class ProfileFailure extends ProfileState {
final String error;
ProfileFailure(this.error);
}
// BLoC
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final UserRepository _repository;
ProfileBloc(this._repository) : super(ProfileInitial()) {
on<ProfileLoaded>(_onLoaded);
on<ProfileRefreshed>(_onRefreshed);
}
Future<void> _onLoaded(ProfileLoaded event, Emitter<ProfileState> emit) async {
emit(ProfileLoading());
try {
final profile = await _repository.getProfile(event.userId);
emit(ProfileSuccess(profile));
} catch (e) {
emit(ProfileFailure(e.toString()));
}
}
}
In a widget:
BlocBuilder<ProfileBloc, ProfileState>(
builder: (context, state) {
return switch (state) {
ProfileLoading() => const CircularProgressIndicator(),
ProfileSuccess(:final profile) => ProfileView(profile: profile),
ProfileFailure(:final error) => ErrorView(error: error),
_ => const SizedBox(),
};
},
)
BLoC vs Cubit
Cubit is a simplified BLoC without Events. Cubit methods are called directly: profileCubit.load(userId). Suitable for simple screens. BLoC with Events provides an event log and tests better with complex logic involving multiple event sources.
What We Configure
Project structure: lib/features/profile/bloc/, data/, domain/. Connect flutter_bloc, equatable (for proper state comparison). Register BLoCs via MultiBlocProvider at app root or BlocProvider at route level. Configure BlocObserver for global event and error logging.
Testing via bloc_test:
blocTest<ProfileBloc, ProfileState>(
'emits [Loading, Success] when ProfileLoaded is added',
build: () => ProfileBloc(mockRepository),
act: (bloc) => bloc.add(ProfileLoaded('user-123')),
expect: () => [ProfileLoading(), ProfileSuccess(tProfile)],
);
Timeline
Setting up BLoC architecture from scratch: 2–3 days. Refactoring setState project: 1–2 weeks. Cost — after analysis.







