animated_progress_bar.dart 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import 'package:flutter/material.dart';
  2. /// 支持动画过渡和渐变色的进度条
  3. // 使用示例
  4. // AnimatedGradientProgressBar(
  5. // targetValue: 0.75,
  6. // gradient: LinearGradient(colors: [Colors.orange, Colors.red]),
  7. // height: 10,
  8. // borderRadius: 5,
  9. // )
  10. class AnimatedGradientProgressBar extends StatefulWidget {
  11. // 目标进度值(0~1)
  12. final double targetValue;
  13. // 动画时长
  14. final Duration duration;
  15. // 渐变色
  16. final Gradient gradient;
  17. // 进度条高度
  18. final double height;
  19. // 圆角半径
  20. final double borderRadius;
  21. const AnimatedGradientProgressBar({
  22. super.key,
  23. required this.targetValue,
  24. required this.duration,
  25. required this.gradient,
  26. this.height = 10.0,
  27. this.borderRadius = 7.0,
  28. });
  29. @override
  30. AnimatedGradientProgressBarState createState() =>
  31. AnimatedGradientProgressBarState();
  32. }
  33. class AnimatedGradientProgressBarState
  34. extends State<AnimatedGradientProgressBar>
  35. with TickerProviderStateMixin {
  36. late AnimationController _controller;
  37. late Animation<double> _animation;
  38. @override
  39. void initState() {
  40. super.initState();
  41. _controller = AnimationController(duration: widget.duration, vsync: this);
  42. _animation = CurvedAnimation(parent: _controller, curve: Curves.easeInOut);
  43. _startAnimation();
  44. }
  45. @override
  46. void didUpdateWidget(AnimatedGradientProgressBar oldWidget) {
  47. super.didUpdateWidget(oldWidget);
  48. if (oldWidget.targetValue != widget.targetValue) {
  49. _resetAnimation();
  50. _startAnimation();
  51. }
  52. }
  53. @override
  54. void dispose() {
  55. // 移除监听
  56. _animation.removeListener(() {});
  57. _controller.dispose();
  58. super.dispose();
  59. }
  60. void _startAnimation() {
  61. print('进度条 => 初始动画值: ${_animation.value}'); // 0.0
  62. // 先移除旧监听
  63. _animation.removeListener(() {});
  64. // 创建新的 Tween 动画
  65. _animation = Tween<double>(
  66. begin: 0,
  67. end: widget.targetValue,
  68. ).animate(_animation);
  69. // 添加监听器并启动动画
  70. _animation.addListener(() => setState(() {}));
  71. _controller.forward();
  72. Future.delayed(widget.duration, () {
  73. print('进度条 => 最终动画值: ${_animation.value}');
  74. });
  75. }
  76. void _resetAnimation() => _controller.reset();
  77. @override
  78. Widget build(BuildContext context) {
  79. return Stack(
  80. // 允许子组件溢出自己本身的大小,默认是裁切的
  81. clipBehavior: Clip.none,
  82. children: [
  83. // 百分比文本
  84. Positioned(
  85. right: 0,
  86. // 文字位于进度条上方
  87. bottom: widget.height + 4,
  88. child: AnimatedBuilder(
  89. animation: _animation,
  90. builder: (context, _) {
  91. return Text(
  92. '${(_animation.value * 100).toStringAsFixed(0)}%',
  93. style: const TextStyle(fontSize: 12, color: Colors.black54),
  94. );
  95. },
  96. ),
  97. ),
  98. // 进度条
  99. ClipRRect(
  100. borderRadius: BorderRadius.circular(widget.borderRadius),
  101. child: Container(
  102. width: double.infinity,
  103. height: widget.height,
  104. decoration: BoxDecoration(
  105. color: Colors.grey[200],
  106. borderRadius: BorderRadius.circular(widget.borderRadius),
  107. ),
  108. child: Stack(
  109. children: [
  110. AnimatedBuilder(
  111. animation: _animation,
  112. builder: (context, _) {
  113. return FractionallySizedBox(
  114. widthFactor: _animation.value,
  115. alignment: Alignment.centerLeft,
  116. child: Container(
  117. decoration: BoxDecoration(
  118. gradient: widget.gradient,
  119. borderRadius: BorderRadius.circular(
  120. widget.borderRadius,
  121. ),
  122. ),
  123. ),
  124. );
  125. },
  126. ),
  127. ],
  128. ),
  129. ),
  130. ),
  131. ],
  132. );
  133. }
  134. }