My app used to work before but stopped due to this: https://bloclibrary.dev/#/migration?id=%e2%9d%97bloc-does-not-emit-last-state-on-subscription. All is good, if this is the new way I will learn to live with it. However, it did break my application and I'm wondering what the best fix is.
The use case: a shopping list app, with 2 blocs: ShopsBloc which loads a list of Shop instances, and ShoppingItemsBloc, which provides a list of Items, but only those, that are defined for a shop (imagine a groceries store and a hardware store: I need screws and apples, so when I select my groceries store only apples pop up, and for the hardware store only screws are shown). So, to load a list of Items I get a stream of items from my repository, and also need a selected Shop to get the items available in it and use that for filtering. (This is simplified and trivialized, so please ignore the fact that all items are loaded and filtered by the bloc.)
Once the shops and items for the initially selected shop are loaded, I have an app bar with a menu to select a shop (which changes the items in the list to only those available in that shop), and also a button to toggle 'important' items. Clicking it will show only 'important' items among the items I need from the selected shop; clicking again will show all items etc.
The first half (initial shop selection, initial loading of the items, and changing the shop, which triggers item reload) works fine. The problem is clicking the 'important' filter button - items are never loaded. The reason in my implementation is that listening to ShopsBloc doesn't re-emit the state, i.e. the last selected shop.
The code:
class Item {
final String name;
final bool isImportant;
const Item({this.name, this.isImportant});
}
class Shop {
final String name;
final Set<String> items;
Shop({this.name, this.items});
}
(Won't include code for the repositories as it is trivial, it simply returns a Stream.)
This is the code that is called when a list of Items should be loaded in ShoppingItemsBloc:
void _subscribe(bool importantOnly) async {
await _subscription?.cancel();
final itemStream = _itemRepository.findAll();
final shopsSuccessStatesStream =
_shopsBloc.whereType<ShopsLoadingSuccess>();
_subscription =
Rx.combineLatest2<List<Item>, ShopsLoadingSuccess, List<Item>>(
itemStream,
shopsSuccessStatesStream,
(items, shopsSuccessState) {
final items = // irrelevant ...
...
return items;
},
).listen(
(items) => add(ShoppingItemsLoaded(items, importantOnly)),
onError: (dynamic error) => add(ShoppingItemsLoadingFailed(error)),
);
}
As listening to _shopsBloc doesn't get the current state any more, my combineLatest never emits the list, either.
What is the best way to fix this issue? My approach would be to create a concatenated Stream:
final shopsSuccessStatesStream = _shopsBloc.startWith(_shopsBloc.state).whereType<ShopsLoadingSuccess>();
, but what is the recommended best-practice solution? I imagine a lot of code is broken by this change as it affects all direct bloc subscriptions (I know it will break every other screen in the app I help implement at work, which @felangel knows very well ;d), so there also should be a recommended migration path for it, right?
Hi @wujek-srujek 馃憢
Thanks for opening an issue!
I believe this is a duplicate of https://github.com/felangel/bloc/issues/1766. Can you take a look and let me know if that helps? Thanks! 馃檹
Hi, yes, that's another option in general, but in my case, I also apply a filter so the code will not be that simple. In the meantime, I found another solution for my case: I normally don't pass BLoCs to other BLoCs if a simple Stream will suffice, so what I do now is:
ShoppingItemsBloc(
itemRepository,
shopsBloc.shareReplay(maxSize: 1),
)
I will close this issue as I guess all my questions are answered: there is no general solution and the fix needs to be considered on a case by case basis.
Most helpful comment
Hi, yes, that's another option in general, but in my case, I also apply a filter so the code will not be that simple. In the meantime, I found another solution for my case: I normally don't pass BLoCs to other BLoCs if a simple Stream will suffice, so what I do now is:
I will close this issue as I guess all my questions are answered: there is no general solution and the fix needs to be considered on a case by case basis.