import 'package:flutter/material.dart'; class AnimatedCountText extends StatefulWidget { const AnimatedCountText({ super.key, required this.value, required this.formatter, this.style, this.duration = const Duration(milliseconds: 900), this.curve = Curves.easeOutCubic, this.animateFromZero = true, }); final double value; final String Function(double) formatter; final TextStyle? style; final Duration duration; final Curve curve; final bool animateFromZero; @override State createState() => _AnimatedCountTextState(); } class _AnimatedCountTextState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; late CurvedAnimation _curve; double _currentValue = 0; @override void initState() { super.initState(); _currentValue = widget.animateFromZero ? 0 : widget.value; _controller = AnimationController(vsync: this, duration: widget.duration); _curve = CurvedAnimation(parent: _controller, curve: widget.curve); _controller.addListener(_handleTick); _configureAnimation(from: _currentValue, to: widget.value); if (_currentValue != widget.value) { _controller.forward(); } } @override void didUpdateWidget(covariant AnimatedCountText oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.curve != widget.curve) { _curve = CurvedAnimation(parent: _controller, curve: widget.curve); _configureAnimation(from: _currentValue, to: widget.value); } if (oldWidget.value != widget.value) { _controller.duration = widget.duration; _configureAnimation(from: _currentValue, to: widget.value); _controller.forward(from: 0); } } void _configureAnimation({required double from, required double to}) { _animation = Tween(begin: from, end: to).animate(_curve); } void _handleTick() { setState(() => _currentValue = _animation.value); } @override void dispose() { _controller.removeListener(_handleTick); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Text( widget.formatter(_currentValue), style: widget.style, ); } }