You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现类似TrueCaller的Flutter流畅波纹动画效果?

实现类似TrueCaller的流畅波纹动画

要实现TrueCaller那种连续向外扩散、逐渐淡出的波纹效果,你当前的方案存在几个问题:每个波纹独立控制动画导致同步性差,来回缩放的逻辑不符合扩散式波纹的需求,且没有处理透明度的渐变。下面是更贴合需求的实现思路和代码:

核心实现思路

  • 用单个AnimationController控制所有波纹的动画进度,避免多控制器的同步问题
  • 每个波纹通过Interval设置不同的动画时间区间,实现依次扩散的效果
  • 波纹动画同时包含尺寸放大和透明度降低两个维度,模拟自然扩散消失的过程
  • 循环播放动画,保持波纹的连续性

优化后的代码实现

class WaveRippleWidget extends StatefulWidget {
  final double maxRadius; // 波纹最大扩散半径
  final Color rippleColor; // 波纹颜色
  final int rippleCount; // 波纹数量
  final int animationDuration; // 单条波纹完成一次动画的时长(毫秒)

  const WaveRippleWidget({
    super.key,
    required this.maxRadius,
    this.rippleColor = Colors.green,
    this.rippleCount = 3,
    this.animationDuration = 2000,
  });

  @override
  State<WaveRippleWidget> createState() => _WaveRippleWidgetState();
}

class _WaveRippleWidgetState extends State<WaveRippleWidget> with TickerProviderStateMixin {
  late AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: widget.animationDuration),
    )..repeat(); // 循环播放动画
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: SizedBox(
        width: widget.maxRadius * 2,
        height: widget.maxRadius * 2,
        child: Stack(
          alignment: Alignment.center,
          children: List.generate(widget.rippleCount, (index) {
            // 为每个波纹设置时间区间,错开动画起始时间
            final animation = Tween<double>(begin: 0, end: 1).animate(
              CurvedAnimation(
                parent: _animationController,
                curve: Interval(
                  index / widget.rippleCount,
                  1.0,
                  curve: Curves.easeOut,
                ),
              ),
            );

            return AnimatedBuilder(
              animation: animation,
              builder: (context, child) {
                final progress = animation.value;
                return Container(
                  width: progress * widget.maxRadius * 2,
                  height: progress * widget.maxRadius * 2,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: widget.rippleColor.withOpacity(1 - progress),
                  ),
                );
              },
            );
          }),
        ),
      ),
    );
  }
}

使用示例

在需要的地方直接调用这个Widget即可:

WaveRippleWidget(
  maxRadius: 100, // 根据需求调整最大半径
  rippleColor: Colors.green.withOpacity(0.4),
  rippleCount: 3,
  animationDuration: 2000,
)

关键细节说明

  • 同步控制:单个AnimationController循环播放,所有波纹基于它的进度计算自己的动画状态,完美保证同步性
  • 时间错开:通过Interval(index / rippleCount, 1.0)让每个波纹依次启动,形成连续扩散的视觉效果
  • 双维度动画:尺寸从0放大到最大半径,透明度从1降到0,符合真实波纹的扩散消失逻辑
  • 性能优化:用AnimatedBuilder结合Container实现,比多个独立StatefulWidget更高效

内容的提问来源于stack exchange,提问作者Sinh Phan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 16:42:49