如何在Flutter中实现与视频同款的SVG箭头动画?
实现三箭头SVG的逐次高亮动画方案
你当前用的shimmer是整体扫过的光效,但目标动画是三个箭头依次循环高亮,所以需要针对SVG内的箭头元素单独控制动画,而非给整个SVG加全局shimmer。以下是几种可行方案:
方案一:拆分SVG+flutter_animate逐次动画
步骤1:拆分SVG结构
打开你的down_arrows.svg,将三个箭头拆分为独立的元素(比如三个<path>),保存为单个箭头的SVG文件,或给每个箭头路径添加唯一id。
步骤2:Flutter代码实现
用Stack叠加默认状态的整体SVG和三个独立高亮箭头,分别添加延迟动画:
Stack( alignment: Alignment.center, children: [ // 默认灰色箭头背景 SvgPicture.asset( 'assets/icons/down_arrows.svg', height: 32.h, width: 32.h, colorFilter: const ColorFilter.mode( AppColors.bottomNavIconGreen, BlendMode.srcIn, ), ), // 三个高亮箭头依次闪烁 ...List.generate(3, (index) { return SvgPicture.asset( 'assets/icons/single_down_arrow.svg', // 单个箭头的SVG文件 height: 32.h, width: 32.h, colorFilter: const ColorFilter.mode( AppColors.mainGreen, BlendMode.srcIn, ), ) .animate( onPlay: (controller) => controller.repeat(), delay: (index * 1000).ms, // 每个箭头延迟1秒启动循环 ) .fadeIn(duration: 500.ms) .fadeOut(duration: 500.ms); }), ], )
方案二:SVG内置SMIL动画(无需Flutter代码控制)
直接在SVG文件中添加原生动画,flutter_svg支持SMIL动画:
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <!-- 顶部箭头 --> <path d="M16 8l-6 6h12l-6-6z" fill="#88c899"> <animate attributeName="fill" values="#88c899;#2ecc71;#88c899" dur="3s" repeatCount="indefinite" begin="0s"/> </path> <!-- 中间箭头 --> <path d="M16 14l-6 6h12l-6-6z" fill="#88c899"> <animate attributeName="fill" values="#88c899;#2ecc71;#88c899" dur="3s" repeatCount="indefinite" begin="1s"/> </path> <!-- 底部箭头 --> <path d="M16 20l-6 6h12l-6-6z" fill="#88c899"> <animate attributeName="fill" values="#88c899;#2ecc71;#88c899" dur="3s" repeatCount="indefinite" begin="2s"/> </path> </svg>
之后直接加载这个动画SVG即可:
SvgPicture.asset( 'assets/icons/down_arrows_animated.svg', height: 32.h, width: 32.h, )
方案三:AnimatedBuilder+ShaderMask控制单个SVG(不拆分)
如果不想拆分SVG,可通过着色器掩码根据动画值高亮对应区域:
class ArrowAnimation extends StatefulWidget { const ArrowAnimation({super.key}); @override State<ArrowAnimation> createState() => _ArrowAnimationState(); } class _ArrowAnimationState extends State<ArrowAnimation> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<int> _currentArrow; @override void initState() { super.initState(); _controller = AnimationController(vsync: this, duration: const Duration(seconds: 3))..repeat(); _currentArrow = StepTween(begin: 0, end: 2).animate(_controller); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _controller, builder: (context, child) { return ShaderMask( shaderCallback: (rect) { final gradient = LinearGradient( colors: [ AppColors.bottomNavIconGreen, _currentArrow.value == 0 ? AppColors.mainGreen : AppColors.bottomNavIconGreen, _currentArrow.value == 1 ? AppColors.mainGreen : AppColors.bottomNavIconGreen, _currentArrow.value == 2 ? AppColors.mainGreen : AppColors.bottomNavIconGreen, ], stops: const [0, 0.33, 0.66, 1], begin: Alignment.topCenter, end: Alignment.bottomCenter, ); return gradient.createShader(rect); }, child: SvgPicture.asset( 'assets/icons/down_arrows.svg', height: 32.h, width: 32.h, ), ); }, ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
推荐优先选择方案一或方案二,实现成本更低且动画效果精准。
内容的提问来源于stack exchange,提问作者Mehedi Hasan
相关产品推荐
相关产品推荐

