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

如何实现带曲线启动与停止效果的重复AnimationController?

解决可重复播放且启停带曲线动画的AnimationController问题

嘿,我来帮你搞定这个动画需求!要让AnimationController启动时带曲线动画进入重复循环,停止时也用曲线平滑过渡,我们需要调整动画的构建方式和启停逻辑,下面是具体的解决方案:

核心思路

  • 启动阶段:先通过曲线动画从初始值过渡到循环终点,完成后自动触发无限重复播放
  • 停止阶段:用animateBack结合曲线过渡到初始状态,动画结束后自动重置控制器状态
  • 状态同步:通过动画状态监听来管理_animating变量,避免手动设置导致的状态冲突

完整代码实现

import 'dart:math';
import 'package:flutter/material.dart';

class RotationAnimationDemo extends StatefulWidget {
  const RotationAnimationDemo({super.key});

  @override
  State<RotationAnimationDemo> createState() => _RotationAnimationDemoState();
}

class _RotationAnimationDemoState extends State<RotationAnimationDemo> with SingleTickerProviderStateMixin {
  bool _animating = false;
  late AnimationController _rotationAnimationController;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _rotationAnimationController = AnimationController(
      duration: const Duration(milliseconds: 2000),
      vsync: this,
    );

    // 整合曲线到动画中:启动阶段使用指定曲线,循环阶段切换为匀速
    _animation = Tween<double>(begin: 0, end: 4 * pi).animate(
      CurvedAnimation(
        parent: _rotationAnimationController,
        curve: Curves.easeIn, // 启动时的入场曲线,可按需替换
      ),
    )..addListener(() {
        setState(() {});
      })..addStatusListener((status) {
        // 第一次动画完成后,若仍处于激活状态则开启无限重复
        if (status == AnimationStatus.completed && _animating) {
          _rotationAnimationController.repeat(
            reverse: false, // 若需要来回旋转可设为true
          );
        }
        // 反向动画完成(回到初始状态)后,重置激活状态
        if (status == AnimationStatus.dismissed) {
          setState(() {
            _animating = false;
          });
        }
      });
  }

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

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        const startStopDuration = Duration(seconds: 3);
        if (_animating) {
          // 停止时:用曲线动画平滑回到初始角度
          _rotationAnimationController.animateBack(
            0,
            duration: startStopDuration,
            curve: Curves.easeOut,
          );
        } else {
          setState(() {
            _animating = true;
          });
          // 启动时:先执行一次带曲线的入场动画,之后自动进入循环
          _rotationAnimationController.forward();
        }
      },
      child: Container(
        width: 120,
        height: 120,
        color: Colors.deepPurple,
        alignment: Alignment.center,
        child: Transform.rotate(
          angle: _animation.value,
          child: const Text(
            "旋转图标",
            style: TextStyle(color: Colors.white, fontSize: 16),
          ),
        ),
      ),
    );
  }
}

关键细节解释

  • 动画构建:用CurvedAnimation将曲线与控制器绑定,确保第一次启动动画应用指定曲线
  • 启动逻辑:点击启动时调用forward(),动画完成后通过状态监听触发repeat(),无缝进入循环
  • 停止逻辑:点击停止时调用animateBack(0),结合Curves.easeOut实现平滑减速停止,动画回到初始状态后自动更新_animating
  • 状态安全:通过AnimationStatus监听同步状态,避免手动设置_animating导致的动画冲突

如果需要让每一轮循环都带曲线,只需把repeat()替换为带CurvedAnimation的动画即可,但通常启动阶段用曲线、循环阶段匀速的体验会更自然。

内容的提问来源于stack exchange,提问作者Tobias H.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 00:32:41