Search code examples
androidflutterblocstatefulwidgetstatelesswidget

Does state as class requires SatefulWidget?


I am trying to learn the bloc pattern in flutter and creating one sample app using bloc framework. The app is a counter and has two buttons beneth the counter text field. Upon clicking on the True and False button currently Ii am just incrementing and decrementing the counter respectively.

What I have noticed is, If I use class as a State while implementing Bloc pattern then I have to write StatefulWidget. Inversely, if I use int only then even using StatelessWidget works fine.

So, it confuses me while implementing the same sample, using class as state and primitive data type as state.

Here is my code CounterBloc code with state defined as class CounterState

enum CounterEvent { Increment, Decrement }

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterState counterState = new CounterState(counter: 0);
  @override
  // TODO: implement initialState
  CounterState get initialState => counterState;

  @override
  Stream<CounterState> mapEventToState(CounterEvent event) async* {
    // TODO: implement mapEventToState

    switch (event) {
      case CounterEvent.Decrement:
        counterState.decrementCounter();
        yield counterState;

        break;

      case CounterEvent.Increment:
        counterState.incrementCounter();
        yield counterState;

        break;
    }
  }
}

My CounterState is defined as follows

class CounterState {
  int counter = 0;

  CounterState({this.counter});

  void incrementCounter() {
    counter++;
  }

  void decrementCounter() {
    counter--;
  }
}

This is how I am dispatching event on button press

onPressed: () {
  setState(() {
    counterBloc.dispatch(CounterEvent.Increment);
  }); 

Binding with UI inside StatefulWidget

 BlocBuilder<CounterBloc, CounterState>(builder: (context, snapshot) {
       .....
                child: Text(
                  snapshot.counter.toString(),
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 25.0,
                    color: Colors.white,
                  ),
                ),

     .....
 });

Now if i use the int as state then it works fine in StatelessWidget.

Here is the CounterBloc with state a integer

enum CounterEvent { Increment, Decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
  @override
  // TODO: implement initialState
  int get initialState => 0;

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    // TODO: implement mapEventToState

    switch (event) {
      case CounterEvent.Decrement:

        yield currentState -1;;

        break;

      case CounterEvent.Increment:

        yield currentState +1;;

        break;
    }
  }
}

Update

enum CounterEvent { Increment, Decrement }

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  @override
  // TODO: implement initialState
  CounterState get initialState => CounterState(counter: 0);

  @override
  Stream<CounterState> mapEventToState(CounterEvent event) async* {
    // TODO: implement mapEventToState

    switch (event) {
      case CounterEvent.Decrement:
        CounterState counterState = CounterState(counter: currentState.counter);
        counterState.decrementCounter();
        yield counterState;

        break;

      case CounterEvent.Increment:
        CounterState counterState = CounterState(counter: currentState.counter);
        counterState.incrementCounter();

        yield counterState;

        break;
    }
  }
}

have resolved the issue with the help of @Rémi Rousselet's comment. Now I am passing the new state everytime


Solution

  • The issue is not about whether it's a class or not, but about immutability.

    Using bloc, your state must be immutable. You should not modify the previous state, but instead, create a new modified state.