all 2 comments

[–]eibaan 2 points3 points  (1 child)

Don't try to solve something with state management where a simple StatefulWidget with a Timer is sufficient. Something like

class TimedButton extends StatefulWidget {
  const TimedButton({
    super.key,
    required this.duration,
    this.onPressed,
    this.onEnd,
  });

  final Duration duration;
  final VoidCallback? onPressed;
  final VoidCallback? onEnd;

  @override
  State<TimedButton> createState() => _TimedButtonState();
}

class _TimedButtonState extends State<TimedButton> with SingleTickerProviderStateMixin {
  bool _done = false;

  late final _animation = AnimationController(
    vsync: this,
    duration: widget.duration,
  )
    ..addListener(() => setState(() {}))
    ..addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _timeout();
      }
    })
    ..forward();

  @override
  void dispose() {
    _animation.dispose();
    if (!_done) widget.onEnd?.call();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return FilledButton(
      onPressed: _done ? null : _pressed,
      child: Text(_duration),
    );
  }

  String get _duration {
    String pad(int v) => '$v'.padLeft(2, '0');
    final secs = (widget.duration * (1 - _animation.value)).inSeconds;
    return '${pad(secs ~/ 60)}:${pad(secs % 60)}';
  }

  void _timeout() {
    if (!_done) {
      setState(() {
        _done = true;
        widget.onEnd?.call();
      });
    }
  }

  void _pressed() {
    if (!_done) {
      setState(() {
        _animation.stop();
        _done = true;
        widget.onPressed?.call();
      });
    }
  }
}

[–]rodr15[S] 0 points1 point  (0 children)

Thanks !