SEGVeenstra/washington

Suggestion to make state listener/builder-functions more specific.

Opened this issue · 1 comments

I'm thinking about making the functions used in StateListener and StateBuilder more specific.

Currently the API for StateBuilder is like this:

StateBuilder<TState, TValue> {
  successBuilder: (context, successState) {
    // successState only contains a value because it will only be called
    // when there are no errors and isLoading is set to false.
    final currentValue = state.value; 
    return ...
  },
  loadingBuilder: (context, loadingState) {
    // loadingState also only contains a value because you already know
    // that isLoading would be true. Or else this function wouldn't be called.
    final currentValue = state.value; 
    return ...
  },
  errorBuilder: (context, errorState) {
    // errorState contains value, isLoading and the error.
    final currentValue = state.value;
    final isLoading = state.isLoading;
    final error = state.error;
    return ...
  },
  child: ...
}

The reason for this was that even though you have an error or are loading new data, you still might want to show the current value.

Proposal 1

My proposal is to remove the extra information from these builders so the API becomes more clear to use.

The idea is that you'd use the default StateBuilder constructor for more top-level decisions like "which widget should I show". When you want to base your ui on a combination of states (showing data while isLoading for example) you can use the StateBuilder.single constructor which gives you all these properties.

Removing these extra information would make the API look like:

StateBuilder<TState, TValue> {
  successBuilder: (context, value) {
    return LoadedScreen(value);
  },
  loadingBuilder: (context) {
    return LoadingScreen();
  },
  errorBuilder: (context, error) {
    return MyErrorScreen(error);
  },
  child: ...
}

When you do need all the information you can use the single constructor which gives you more control.

StateBuilder<TState, TValue>.single {
  builder: (context, state) {
    return MyPage(
      value: state.value,
      isLoading: state.isLoading,
      error: state.error,
    );
  },
  child: ...
}

Proposal 2

Just get rid of the wrapping state-objects.

StateBuilder<TState, TValue> {
  successBuilder: (context, value) {
    return LoadedScreen(value);
  },
  loadingBuilder: (context, value) {
    return LoadingScreen(value);
  },
  errorBuilder: (context, value, isLoading, error) {
    return MyErrorScreen(error);
  },
  child: ...
}