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
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.