From 16ea5ea56338ee9c0bd4b514bb2a99c24912d837 Mon Sep 17 00:00:00 2001 From: avashilling <165050625+avashilling@users.noreply.github.com> Date: Mon, 30 Jun 2025 15:49:42 -0400 Subject: [PATCH] Progress indicators rise and fade when increased for more emphasis --- .../analytics_summary/progress_indicator.dart | 99 +++++++++++++++++-- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/lib/pangea/analytics_summary/progress_indicator.dart b/lib/pangea/analytics_summary/progress_indicator.dart index 130ad0650..9ecc74f1f 100644 --- a/lib/pangea/analytics_summary/progress_indicator.dart +++ b/lib/pangea/analytics_summary/progress_indicator.dart @@ -1,6 +1,5 @@ -import 'package:flutter/material.dart'; - import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart'; +import 'package:flutter/material.dart'; /// A badge that represents one learning progress indicator (i.e., construct uses) class ProgressIndicatorBadge extends StatelessWidget { @@ -30,13 +29,9 @@ class ProgressIndicatorBadge extends StatelessWidget { ), const SizedBox(width: 6.0), !loading - ? Text( - points.toString(), - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: indicator.color(context), - ), + ? _AnimatedFloatingNumber( + number: points, + indicator: indicator, ) : const SizedBox( height: 8, @@ -50,3 +45,89 @@ class ProgressIndicatorBadge extends StatelessWidget { ); } } + +class _AnimatedFloatingNumber extends StatefulWidget { + final int number; + final ProgressIndicatorEnum indicator; + final Duration duration; + + const _AnimatedFloatingNumber({ + required this.number, + required this.indicator, + this.duration = const Duration(milliseconds: 900), + }); + + @override + State<_AnimatedFloatingNumber> createState() => + _AnimatedFloatingNumberState(); +} + +class _AnimatedFloatingNumberState extends State<_AnimatedFloatingNumber> + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _fadeAnim; + late Animation _offsetAnim; + int? _lastNumber; + int? _floatingNumber; + + @override + void initState() { + super.initState(); + _controller = AnimationController(vsync: this, duration: widget.duration); + _fadeAnim = CurvedAnimation(parent: _controller, curve: Curves.easeOut); + _offsetAnim = Tween( + begin: const Offset(0, 0), + end: const Offset(0, -0.7), + ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut)); + _lastNumber = widget.number; + } + + @override + void didUpdateWidget(covariant _AnimatedFloatingNumber oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.number > _lastNumber!) { + _floatingNumber = widget.number; + _controller.forward(from: 0.0).then((_) { + setState(() { + _lastNumber = widget.number; + _floatingNumber = null; + }); + }); + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final TextStyle indicatorStyle = TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: widget.indicator.color(context), + ); + return Stack( + alignment: Alignment.center, + children: [ + if (_floatingNumber != null) + SlideTransition( + position: _offsetAnim, + child: FadeTransition( + opacity: ReverseAnimation(_fadeAnim), + child: Text( + "$_floatingNumber", + style: indicatorStyle, + ), + ), + ), + Text( + widget.number.toString(), + style: indicatorStyle, + ), + ], + ); + } +}