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

Flutter动画优化:单AnimationController实现多动画并单独反转指定动画

问题描述

我想实现用单个AnimationController控制多个动画,并且能单独反转该控制器中的指定动画。目前我用了多个AnimationController,每个单独控制一个动画,代码如下:

late final AnimationController controllerHeightCard,
    controllerHeightNativeView, controllerInsertingView;

void heightAnimation1() {
  controllerHeightCard = AnimationController(
      duration: const Duration(milliseconds: 250), vsync: this);

  controllerInsertingView = AnimationController(
      duration: const Duration(milliseconds: 250), vsync: this);
  heightAnimation_1 =
      Tween(begin: 310.0, end: 380.0).animate(controllerHeightCard);
  heightAnimation_1.addListener(() {
    cardHeight.value = heightAnimation_1.value;
  });
  heightAnimation_2 =
      Tween(begin: 0.0, end: 75.0).animate(controllerHeightNativeView);

  heightAnimation_2.addListener(() {
    containerHeight.value = heightAnimation_2.value;
  });
  controllerHeightCard.addListener(() {
    if (controllerHeightCard.isCompleted && isSignUp.value) {
      controllerHeightNativeView.forward();
    }
  });

  controllerHeightNativeView.addStatusListener((status) {
    if (controllerHeightNativeView.isDismissed && isSignUp.value == false) {
      controllerHeightCard.reverse();
    }
  });
}

heightAnimationForward() {
  isSignUp.value = !isSignUp.value;
  if (isSignUp.value == true) {
    controllerHeightCard.forward();
  }
  if (isSignUp.value == false) {
    controllerHeightNativeView.reverse(from: 0.1);
  }
}

请问该怎么实现这个需求?PS:我是Flutter新手,代码写得不好请见谅。


解决方案

1. 核心思路

用单个AnimationController作为统一驱动源,给每个动画创建独立的Animation对象绑定到该控制器。要实现单独反转指定动画,可通过自定义动画值计算的方式,用布尔标记控制单个动画的方向,无需额外控制器。

2. 改造后的完整代码

late final AnimationController _mainController;
// 用布尔标记控制单个动画的反转状态
bool _reverseCardAnim = false;
bool _reverseNativeViewAnim = false;
final ValueNotifier<double> cardHeight = ValueNotifier(310.0);
final ValueNotifier<double> containerHeight = ValueNotifier(0.0);
final ValueNotifier<bool> isSignUp = ValueNotifier(false);

void initAnimations() {
  // 单个主控制器,统一管理动画时长
  _mainController = AnimationController(
    duration: const Duration(milliseconds: 250),
    vsync: this,
  );

  // 监听控制器进度,手动计算每个动画的当前值(支持单独反转)
  _mainController.addListener(() {
    final progress = _mainController.value;
    // 卡片高度:根据反转状态计算值
    final currentCardHeight = _reverseCardAnim
        ? 380.0 - 70.0 * progress // 反向:从380降到310
        : 310.0 + 70.0 * progress; // 正向:从310升到380
    cardHeight.value = currentCardHeight;

    // NativeView高度:根据反转状态计算值
    final currentNativeHeight = _reverseNativeViewAnim
        ? 75.0 - 75.0 * progress // 反向:从75降到0
        : 75.0 * progress; // 正向:从0升到75
    containerHeight.value = currentNativeHeight;
  });

  // 保留原有联动逻辑:动画状态监听
  _mainController.addStatusListener((status) {
    if (status == AnimationStatus.completed && isSignUp.value) {
      // 卡片动画完成后,启动NativeView正向动画
      _reverseNativeViewAnim = false;
      _mainController.reset();
      _mainController.forward();
    }
    if (status == AnimationStatus.dismissed && !isSignUp.value) {
      // NativeView动画消失后,启动卡片反向动画
      _reverseCardAnim = true;
      _mainController.reset();
      _mainController.forward();
    }
  });
}

void toggleAnimation() {
  isSignUp.value = !isSignUp.value;
  if (isSignUp.value) {
    // 切换到注册状态:正向执行卡片动画
    _reverseCardAnim = false;
    _mainController.reset();
    _mainController.forward();
  } else {
    // 切换到非注册状态:反向执行NativeView动画
    _reverseNativeViewAnim = true;
    _mainController.reset();
    _mainController.forward();
  }
}

// 记得在页面销毁时释放控制器
@override
void dispose() {
  _mainController.dispose();
  super.dispose();
}

3. 关键说明

  • 单控制器驱动:所有动画共享_mainController,减少资源占用,逻辑更集中。
  • 单独反转控制:通过_reverseCardAnim、_reverseNativeViewAnim两个布尔标记,在控制器监听中手动计算每个动画的当前值,实现单个动画的反转效果。
  • 联动逻辑保留:通过控制器的addStatusListener,保留了你原有代码中动画完成/消失后的联动逻辑。

4. 进阶优化:用AnimatedBuilder简化UI更新

如果你的UI是用AnimatedBuilder构建的,可以去掉ValueNotifier,让Flutter自动根据动画进度重建UI,性能更优:

@override
Widget build(BuildContext context) {
  return AnimatedBuilder(
    animation: _mainController,
    builder: (context, child) {
      final progress = _mainController.value;
      final cardHeight = _reverseCardAnim
          ? 380.0 - 70.0 * progress
          : 310.0 + 70.0 * progress;
      final nativeHeight = _reverseNativeViewAnim
          ? 75.0 - 75.0 * progress
          : 75.0 * progress;

      return Column(
        children: [
          Container(
            height: cardHeight,
            color: Colors.blue,
            child: child,
          ),
          Container(
            height: nativeHeight,
            color: Colors.red,
          ),
        ],
      );
    },
    child: const Text('Card Content'), // 无需动画的子部件,避免重复重建
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 17:58:09