Bloc: Keeping track of many properties feels difficult

Created on 5 Mar 2020  路  6Comments  路  Source: felangel/bloc

Hi!

I'm fairly new to the bloc pattern. I am having trouble keeping track of many properties in a single bloc. So far bloc feels very repetitive when there are multiple properties.

For example: (I have no app demo sorry)

I have a SearchResults bloc. In this bloc, I need to keep track of 3 different properties, regardless of the state. These 3 properties guide the GUI with what to display.

My state class looks like such

@immutable
abstract class SearchResultsState {
 final bool prop1;
 final bool prop2;
 final bool prop3;

 const SearchResultsState({@required this.prop1, @required this.prop2, @required this.prop3});
}
class Loaded({@required bool prop1, @required bool prop2, @required bool prop3}) : super(prop1: property1, prop2: prop2, prop3: prop3);

class Loading({@required bool prop1, @required bool prop2, @required bool prop3}) : super(prop1: property1, prop2: prop2, prop3: prop3);

class NoResults({@required bool prop1, @required bool prop2, @required bool prop3}) : super(prop1: property1, prop2: prop2, prop3: prop3);

First of all, is this correct? Does this make sense when I need to keep track of these 3 properties in all states? What if I have 8 properties, will I need to type out these constructers with all 8 properties?

Second, inside the bloc class itself (where I would handle the business logic), when I yield a new state and I want to continue keeping track of the properties, I would need to do this:

Stream<SearchResultsState> _mapSearchToState(Search search) async* {
 yield Loaded(prop1: state.prop1, prop2: state.prop2,  prop3: state.prop3);
 await searchProvider.search();
 yield Loaded(prop1: state.prop1, prop2: state.prop2,  prop3: state.prop3);
}

In a lot of states, those 3 properties won't change, and it seems annoying to often type out the properties in the state object

yield Loaded(prop1: state.prop1, prop2: state.prop2,  prop3: state.prop3);

How can I handle many properties in a bloc so that the bloc pattern doesn't feel like a ton of repeated code?

How would you handle this?

question

Most helpful comment

Okay, I completely understand! Thanks again, I'll keep both approaches in mind :)

Cheers.

All 6 comments

Hi @kevincherryholme 馃憢
Thanks for opening an issue!

It depends on the feature/use-case but in general I think it would simplify things to restructure your states like:

@immutable
abstract class SearchResultsState {}

class LoadInProgress extends SearchResultsState {}

class LoadFailure extends SearchResultsState {}

class LoadSuccess extends SearchResultsState {
  LoadSuccess({@required this.prop1, @required this.prop2, @required this.prop3});

  final bool prop1;
  final bool prop2;
  final bool prop3;
}

Then in your mapEventToState you would have something like:

Stream<SearchResultsState> _mapSearchToState(Search search) async* {
  yield LoadInProgress();
  try {
    final results = await searchProvider.search();
    yield LoadSuccess(prop1: results.prop1, prop2: results.prop2,  prop3: results.prop3);
  } catch (_) {
    yield LoadFailure();
  }
}

Let me know if that helps!

Hi!
@felangel

Thanks for the quick response!

So more specific states could help.

I do however still have a problem for my use case. All three states, LoadInProgress, LoadFailure and LoadSuccess require the 3 properties prop1, prop2 and prop3 because regardless of the state, I use these 3 properties to display different buttons. Does that make sense?

I use the type of state to define how to display my list of results, but I use the 3 properties to display different buttons on the same screen.

For example:

  1. When the state is LoadInProgress, my screen displays a loading indicator, but the app bar is displaying buttonA because prop1 is true.
  1. When the state changes to LoadSuccess, my screen displays a list of search results, and the app bar is still displaying buttonA because prop1 is still true.

Maybe a solution would be to create a model for the SearchResults screen, consisting of prop1, prop2 and prop3? That way I only need to manage a single object in the constructors?

If I'm not making sense please let me know, I'll do my best to create a demo :)

Hey @kevincherryholme no problem! In that case I would recommend restructuring like:

@immutable
class SearchResultsState {
  const SearchResultsState({
    @required this.prop1,
    @required this.prop2,
    @required this.prop3,
  });

  final bool prop1;
  final bool prop2;
  final bool prop3;

  SearchResultsState copyWith({
    bool prop1,
    bool prop2,
    bool prop3,
  }) {
    return SearchResultsState(
      prop1: prop1 ?? this.prop1,
      prop2: prop2 ?? this.prop2,
      prop3: prop3 ?? this.prop3,
    );
  }
}

Then in your mapEventToState you can do something like:

Stream<SearchResultsState> _mapSearchToState(Search search) async* {
  final currentState = state;
  yield currentState.copyWith(...);
  try {
    final results = await searchProvider.search();
    yield currentState.copyWith(...);
  } catch (_) {
    yield currentState.copyWith(...);
  }
}

Let me know if that helps 馃憤

Oh! Interesting, @felangel

So we do not have to stick with the abstract state class?

Is this considered standard and proper? I want to make sure I follow good techniques.

But yeah I think this helps a lot! I'll try this out.

Thank you!!

@kevincherryholme I think this approach works for properties that are inclusive (all are possible at the same time). The first approach works well when properties/states are exclusive. Both approaches are fine and have different use-cases 馃憤

Okay, I completely understand! Thanks again, I'll keep both approaches in mind :)

Cheers.

Was this page helpful?
0 / 5 - 0 ratings