Flutter中使用GetX的AnimationController相关问题咨询
近期切换到GetX框架,想要在GetxController中初始化AnimationController并在GetView中访问它。当前应用启动时动画能正常运行,但点击按钮无法让动画再次从头播放。
现有代码
SplashController代码
class SplashController extends GetxController with GetTickerProviderStateMixin { var h = 0.0.obs; late AnimationController ac; late Animation animation; Future<void> initAnimation() async { ac = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); animation = Tween(begin: 0.0, end: 1.0).animate(ac); } forwardAnimationFromZero() { ac.forward(from: 0); } @override void onInit() { super.onInit(); initAnimation(); forwardAnimationFromZero(); ac.addListener(() { h.value = animation.value; }); } @override void onReady() { super.onReady(); forwardAnimationFromZero(); } @override void onClose() { super.onClose(); ac.dispose(); } }
初始SplashPage代码
class SplashPage extends GetView<SplashController> { const SplashPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { var c = Get.put(SplashController()); return Scaffold( body: Column( children: [ Container( color: Colors.amber, width: (controller.animation.value * 100) + 100, height: (controller.animation.value * 100) + 100, child: Center( child: Text( controller.animation.value.toString(), ), ), ) ], ), floatingActionButton: FloatingActionButton( onPressed: () { c.ac.forward(from: 0); }, child: Icon(Icons.refresh), ), appBar: AppBar( title: const Text('Splash Page'), ), ); } }
修改后的SplashPage(Obx包裹)
class SplashPage extends GetView<SplashController> { const SplashPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { var c = Get.put(SplashController()); return Scaffold( body: Column( children: [ Obx( () => Container( color: Colors.amber, width: (controller.animation.value * 100) + 100, height: (controller.h.value * 100) + 100, child: Center( child: Text( controller.animation.value.toString(), ), ), ), ) ], ), floatingActionButton: FloatingActionButton( onPressed: () { c.ac.forward(from: 0); }, child: Icon(Icons.refresh), ), appBar: AppBar( title: const Text('Splash Page'), ), ); } }
修改后初始动画可播放,但点击浮动按钮仍无法让动画从头播放。
疑问解答
1. 为什么没有h可观察变量时,初始动画无法播放?
GetX的视图更新依赖其响应式机制:只有当UI部件被Obx、GetBuilder等响应式组件包裹,且依赖的是GetX标记的可观察变量(如.obs修饰的变量)时,变量变化才会触发UI重建。你的animation.value是普通的动画值,不属于GetX的可观察对象,所以即使它随动画进度变化,视图也不会感知到并更新,自然看不到动画效果。而h.value是.obs修饰的可观察变量,在动画监听器中更新它时,Obx会立刻感知到变化并重建子部件,从而让动画的变化反映到UI上。
2. 如何在视图中访问AnimationController的方法?
禁止直接在视图中访问Controller的内部实例(如ac),这违反了MVVM的职责分离原则。正确的做法是在Controller中封装操作动画的方法(比如你已经定义的forwardAnimationFromZero()),然后在视图中通过GetView自带的controller对象调用该方法即可。
另外要注意:不要在build方法中重复调用Get.put(SplashController()),因为GetView已经通过泛型自动完成了Controller的注入,直接使用controller即可。重复调用Get.put会创建多个Controller实例,导致你点击按钮时操作的是新实例的动画,而UI绑定的是旧实例,这也是点击按钮无反应的核心原因之一。
修正后的浮动按钮代码:
floatingActionButton: FloatingActionButton( onPressed: () { controller.forwardAnimationFromZero(); }, child: Icon(Icons.refresh), ),
3. 某个AnimationController完成后,如何启动另一个AnimationController?
可以给第一个AnimationController添加状态监听器,监听其status属性的变化,当状态变为AnimationStatus.completed时,启动第二个AnimationController即可。
示例Controller代码:
class SplashController extends GetxController with GetTickerProviderStateMixin { var animationValue = 0.0.obs; late AnimationController firstAC; late Animation<double> firstAnim; late AnimationController secondAC; late Animation<double> secondAnim; void initAnimations() { // 初始化第一个动画控制器 firstAC = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); firstAnim = Tween(begin: 0.0, end: 1.0).animate(firstAC); // 初始化第二个动画控制器 secondAC = AnimationController( vsync: this, duration: const Duration(seconds: 1), ); secondAnim = Tween(begin: 1.0, end: 0.0).animate(secondAC); // 监听第一个动画的状态变化 firstAC.addStatusListener((status) { if (status == AnimationStatus.completed) { secondAC.forward(from: 0); // 第一个动画完成后启动第二个 } }); // 监听两个动画的值变化,更新可观察变量 firstAC.addListener(() { animationValue.value = firstAnim.value; }); secondAC.addListener(() { animationValue.value = secondAnim.value; }); } void startFirstAnimation() { firstAC.forward(from: 0); } @override void onInit() { super.onInit(); initAnimations(); startFirstAnimation(); } @override void onClose() { firstAC.dispose(); secondAC.dispose(); super.onClose(); } }
内容的提问来源于stack exchange,提问作者Tofiq Samali

