Flutter 不依赖setState实现双动画同步运行的问题解决
Flutter 无setState双同步动画实现方案
核心问题根因
- 页面卡顿的来源是给动画控制器添加全局
setState监听:每次动画值更新都会触发整个DetailScreen组件树重绘,包括BalanceCard、TransactionWidget这类不需要随动画同步更新的静态UI部分,造成不必要的渲染开销。 - 移除
setState后第二个动画失效的原因:内置的SizeTransition属于AnimatedWidget子类,内置了对传入动画的监听逻辑,动画值变化时会自行触发局部重绘,因此移除全局setState也能正常运行;而自定义的TransactionWidget只是通过构造参数接收Animation对象,没有实现动画监听逻辑,只有外层组件重绘时才会被动读取新的动画值,因此移除全局重绘后就失去了更新触发。
方案1:单控制器驱动双反向动画(资源最优)
两组动画时长、执行节奏完全一致,不需要维护两个独立控制器,直接复用同一个控制器完成两个反向补间的映射即可,完全不依赖全局setState:
- 删除冗余的
animationController2,仅保留一个主动画控制器 - 定义两个方向相反的Tween,全部绑定到同一个控制器,天然实现同步执行:
// 替换原有initState中的动画初始化逻辑 animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 800) ); // 用于SizeTransition的正向动画:0.0 → 1.0 tween = Tween<double>(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: animationController, curve: Curves.easeOut) ); // 用于TransactionWidget透明度的反向动画:1.0 → 0.0,直接绑定同一个控制器 tween2 = Tween<double>(begin: 1.0, end: 0.0).animate( CurvedAnimation(parent: animationController, curve: Curves.easeOut) ); // 彻底移除两个控制器上的 addListener + setState 逻辑 - 简化动画切换逻辑,仅调用单个控制器的方向方法即可同时驱动两个动画:
void showHideSendView() { if (_forward) { animationController.reverse(); _forward = false; } else { animationController.forward(); _key.currentState.initView(); _forward = true; } } // 移除showTransactions方法中多余的setState包裹,直接调用showHideSendView即可 void showTransactions() { showHideSendView(); } - 给需要读取动画值的自定义组件套上
AnimatedBuilder,实现动画变化时的局部重绘,同时利用child参数缓存不随动画变化的内容,进一步降低重绘开销:// 替换原有TransactionWidget的渲染逻辑 AnimatedBuilder( animation: tween2, builder: (context, child) { return TransactionWidget( // 直接取动画实时值传递,不需要传Animation对象 opacity: tween2.value, getTransactions: _getTransactions, // 缓存不需要随透明度变化的子组件 child: child, ); }, // 把静态渲染内容提前传入,避免动画更新时重复创建 child: const TransactionStaticContent(), ) - 同样用
AnimatedBuilder优化SendWidget的渲染,把静态内容做缓存,避免动画更新时重复构建无变化的UI:AnimatedBuilder( animation: tween, builder: (context, child) { return SizeTransition( sizeFactor: tween, axis: Axis.vertical, axisAlignment: -1, child: SendWidget( key: _key, func: shrinkSendView, height: tween.value, ), ); }, )
方案2:保留双独立控制器的兼容实现
如果后续需要为两组动画配置不同的时长、曲线节奏,不需要合并控制器,仅需为每个绑定动画的组件添加对应的AnimatedBuilder即可实现同样的无全局setState效果:
- 保留两个独立的
AnimationController和对应的Tween定义,删除控制器上的全局setState监听 - 分别为两个动画对应的渲染组件套上独立的
AnimatedBuilder,监听对应动画的变化做局部重绘 - 两个控制器的
forward/reverse调用不需要包裹setState,系统会自动在动画帧更新时触发对应组件的重绘
内容的提问来源于stack exchange,提问作者Michal Žídek
相关产品推荐
相关产品推荐

