Flutter中使用AnimationController触发生命周期状态崩溃错误求助
Flutter AnimationController 生命周期崩溃问题修复
错误现象
触发以下断言错误,导致应用直接崩溃,需重新加载:
'package:flutter/src/widgets/framework.dart': Failed assertion: line 4519 pos 12: '_lifecycleState != _ElementLifecycle.defunct': is not true
问题根源
- 重复调用
super.initState():initState中连续执行两次super.initState(),违反Flutter生命周期规范 - 未安全处理控制器实例:多处直接用
animationController!强制解包,未判断控制器是否初始化或已销毁 - GetX控制器重复注入:类成员已初始化
TimerTabController,initState中再次调用Get.put()造成重复实例 - 动画监听未清理:动画控制器的监听函数未在组件销毁时移除,组件进入defunct状态后仍可能触发
setState
修复后的完整代码
// ignore_for_file: depend_on_referenced_packages, sized_box_for_whitespace, avoid_print import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:habbit_app/controllers/timer_tab_controller.dart'; import 'package:habbit_app/screens/timer/set_timer_component.dart'; import 'package:flutter_ringtone_player/flutter_ringtone_player.dart'; import 'package:habbit_app/widgets/text_widget/label_text.dart'; class TimerTab extends StatefulWidget { const TimerTab({super.key}); @override State<TimerTab> createState() => _TimerTabState(); } class _TimerTabState extends State<TimerTab> with TickerProviderStateMixin { AnimationController? animationController; final TimerTabController tabController = Get.put(TimerTabController(), permanent: false); late TabController _controller; bool isPlaying = false; double progress = 1.0; String get countText { if (animationController == null) return "0:00:00"; Duration count = animationController!.duration! * animationController!.value; return animationController!.isDismissed ? '${animationController!.duration!.inHours}:${(animationController!.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(animationController!.duration!.inSeconds % 60).toString().padLeft(2, '0')}' : '${count.inHours}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}'; } void notifiy() { if (countText == "0:00:00") { FlutterRingtonePlayer.playNotification(); } } @override void initState() { super.initState(); _controller = TabController(length: 3, vsync: this); _controller.addListener(() { tabController.tabIndex.value = _controller.index; }); animationController = AnimationController( vsync: this, duration: Duration(seconds: tabController.totalSeconds)); animationController!.addListener(_animationListener); } void _animationListener() { if (!mounted) return; notifiy(); if (animationController!.isAnimating) { setState(() { progress = animationController!.value; }); } else { setState(() { progress = 1.0; isPlaying = false; }); } } @override void dispose() { _controller.dispose(); if (animationController != null) { animationController!.removeListener(_animationListener); animationController!.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { var color = Theme.of(context); return Obx( () => tabController.isFirst.value ? SetTimerComponent( changeAnimation: () { if (animationController != null) { animationController!.duration = Duration(seconds: tabController.totalSeconds); } tabController.changeFirst(); }, ) : Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( height: 350, width: MediaQuery.of(context).size.width, child: Column(children: [ Stack( alignment: Alignment.center, children: [ SizedBox( width: 300, height: 300, child: CircularProgressIndicator( color: color.primaryColor, backgroundColor: color.disabledColor.withOpacity(0.6), value: progress, strokeWidth: 10, )), GestureDetector( onTap: () { if (animationController?.isDismissed ?? false) { showModalBottomSheet( context: context, builder: (context) => Container( height: 300, child: CupertinoTimerPicker( initialTimerDuration: animationController?.duration ?? const Duration(seconds: 0), backgroundColor: color.backgroundColor, onTimerDurationChanged: (time) { if (mounted && animationController != null) { setState(() { animationController!.duration = time; }); } }, ), )); } }, child: AnimatedBuilder( animation: animationController!, builder: (context, child) => Text( countText, style: TextStyle( fontSize: 60, fontWeight: FontWeight.bold, color: color.primaryColor), ), ), ), ], ), ]), ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ GestureDetector( onTap: () { print(tabController.totalSeconds); if (animationController == null) return; if (animationController!.isAnimating) { animationController!.stop(); if (mounted) { setState(() { isPlaying = false; }); } } else { animationController!.reverse( from: animationController!.value == 0 ? 1.0 : animationController!.value); if (mounted) { setState(() { isPlaying = true; }); } } }, child: Container( height: 35, width: 90, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(30)), color: color.primaryColor), child: Center( child: LabelText( text: isPlaying ? "PAUSE" : "RESUME", isBold: true, isColor: true, color: Colors.white, )), ), ), GestureDetector( onTap: () { if (animationController != null) { animationController!.reset(); } tabController.currentvalueHour.value = 0; tabController.currentvalueMin.value = 0; tabController.currentvalueSec.value = 0; if (mounted) { setState(() { isPlaying = false; }); } tabController.changeFirst2(); }, child: Container( height: 35, width: 90, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(30)), color: color.disabledColor.withOpacity(0.5)), child: const Center( child: LabelText( text: "DELETE", isColor: true, color: Colors.white, isBold: true, )), ), ) ], ) ], ), ); } }
修复要点说明
- 移除重复的
super.initState()调用,确保生命周期流程正确 - 将动画监听提取为独立方法,在
dispose时调用removeListener清理 - 所有使用
animationController的位置增加判空检查,配合mounted状态避免组件销毁后调用setState - 移除
initState中重复的GetX控制器注入 - 在
dispose中优先清理TabController和动画监听,再执行控制器销毁操作
内容的提问来源于stack exchange,提问作者Jay Soni
相关产品推荐
相关产品推荐

