| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- import 'package:flutter/material.dart';
- import 'bubble/bubble_widget.dart';
- /// 支持动画过渡和渐变色的进度条
- // 使用示例
- // AnimatedGradientProgressBar(
- // targetValue: 0.75,
- // gradient: LinearGradient(colors: [Colors.orange, Colors.red]),
- // height: 10,
- // borderRadius: 5,
- // )
- class AnimatedGradientProgressBar extends StatefulWidget {
- // 目标进度值(0~1)
- final double targetValue;
- // 动画时长
- final Duration duration;
- // 渐变色
- final Gradient gradient;
- // 进度条高度
- final double height;
- // 圆角半径
- final double borderRadius;
- const AnimatedGradientProgressBar({
- super.key,
- required this.targetValue,
- required this.duration,
- required this.gradient,
- this.height = 10.0,
- this.borderRadius = 7.0,
- });
- @override
- AnimatedGradientProgressBarState createState() =>
- AnimatedGradientProgressBarState();
- }
- class AnimatedGradientProgressBarState
- extends State<AnimatedGradientProgressBar>
- with TickerProviderStateMixin {
- late AnimationController _controller;
- late Animation<double> _animation;
- @override
- void initState() {
- super.initState();
- _controller = AnimationController(duration: widget.duration, vsync: this);
- _animation = CurvedAnimation(parent: _controller, curve: Curves.easeInOut);
- _startAnimation();
- }
- @override
- void didUpdateWidget(AnimatedGradientProgressBar oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (oldWidget.targetValue != widget.targetValue) {
- _resetAnimation();
- _startAnimation();
- }
- }
- @override
- void dispose() {
- // 移除监听
- _animation.removeListener(() {});
- _controller.dispose();
- super.dispose();
- }
- void _startAnimation() {
- print('进度条 => 初始动画值: ${_animation.value}'); // 0.0
- // 先移除旧监听
- _animation.removeListener(() {});
- // 创建新的 Tween 动画
- _animation = Tween<double>(
- begin: 0,
- end: widget.targetValue,
- ).animate(_animation);
- // 添加监听器并启动动画
- _animation.addListener(() => setState(() {}));
- _controller.forward();
- Future.delayed(widget.duration, () {
- print('进度条 => 最终动画值: ${_animation.value}');
- });
- }
- void _resetAnimation() => _controller.reset();
- @override
- Widget build(BuildContext context) {
- return LayoutBuilder(
- builder: (context, constraints) {
- // 进度条的实际宽度
- final progressBarWidth = constraints.maxWidth;
- return Stack(
- // 允许子组件溢出自己本身的大小,默认是裁切的
- clipBehavior: Clip.none,
- children: [
- // 百分比文本
- Positioned(
- // 左侧偏移量,跟随当前进度的末尾,公式:当前比例值 * 进度条的宽度 - 气泡宽度
- left: widget.targetValue * progressBarWidth - (widget.targetValue > 0.5 ? 32 : 24),
- // 进度气泡,位于进度条上方
- bottom: widget.height + 0.85,
- child: AnimatedBuilder(
- animation: _animation,
- builder: (context, _) {
- return BubbleWidget(
- // 箭头方向
- arrowDirection: AxisDirection.down,
- arrowOffset: 22,
- arrowLength: 8,
- arrowRadius: 2,
- arrowWidth: 5,
- padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 7),
- borderRadius: BorderRadius.circular(9),
- // 气泡的背景颜色,取渐变色的第2个颜色
- backgroundColor: widget.gradient.colors.last,
- contentBuilder: (context) {
- return Text(
- '${(_animation.value * 100).toStringAsFixed(0)}%',
- style: const TextStyle(fontSize: 10, color: Colors.white),
- );
- },
- );
- },
- ),
- ),
- // 进度条
- ClipRRect(
- borderRadius: BorderRadius.circular(widget.borderRadius),
- child: Container(
- width: double.infinity,
- height: widget.height,
- decoration: BoxDecoration(
- color: Colors.grey[200],
- borderRadius: BorderRadius.circular(widget.borderRadius),
- ),
- child: Stack(
- children: [
- AnimatedBuilder(
- animation: _animation,
- builder: (context, _) {
- return FractionallySizedBox(
- widthFactor: _animation.value,
- alignment: Alignment.centerLeft,
- child: Container(
- decoration: BoxDecoration(
- gradient: widget.gradient,
- borderRadius: BorderRadius.circular(
- widget.borderRadius,
- ),
- ),
- ),
- );
- },
- ),
- ],
- ),
- ),
- ),
- ],
- );
- }
- );
- }
- }
|