5

So a have a stateful parent with a timer value. This timer can be reset by the user. I also have a child stateful widget with an animation controller. The parent passes the timer value to this child to reset the animation. Here is some code:

class _ParentState extends State<Parent> {
  int timer = 20;
  
  void _userInput() {
    setState(() {
      timer = 20;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Child(
      time: timer
    );
  }
}

Note: this is striped down for simplicity

So when my user triggers the _userInput method, the timer value gets changed but the child doesn't. Here is my full child widget:

class TimerProgress extends StatefulWidget {
  TimerProgress({
    Key key,
    @required this.time,
    @required this.onCompleted
  }) : super(key: key);

  final int time;
  final Function onCompleted;

  @override
  _TimerProgressState createState() => _TimerProgressState();
}

class _TimerProgressState extends State<TimerProgress> with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<double> _widthAnimation;

  @override
  void initState() {
    _controller = AnimationController(
      duration: Duration(seconds: widget.time),
      vsync: this
    );

    _widthAnimation = Tween<double>(
      begin: 200,
      end: 0
    ).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.linear
    ));

    _controller.addListener(() {
      if (_controller.value == 1) {
        widget.onCompleted();
      }
    });

    _controller.forward();

    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ...
  }
}

So this all works, but ideally I want the animation to be reset, which doesn't work...

Herbie Vine
  • 1,643
  • 3
  • 17
  • 32

1 Answers1

4

initState runs only once per state instance.

Implement resetting the time inside didUpdateWidget: https://api.flutter.dev/flutter/widgets/RawScrollbarState/didUpdateWidget.html

You can't (cause it will cause recursive loop, setState will trigger didUpdateWidget again) and shouldn't (after this method is invoked, build is being called) have to call setState inside this method.

Runnable example: https://www.dartpad.dev/848976603f866f4e3aa6030aba8addf7?null_safety=true

enter image description here

eeqk
  • 3,492
  • 1
  • 15
  • 22
  • I tried implementing this. Didn't work. Am I doing this incorrectly? ` AnimationController _controller; Animation _widthAnimation; int duration; //new @override void initState() { _controller = AnimationController( duration: Duration(seconds: duration), vsync: this ); // same as before super.initState(); } void didUpdateWidget(old) { duration = widget.time; super.didUpdateWidget(old); } ` – Herbie Vine Apr 02 '21 at 22:09
  • 1
    I've added an example on dartpad, please have a look. – eeqk Apr 03 '21 at 12:35
  • Still can't get it to work... I made a dartpad: https://www.dartpad.dev/4e05042e7954e29d7a79110b67617f89?null_safety=true. I want to reset the timer when the parent state changes – Herbie Vine Apr 03 '21 at 13:06
  • 1
    https://www.dartpad.dev/8a3390f3e4a0770546c69e20081ac624?null_safety=true – eeqk Apr 03 '21 at 14:12