I'm having trouble to understend why my Stateful widget is not updating the state after a rebuild. I have a stateful widget responsable for decrementing a counter each second, so it recieves a initial value, I pass this initial value to the State and start decrementing it.
Also it has a button that when pressed sends a event to my bloc which rebuilds the stateful widget with a new initial value, the thing is, it indeed sends and update the initial value, but the counter continue decrementing the old value and I have no clue why. Here a example of the code:
Widget
void main() => runApp(
BlocProvider(
create: (context) => TBloc(),
child: MaterialApp(
home: Scaffold(
body: BlocBuilder<TBloc, BState>(
builder: (context, state) {
if (state is BState) {
return Column(
children: [
Counter(state.value),
FlatButton(
child: Text("tap"),
onPressed: () =>
BlocProvider.of<TBloc>(context).add(BEvent(200)),
),
],
);
}
return Container();
},
),
),
),
),
);
class Counter extends StatefulWidget {
final int initialValue;
Counter(this.initialValue);
@override
CounterState createState() => CounterState(this.initialValue);
}
class CounterState extends State<Counter> {
Timer timer;
int value;
CounterState(this.value);
@override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() => --value);
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("Value: $value"),
Text("Initial: ${widget.initialValue}")
],
);
}
}
Bloc
class TBloc extends Bloc<BEvent, BState> {
@override
BState get initialState => BState(100);
@override
Stream<BState> mapEventToState(BEvent event) async* {
yield BState(event.value);
}
}
class BEvent extends Equatable {
final int value;
BEvent(this.value);
@override
List<Object> get props => [value];
}
class BState extends Equatable {
final int value;
BState(this.value);
@override
List<Object> get props => [value];
}
Thank you in advance.
That's because you are only setting your Timer
on your Counter
's initState
which is called only once per widget lifecycle.
Basically, when your Counter
is first built, it will call initState()
and as long as it remains in that widget tree, only didUpdateWidget()
and/or didChangeDependencies()
will be called in order to update that widget (eg. when rebuilt with new parameters, just like you're doing).
The same happens for dispose()
method, which is the opposite of initState()
called only once but this time, when the widget is being removed from the tree.
Also, you are using states the wrong way here. You don't need to pass the parameters to the state itself (like you are doing by:
CounterState createState() => CounterState(this.initialValue);
but instead, you can access all parameters from that widget by calling: widget.initialValue
.
So, with all of that said, basically, what you'll want to do is to update your Counter
widget based on the new updates, like so:
class Counter extends StatefulWidget {
final int initialValue;
Counter(this.initialValue);
@override
CounterState createState() => CounterState();
}
class CounterState extends State<Counter> {
Timer timer;
int value;
void _setTimer() {
value = widget.initialValue;
timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() => --value);
});
}
@override
void initState() {
super.initState();
_setTimer();
}
@override
void didUpdateWidget(Counter oldWidget) {
super.didUpdateWidget(oldWidget);
if(oldWidget.initialValue != widget.initialValue) {
_setTimer();
}
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("Value: $value"),
Text("Initial: ${widget.initialValue}")
],
);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.