When BLoCs Start Talking Too Much đź§©
(and how the Mediator Pattern can save you)
Sometimes in a Flutter app, one BLoC starts depending on another. Maybe the profile screen needs the current user ID from the AuthBloc. So we inject one BLoC into the other. It works. Until it doesn’t.
Suddenly, one small change in authentication breaks your profile screen. And your presentation layer is no longer independent.
The Real Problem
When BLoCs depend on each other, they stop being reusable. Your app becomes a network of small dependencies that are hard to trace. You can’t easily test one without the other. And the more features you add, the more they all start to pull on the same thread.
The Mediator Pattern
But the good news is that there’s a better way, it’s called the Mediator Pattern. The idea is simple:
Two components never talk directly. They talk through a mediator.
In Flutter, that “mediator” can be a repository or service that knows how to fetch or combine data from multiple sources.
Instead of this:
// ❌ BAD: BLoCs depend on each other
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final AuthBloc authBloc;
ProfileBloc(this.authBloc) : super(ProfileInitial()) {
on<LoadProfile>((event, emit) async {
final userId = authBloc.state.userId; // tight coupling!
final profile = await api.getProfile(userId);
emit(ProfileLoaded(profile));
});
}
}
Do this:
// âś… GOOD: Both BLoCs use the same repository
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final UserRepository userRepository;
ProfileBloc(this.userRepository) : super(ProfileInitial()) {
on<LoadProfile>((event, emit) async {
final user = await userRepository.getCurrentUser();
emit(ProfileLoaded(user));
});
}
}
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final UserRepository userRepository;
AuthBloc(this.userRepository) : super(AuthInitial()) {
on<Login>((event, emit) async {
final user = await userRepository.login(event.credentials);
emit(Authenticated(user));
});
}
}
Now both AuthBloc and ProfileBloc stay independent, they just use the same UserRepository as their common ground.
Why It Matters
- You stop creating dependencies between BLoCs.
- Your code becomes easier to test.
- The flow stays clear: UI → Bloc → Repository → Data.
- And when the app grows, you can refactor without fear.
My Rule of Thumb
Whenever I feel tempted to connect one BLoC to another, I ask myself:
“Could this logic live in a repository instead?”
If the answer is yes I move it there. That’s how I keep BLoCs focused on what they do best: managing UI state. Nothing more.
Closing Thought
Good architecture is like good conversation: everyone speaks, but no one interrupts. That’s the spirit of the Mediator Pattern.