How to "undispose" animation controller for reuse?
If you look at the SpinkitWave class you sent in the comments: https://github.com/jogboms/flutter_spinkit/blob/master/lib/src/wave.dart
You will see that the below is its initState method:
@override
void initState() {
super.initState();
_controller = (widget.controller ?? AnimationController(vsync: this, duration: widget.duration))..repeat();
}
What this is doing is saying if the user has passed in an AnimationController in as a parameter, then use that AnimationController, if not create a new one and use the duration that the user has passed in. Therefore if all you need the AnimationController for is to control the SpinKitWave widget, then I would suggest you are better off just passing in the duration to it and letting it then manage the AnimationController internally. i.e create the widget using the below.
// As you no longer need to handle the state of the AnimationController you can
// convert the ResultBox into a StatelessWidget
class ResultBox extends StatelessWidget {
final String time;
const ResultBox({
this.time,
Key key,
}) : super(key: key);
Widget build(BuildContext context) {
var _resultRow;
if (widget.time != null) {
_resultRow = Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Text(
widget.time,
style: TextStyle(color: Colors.white, fontSize: 50),
),
Text(
"ms",
style: TextStyle(color: Colors.white, fontSize: 20),
),
]);
} else {
// The controller argument has been removed here and the duration
// argument added
_resultRow = SpinKitWave(color: Colors.white, duration: const Duration(seconds: 1);
}
return FittedBox(
fit: BoxFit.fitWidth,
child: _resultRow,
);
}
}

Jonah Kornberg
Updated on December 26, 2022Comments
-
Jonah Kornberg less than a minute
I'm new to Flutter and having a bit of trouble with lifecycle methods and animation controllers.
When I press the bottom button it begins playing animation, when I press the circle in the center it stops the animation, and if I press the bottom button again I get the error:
AnimationController.stop() called after AnimationController.dispose() AnimationController methods should not be used after calling dispose. 'package:flutter/src/animation/animation_controller.dart': Failed assertion: line 767 pos 7: '_ticker != null'
I want the animation to begin playing again instead. Thanks for any advice!
import 'package:flutter/cupertino.dart'; import 'package:provider/provider.dart'; import 'package:flutter/material.dart'; import 'model/app_state_model.dart'; import 'dart:developer' as developer; import 'package:flutter_spinkit/flutter_spinkit.dart'; class ProductListTab extends StatefulWidget { @override _ProductListTabState createState() => _ProductListTabState(); } class _ProductListTabState extends State<ProductListTab> with SingleTickerProviderStateMixin { var _isActive = true; var _time = " "; var _resultBox; @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded( flex: 3, child: Container( color: Colors.lightBlue, child: Padding( padding: const EdgeInsets.all(30.0), child: Align( alignment: Alignment(0, .6), child: Container( width: 150, height: 150, child: OutlinedButton( //elevation: 0.0, style: OutlinedButton.styleFrom( shape: CircleBorder(), backgroundColor: Colors.blue), onPressed: () { setState(() { _time = "999"; }); }, child: AnimatedSwitcher( duration: Duration(seconds: 1), child: ResultBox(time: _time), ))), )), )), Expanded( flex: 2, child: Padding( padding: const EdgeInsets.all(30), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ CupertinoButton( child: Text(_isActive ? 'Cancel' : 'Ready'), onPressed: () { setState(() { _isActive = !_isActive; _time = null; }); }, color: Colors.amber, ) ], ), ), ) ], ); } } class ResultBox extends StatefulWidget { final String time; const ResultBox({ String this.time, Key key, }) : super(key: key); @override _ResultBoxState createState() => _ResultBoxState(); } class _ResultBoxState extends State<ResultBox> with SingleTickerProviderStateMixin { AnimationController _controller; void initState() { this._controller = AnimationController(vsync: this, duration: Duration(seconds: 1)); super.initState(); _controller.reset(); } Widget build(BuildContext context) { var _resultRow; if (widget.time != null) { _resultRow = Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Text( widget.time, style: TextStyle(color: Colors.white, fontSize: 50), ), Text( "ms", style: TextStyle(color: Colors.white, fontSize: 20), ), ]); } else { _resultRow = SpinKitWave(color: Colors.white, controller: _controller); } return FittedBox( fit: BoxFit.fitWidth, child: _resultRow, ); } Widget dispose() { this._controller.dispose(); } }
-
JayDev almost 2 yearsCan you add the code for SpinKitWave please? - Nowhere in your code above does it call AnimationController.stop(). I'm assuming its that component that is doing it. As a first note though your dispose method isn't the lifecycle dispose method as it has a return type of Widget. The lifecycle event has a return type of void
-
Jonah Kornberg almost 2 years@JayDev It's not because of SpinKitWave, its because the whole _ResultBox is re-rendered when _time changes that the animation disappears. I suppose this probably is not good practice Edit: You might be right actually because there is no Stop called
-
Jonah Kornberg almost 2 yearsThe code for SpinKitWave can be found here: github.com/jogboms/flutter_spinkit/blob/master/lib/src/…
-
JayDev almost 2 yearsAnimationControllers can be abit tricky. But in this use case I don't think you really need to use it, as if you let it the SpinKitWave widget will create and manage its own. I'll show you what I mean in an answer
-
-
Jonah Kornberg almost 2 yearsThank you so much for reading through it and helping :) I have it working now.