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

Flutter如何让旋转动画更流畅?解决5秒刷新生硬重启问题

解决Flutter旋转动画生硬重启并实现反向播放的方案

问题根源

你的代码里直接修改change_rotation_speed参数,会导致Transform.rotate的angle值突然跳变,出现生硬的重启效果;同时没有保留当前旋转的累计角度,没法基于现有状态平滑过渡方向。

解决方案

核心思路是维护实际旋转的累计角度,每次切换时基于当前角度平滑过渡方向和速度,避免突变。以下是修改后的完整代码:

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

class SpinnerAnimation extends StatefulWidget {
  final Widget body;

  SpinnerAnimation({required this.body});

  @override
  _SpinnerAnimationState createState() => _SpinnerAnimationState();
}

class _SpinnerAnimationState extends State<SpinnerAnimation>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  // 记录旋转以来的总角度(弧度)
  double _totalRotation = 0.0;
  // 旋转方向:1正向,-1反向
  int _rotationDirection = 1;
  // 当前旋转速度系数
  double _speed = 1.2;
  late Timer _timer;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 10),
      vsync: this,
    );

    // 监听动画帧,实时累计旋转角度
    _controller.addListener(() {
      final frameRotation = _controller.value * _speed * math.pi * 2 * _rotationDirection;
      _totalRotation = frameRotation;
    });

    // 启动初始动画
    _controller.repeat();

    // 每5秒切换方向和速度
    _timer = Timer.periodic(const Duration(seconds: 5), (_) {
      _switchRotation();
    });
  }

  void _switchRotation() {
    setState(() {
      // 反转旋转方向
      _rotationDirection *= -1;
      // 生成新的随机速度
      _speed = randoDoubleGeneratorWithRange(1.2, 5);
    });

    // 基于当前累计角度,计算反向动画的目标值
    final targetValue = (_totalRotation / (_speed * math.pi * 2 * _rotationDirection)).clamp(0.0, 1.0);
    // 平滑过渡到目标状态后继续循环
    _controller.animateTo(targetValue, duration: const Duration(milliseconds: 300)).then((_) {
      _controller.repeat();
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    _timer.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      child: widget.body,
      builder: (BuildContext context, Widget? child) {
        final currentAngle = _controller.value * _speed * math.pi * 2 * _rotationDirection;
        return Transform.rotate(
          angle: currentAngle,
          child: child,
        );
      },
    );
  }

  double randoDoubleGeneratorWithRange(double min, double max) {
    return min + Random().nextDouble() * (max - min);
  }
}

关键改动说明

  • 累计角度维护:通过_totalRotation记录当前实际旋转的角度,避免切换时角度重置
  • 平滑过渡:使用animateTo方法,用300ms的过渡时间切换到反向状态,完全消除生硬跳转
  • 方向控制:通过_rotationDirection的正负值控制旋转方向,切换时直接取反即可
  • 定时器管理:在dispose中取消定时器,避免内存泄漏

可选调整

  • 如果不需要每次切换都改变速度,删掉_speed的随机生成逻辑即可
  • 调整animateTo的duration参数,可以改变过渡的快慢(比如改成500ms会更平缓)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 14:10:40