如何实现Linear Progress Indicator的同款动画效果?
实现自定义流动渐变线性进度条动画
从你提供的动画效果来看,核心是进度条填充区域内的渐变色彩循环流动,而非Shimmer那种整体扫过的闪烁效果,所以需要自定义动画逻辑来实现。以下分Flutter和Android原生两种场景给出实现方案:
Flutter 实现方案
通过Stack叠加基础进度条与动态渐变层,配合动画控制器控制渐变的水平偏移,实现流动效果:
import 'package:flutter/material.dart'; class FlowProgressIndicator extends StatefulWidget { final double value; // 进度值(0-1) final Color baseColor; final Color highlightColor; const FlowProgressIndicator({ super.key, required this.value, this.baseColor = const Color(0xFFE0E0E0), this.highlightColor = const Color(0xFF4CAF50), }); @override State<FlowProgressIndicator> createState() => _FlowProgressIndicatorState(); } class _FlowProgressIndicatorState extends State<FlowProgressIndicator> with SingleTickerProviderStateMixin { late AnimationController _animController; late Animation<double> _offsetAnim; @override void initState() { super.initState(); // 创建循环动画控制器,周期1秒 _animController = AnimationController( duration: const Duration(seconds: 1), vsync: this, )..repeat(); // 控制渐变偏移的动画值(0→1) _offsetAnim = Tween<double>(begin: 0.0, end: 1.0).animate(_animController); } @override void dispose() { _animController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _offsetAnim, builder: (context, child) { return Stack( children: [ // 基础灰色进度条(背景+已填充部分) LinearProgressIndicator( value: widget.value, backgroundColor: widget.baseColor, valueColor: AlwaysStoppedAnimation(widget.baseColor), ), // 流动渐变层:仅显示在进度范围内 ClipRect( child: Align( alignment: Alignment.centerLeft, widthFactor: widget.value, child: Container( height: 4, // 与进度条高度保持一致 decoration: BoxDecoration( gradient: LinearGradient( colors: [widget.baseColor, widget.highlightColor, widget.baseColor], // 通过动画值控制渐变的起始/结束位置,实现流动 begin: Alignment(_offsetAnim.value * 2 - 1, 0), end: Alignment(_offsetAnim.value * 2 + 1, 0), ), ), ), ), ), ], ); }, ); } } // 使用示例 class DemoPage extends StatelessWidget { const DemoPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("流动进度条示例")), body: const Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: FlowProgressIndicator(value: 0.6), ), ), ); } }
逻辑说明
- 用
AnimationController创建循环动画,控制渐变的水平偏移量 - 通过
ClipRect和Align限制渐变层仅显示在进度条已填充的范围内 - 渐变采用「基础色→高亮色→基础色」的过渡,随着偏移量变化形成流动效果
Android 原生实现方案
通过自定义View,结合ValueAnimator动态修改渐变的位置参数,实现流动效果:
import android.content.Context import android.graphics.* import android.util.AttributeSet import android.view.View import androidx.core.content.ContextCompat class FlowProgressView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private var progress = 0.6f // 进度值(0-1) private val baseColor = ContextCompat.getColor(context, R.color.grey) private val highlightColor = ContextCompat.getColor(context, R.color.green) private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private lateinit var gradient: LinearGradient private val animator = ValueAnimator.ofFloat(0f, 2f).apply { duration = 1000 repeatCount = ValueAnimator.INFINITE addUpdateListener { // 动态更新渐变的颜色位置,实现流动 val offset = it.animatedValue as Float gradient.locations = floatArrayOf(offset - 1f, offset - 0.5f, offset) invalidate() } start() } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) // 初始化线性渐变 gradient = LinearGradient( 0f, 0f, w.toFloat(), 0f, intArrayOf(baseColor, highlightColor, baseColor), floatArrayOf(0f, 0.5f, 1f), Shader.TileMode.CLAMP ) } fun setProgress(progress: Float) { this.progress = progress.coerceIn(0f, 1f) invalidate() } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) val viewWidth = width.toFloat() val viewHeight = height.toFloat() val radius = viewHeight / 2 // 绘制背景进度条 paint.color = baseColor paint.shader = null canvas.drawRoundRect(RectF(0f, 0f, viewWidth, viewHeight), radius, radius, paint) // 绘制流动渐变进度层 paint.shader = gradient val progressWidth = viewWidth * progress canvas.drawRoundRect( RectF(0f, 0f, progressWidth, viewHeight), radius, radius, paint ) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() animator.cancel() // 页面销毁时停止动画,避免内存泄漏 } }
逻辑说明
- 在
onSizeChanged中初始化线性渐变,确保渐变宽度与View一致 - 用
ValueAnimator循环更新渐变的locations数组,让渐变色彩沿水平方向流动 - 绘制时先画灰色背景,再在进度范围内绘制流动渐变层
内容的提问来源于stack exchange,提问作者Farid Ahmadov
相关产品推荐
相关产品推荐

