GIfatahTH / states_rebuilder

a simple yet powerful state management technique for Flutter
494 stars 56 forks source link

Exhaustive switch over ReactiveModel's Future-related fields #30

Closed ResoDev closed 4 years ago

ResoDev commented 4 years ago

Thank you very much for the library, I can't believe how polished and easy to use it is!

Seeing that you added isIdle and isWaiting in 1.9.0, I thought it would benefit us all to have a Kotlin-like when expression. Currently, I'm using a custom extension which looks like this:

extension ReactiveModelX<T> on ReactiveModel<T> {
  R whenConnectionState<R>({
    @required R Function() idle,
    @required R Function() waiting,
    @required R Function(T reactiveModel) data,
    @required R Function(dynamic error) error,
  }) {
    if (this.isIdle) {
      return idle();
    } else if (this.isWaiting) {
      return waiting();
    } else if (this.hasData) {
      return data(this.state);
    } else /*if (hasError)*/ {
      return error(this.error);
    }
  }
}

Exhaustive "when" expressions are extremely useful in a StateBuilder where they save keystrokes.

Compare the current way:

StateBuilder<WeatherStore>(
  models: [Injector.getAsReactive<WeatherStore>()],
  builder: (context, reactiveModel) {
    if (reactiveModel.isIdle) {
      return buildInitialInput();
    } else if (reactiveModel.isWaiting) {
      return buildLoading();
    } else if (reactiveModel.hasData) {
      return buildColumnWithData(context, reactiveModel.state.weather);
    } else /*if (reactiveModel.hasError)*/ {
      return buildInitialInput();
    }
  },
),

And the proposed solution:

StateBuilder<WeatherStore>(
  models: [Injector.getAsReactive<WeatherStore>()],
  builder: (context, reactiveModel) {
    return reactiveModel.whenConnectionState(
      idle: () => buildInitialInput(),
      waiting: () => buildLoading(),
      data: (store) => buildColumnWithData(context, store.weather),
      error: (_) => buildInitialInput(),
    );
  },
),
GIfatahTH commented 4 years ago

Thank you very much for the proposal and I appreciate your comment and above all, I appreciate your contribution to the Flutter community, and I do not give up any secrets if I tell you that I have refined my library several times after watching your video tutorials.

Yes, whenConnectionState is a cool idea and I think that it makes states_rebuilder even easier.

I create the v-1.10.0 branch where I implemented the state whenConnectionState and tested it. I changed the names of the parameters to start with "on" because they are events and I think it is more practical.

  Widget whenConnectionState({
    @required Widget Function() onIdle,
    @required Widget Function() onWaiting,
    @required Widget Function(T state) onData,
    @required Widget Function(dynamic error) onError,
  }) {
    if (this.isIdle) {
      return onIdle();
    }
    if (this.hasError) {
      return onError(this.error);
    }
    if (this.isWaiting) {
      return onWaiting();
    }
    return onData(this.state);
  }

BTW, with the conventional way of the if-else sequence, there is no need to go through the four states. In your example, you can simply

StateBuilder<WeatherStore>(
  models: [Injector.getAsReactive<WeatherStore>()],
  builder: (context, reactiveModel) {
     if (reactiveModel.isWaiting) {
      return buildLoading();
    } 

   if (reactiveModel.hasData) {
      return buildColumnWithData(context, reactiveModel.state.weather);
    } 

      return buildInitialInput();

  },
),

I want to draw your attention to the fact that the onError callback here doesn't treat the error as a side effect, it must return a widget.

To treat the error as a side effect, it is more appropriate to use the onError of the setState method. it takes BuildContext and error as a parameter. The BuildContext is used to display the alert dialog, the snack bar or for navigation.

ResoDev commented 4 years ago

I'm glad you find value in my tutorials!

My thinking was set in the BLoC pattern where you need to check through all the states. Thanks for bringing up the less verbose if-else statement.

Because of your amazing documentation and articles, I'm already using onError in setState for side effects.

Created a PR with generics for the method: https://github.com/GIfatahTH/states_rebuilder/pull/31