New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docs: Discussion - Bloc-to-Bloc communication recommendations #3816
Comments
I would also like to describe our use case (CC @Stev3nsen): we were trying to use the Connecting Blocs through Presentation approach, but encountered some problems with it. The use case looks as follows (simplified): Now we implemented a filter mechanism which allows to filter the entries by categories. The filter mechanism appears in both pages. The state of the filter is held in a bloc Both pages should use the same filter state. I.e. if I select "filter 1" on page 1, then also page 2 should apply "filter 1" automatically. The BlocListener<FilterBloc, FilterState>(
listener: (context, state) =>
context.read<ListBloc>().add(ListFilterChanged(state.filter)),
child: BlocBuilder<ListBloc, ListState>(
builder: (context, state) {
// build list with filtered items
},
),
) Map page: BlocListener<FilterBloc, FilterState>(
listener: (context, state) =>
context.read<MapBloc>().add(MapFilterChanged(state.filter)),
child: BlocBuilder<MapBloc, MapState>(
builder: (context, state) {
// build map with filtered marker items
},
),
) Which is exactly like in the example from the docs. The problem we encounter here is that the synchronisation of the Blocs now depends on the UI. We ended up sticking to an approach similar to the one mentioned by @wujek-srujek, which connects the Blocs directly, without the involvement of the UI. |
Found this article which explains connection via domain on specific example: |
Is there a reason you couldn't use a |
Hi Felix (@felangel).
First off, I'm sorry if this is not the right place for discussion, it used to work fine here but I've been away for a long time so please forgive me.
After a many months hiatus, I'm back to Flutter with a vengeance, and back to my favourite state management library. I decided to see what changed and was very surprised to read the Bloc-to-Bloc Communication section. It seems to be making statements that many people (at least in my surroundings) don't agree with, and I would like to ask you for your opinion on an approach we have been employing:
You essentially officially recommend against Bloc-to-Bloc communication, and I would consider the main reason controversial:
Where does this rule come from? If you take a look at e.g. onion or port-adapters (hexagonal) architectures, nowhere do they prohibit components in the same layer ('siblings') from talking to each other. The only 'bad'/wrong dependencies are pointing 'outside' (e.g. a repository knowing a Bloc). I have a lot of backend experience and it is also day-to-day business to call a component with business logic by another component in the same layer.
The official recommendation basically kills Bloc composition - you recommend against it - while if you consider other libraries, like
provider
orriverpod
(to name the most written-about ones recently, apart from Bloc), they actively promote it (withProxyProvider
andRef.watch(someOtherProvider)
, accordingly).The recommended solutions are not ideal:
build
method. For example, when the UI depends on the state ofBlocB
, which in turn is triggered by a change inBlocA
, you need aBlocListener<BlocA>
and aBlocBuilder<BlocB>
, where the listener is nested within the builder. (And this is just one use case, there are others.) For these reasons our team decided to completely reject this approach.My example use case is the following (silly and contrived): there is a
WeatherBloc
that gives me weather forecast (it changes as the day goes by, and the Bloc periodically calls the repository to get weather data) andActivityBloc
which suggests what I could do now.ActivityBloc
gets favourite activities for the current user from a repository, but what it suggests takes weather forecast into account - it will not suggest taking a walk if the forecast is a downpour. As you can see,ActivityBloc
could really use some help fromWeatherBloc
.(BTW, I keep saying Bloc this, Bloc that, but in reality I always start off with a
Cubit
; all that I write here still applies.)WeatherBloc
, I want to split all of this.WeatherRepository
in both Blocs, butActivityBloc
would pretty much redo a lot of logic thatWeatherBloc
already does - the polling in this case.WeatherRepository
and implements the polling, and both Blocs could use it. But this would introduce yet another layer which is not really talked about in any Flutter-related documents. It would also work only on one level of dependencies -ActivityBloc
needs forecast information - but what if some other components need to work with activity suggestions as their input? This could get pretty complex.Now the solution that I have been very successful with in a few projects - Bloc-to-Bloc communication using abstractions:
WeatherBloc
get its weather data from aWeatherRepository
and issuesWeatherState
s - pretty normal stuff.ActivitiyBloc
gets the list the user's favorite activities fromActivityRepository
, AND ALSOWeatherState
fromWeatherBloc
, intersect these two pieces of information and provideActivitySuggestions
based on that.ActivityBloc
doesn't useWeatherBloc
directly, nor vice-versa.ActivityBloc
gets aStream<WeatherState>
and when the stream emits a new event, it triggers emitting new activity suggestions.Bloc
just so happens to be able to give me aStream
, which is really neat.(I think Bloc used to immediately give the current state on subscription way back in the past, then it changed, that's why I 'prefix' the stream with the current state. In my app I have an extension for it.)
This approach been working wonderfully for me:
ActivitySuggestions
triggering a change in some other Blocs/features.)ActivityBloc
knows aboutWeatherState
- but is this bad? It does need the forecast data in some form, and it makes it possible without introducing extra code for the sake of abstractions, or more layers.StreamController
and trigger changes and test reactions.I have never had to wire the Blocs the other way round: continuing from my example above, I never needed
WeatherBloc
to knowActivityBloc
to trigger a change. In my head this would be backwards (asWeatherBloc
would need to know all of the Blocs that depend on it). I could make it work by just giving an abstractSink<WeatherState>
toWeatherBloc
, and theSink
would be a 'multicast sink' that would in turn push the new state to other Blocs, and all that would be wired when the application is constructed. But I have never needed it this way, it does sound much more complex, and wrong.I would be interested in your thoughts about our approach described in this essay. Is it a violation of some principles that don't occur to me?
Cheers.
The text was updated successfully, but these errors were encountered: