Bloc: Using streams in mapEventToState

Created on 25 Feb 2019  路  11Comments  路  Source: felangel/bloc

Hello,

I have a question regarding the best approach for working with streams in BLoC. Below is one specific use case, but I was wondering about similar approaches in other use cases.

What I am trying to do:

  1. When user clicks a button with one of his addresses on screen, I want to dispatch an event SelectAddress.
  2. In BLoC I want to map this event with State. I am receiving Address object (latitude, longitude and etc.) with an SelectAddress event, pass it to GeoFlutterFire library method and get Stream<List<Store>>.
  3. After receiving data, I want to update State with new values from List<Store>.
  4. After State changes, I change view (showing user locations nearby).

Where I am stuck
First part works, second and third does not. Last part is OK.

I have a repository, which returns Stream<List<Store>> from Firestore (plugin is https://pub.dartlang.org/packages/geoflutterfire), but this stream emits at least 8-9 events, before it returns list with documents.
I am trying to listen to this stream in mapEventToState, but StreamSubscription does not work at all:

    if (event is SelectAddress) {
      yield state.copyWith(isLoading: true);
      try {
        StreamSubscription streamSubscription;
        Stream<List<Store>> _storeListStream =
            _databaseInterface.getNearbyStores(event.deliveryAddress, 5);
        streamSubscription = _storeListStream.listen((stores) async* {
          if (stores.isNotEmpty) {
            yield state.copyWith(
              isLoading: false,
              deliveryAddress: event.deliveryAddress,
              isAddressSelected: true,
              listOfStores: stores,
            );
          } else {
            yield state.copyWith(isLoading: false);
          }
        });
      } catch (error) {
        yield state.copyWith(isLoading: false, error: error);
      }
    }

When using await for method, I cannot pass new events to this bloc after SelectAddress event:

    if (event is SelectAddress) {
      yield state.copyWith(isLoading: true);
      try {
        Stream<List<Store>> _storeListStream =
            _databaseInterface.getNearbyStores(event.deliveryAddress, 5);
        await for (List<Store> stores in _storeListStream) {
          if (stores.isNotEmpty)
            yield state.copyWith(
              isLoading: false,
              deliveryAddress: event.deliveryAddress,
              isAddressSelected: true,
              listOfStores: stores,
            );
        }
      } catch (error) {
        yield state.copyWith(isLoading: false, error: error);
      }
    }

I have a feeling I am not using the right approach.
Any help would be appreciated.

question

Most helpful comment

Yep, My issue is solved.
I'm using your solution with Todo sample.
My only "pb" was your solution to manage the updatedTodos
You only update this list in the state TodosLoaded
I was not sure if "else" cases can occurs

All 11 comments

Hi @algirdasmac thanks for opening an issue!

Right off the bat, I wouldn't recommend creating a subscription directly in mapEventToState because mapEventToState gets called every time an event is dispatched. I would instead move all of the logic for getting nearby stores into a StoreRepository and just expose a public getNearbyStores which returns a Future of nearby stores. Then in mapEventToState you can do something like:

if (event is SelectAddress) {
      yield state.copyWith(isLoading: true);
      try {
        List<Store> stores =
            await _storeRepository.getNearbyStores(event.deliveryAddress, 5);
        yield state.copyWith(
              isLoading: false,
              deliveryAddress: event.deliveryAddress,
              isAddressSelected: true,
              listOfStores: stores,
            );
      } catch (error) {
        yield state.copyWith(isLoading: false, error: error);
      }
}

If this answer doesn't make sense then:

  • Are you able to give me read-only access to the repo?

    • If so, I can take a look and potentially open a PR to fix the issue.

    • If not, it'd be really helpful if you could create a simple app that demonstrates the problem you're having.

Let me know if that helps! 馃憤

Thanks @felangel - you are always as fast as it can be - impressive!
Although this answer makes sense, it's just not what I am looking for. Unfortunately I can't give access to the repo (company policy), but I created simple flutter app with the situation:
https://github.com/algirdasmac/streams_and_blocs

In /lib/todo_bloc/todo_bloc.dart I try to take stream results and add them to state. What I would try to do later is to listen to Firestore collection and show todo's to user (in case other user added some todo's).

I hope this is clear. If it is not - please do not hesitate to ask and I will try to explain even more. Thanks.

PS. I am looking more of an advice here, it should not be considered as an issue.

@algirdasmac no problem haha! I'll take a look shortly and will try to open a PR with some suggestion. 馃憤

@algirdasmac just opened a PR to connect the bloc to Firestore. Let me know if that helps!

Marking this as closed since it's more of a discussion at this point 馃槃

So simple and so effective. It is strange to me that I didn't think about it myself. Thanks @felangel !

I had exactly the same pb and this PR help me.
Question: This sample is using the previous State management implementation without "Equatable".

How easy it is to use the new Equatable solution to manage the state variables (todos) ?
Can you please update the sample ?

I have some difficulties to manage the different class to easily manage the variable (todos)

@fvisticot what sample are you referring to? Examples/flutter_todos?

I'm referring to https://github.com/algirdasmac/streams_and_blocs
I try to do the "same" thing with Firebase / Stream integration inside a bloc.
Would like to find a simple solution to manage Firebase data from Stream and Bloc/State

My "pb" is to manage the data (List) between the different state classes

It's OK with todo sample code. Tx for your help.

@fvisticot Just want to confirm, you were able to solve your issue?

Yep, My issue is solved.
I'm using your solution with Todo sample.
My only "pb" was your solution to manage the updatedTodos
You only update this list in the state TodosLoaded
I was not sure if "else" cases can occurs

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zjjt picture zjjt  路  30Comments

windinvip picture windinvip  路  39Comments

sawankumarbundelkhandi picture sawankumarbundelkhandi  路  108Comments

felangel picture felangel  路  32Comments

hahai96 picture hahai96  路  40Comments