简体   繁体   中英

Stateful Widget not rebuilding after BLOC state change

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM