如何在Flutter的StatelessWidget中结合Cubit/Bloc使用AnimationController?
在StatelessWidget中结合Cubit/Bloc使用AnimationController的实现方案
核心思路
StatelessWidget没有initState和dispose这类生命周期方法,所以我们把AnimationController的创建、状态管理和资源释放逻辑完全交给Cubit/Bloc处理,StatelessWidget只负责监听状态变化并渲染动画内容。
1. 基于Cubit的实现
第一步:创建管理动画的Cubit
这个Cubit会封装AnimationController的初始化、播放、反向播放和资源释放逻辑:
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/scheduler.dart'; // 定义动画状态,包含控制器和动画对象 class AnimationState { final AnimationController controller; final Animation<double> animation; AnimationState({required this.controller, required this.animation}); } class AnimationCubit extends Cubit<AnimationState> { AnimationCubit(TickerProvider vsync) : super(_initAnimation(vsync)) { // 监听动画值变化,触发状态更新以刷新UI state.controller.addListener(() => emit(state)); } // 初始化动画控制器和动画对象 static AnimationState _initAnimation(TickerProvider vsync) { final controller = AnimationController( duration: const Duration(seconds: 2), vsync: vsync, ); final animation = Tween<double>(begin: 0, end: 1).animate(controller); return AnimationState(controller: controller, animation: animation); } // 播放动画 void play() => state.controller.forward(); // 反向播放动画 void reverse() => state.controller.reverse(); // 重置动画 void reset() => state.controller.reset(); // 释放资源,避免内存泄漏 @override Future<void> close() { state.controller.dispose(); return super.close(); } }
第二步:在StatelessWidget中使用Cubit
通过BlocProvider提供Cubit实例,用BlocBuilder监听状态变化,结合AnimatedBuilder渲染动画:
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/scheduler.dart'; class StatelessAnimationPage extends StatelessWidget { const StatelessAnimationPage({super.key}); @override Widget build(BuildContext context) { return BlocProvider( // 使用全局SchedulerBinding作为vsync,解决StatelessWidget无法提供TickerProvider的问题 create: (_) => AnimationCubit(SchedulerBinding.instance), child: Scaffold( appBar: AppBar(title: const Text('Stateless + Cubit 动画示例')), body: Center( child: BlocBuilder<AnimationCubit, AnimationState>( builder: (context, state) { return AnimatedBuilder( animation: state.animation, builder: (_, child) { return Transform.scale( scale: state.animation.value, child: child, ); }, child: const FlutterLogo(size: 200), ); }, ), ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => context.read<AnimationCubit>().play(), child: const Icon(Icons.play_arrow), ), const SizedBox(width: 10), FloatingActionButton( onPressed: () => context.read<AnimationCubit>().reverse(), child: const Icon(Icons.replay), ), ], ), ), ); } }
2. 基于Bloc的实现(可选)
如果习惯用Bloc的事件-状态模式,只需调整为Bloc结构:
第一步:定义动画事件和Bloc
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; // 动画事件 abstract class AnimationEvent {} class PlayAnimation extends AnimationEvent {} class ReverseAnimation extends AnimationEvent {} class ResetAnimation extends AnimationEvent {} // 动画状态(和Cubit的状态一致) class AnimationState { final AnimationController controller; final Animation<double> animation; AnimationState({required this.controller, required this.animation}); } class AnimationBloc extends Bloc<AnimationEvent, AnimationState> { AnimationBloc(TickerProvider vsync) : super(_initAnimation(vsync)) { on<PlayAnimation>((_, emit) => state.controller.forward()); on<ReverseAnimation>((_, emit) => state.controller.reverse()); on<ResetAnimation>((_, emit) => state.controller.reset()); state.controller.addListener(() => emit(state)); } static AnimationState _initAnimation(TickerProvider vsync) { final controller = AnimationController( duration: const Duration(seconds: 2), vsync: vsync, ); final animation = Tween<double>(begin: 0, end: 1).animate(controller); return AnimationState(controller: controller, animation: animation); } @override Future<void> close() { state.controller.dispose(); return super.close(); } }
第二步:在StatelessWidget中使用Bloc
只需把BlocProvider和事件调用方式修改即可:
// ... 其他代码和Cubit版本一致 BlocProvider( create: (_) => AnimationBloc(SchedulerBinding.instance), child: Scaffold( // ... floatingActionButton: Row( children: [ FloatingActionButton( onPressed: () => context.read<AnimationBloc>().add(PlayAnimation()), child: const Icon(Icons.play_arrow), ), // ... 其他按钮同理 ], ), ), )
关键注意事项
- 资源释放:必须在Cubit/Bloc的
close方法中调用AnimationController.dispose(),否则会造成内存泄漏。 - 状态更新触发:动画值变化不会自动触发Bloc/Cubit的状态更新,所以需要在控制器的
addListener中调用emit(state)。 - Vsync获取:StatelessWidget无法直接提供
TickerProvider,这里使用全局的SchedulerBinding.instance作为替代;如果是复杂动画场景,建议从父级StatefulWidget传递TickerProvider。
内容的提问来源于stack exchange,提问作者Mohammad Mirshahbazi
相关产品推荐
相关产品推荐

