0

I'm animating a widget using AnimationController (The widget is the red wave shown in the image attached). The widget starts with visibility = false and turns true for periods of 10 seconds after the user hits the red button to speak. The problem I'm facing is that when hitting the red button for a second time I get the error:

AnimationController.stop() called after AnimationController.dispose().

And the widget never shows again. Since I'm not disposing the widget just hiding it, I can't understand what is going on. I have tried so far:

  • Create the _controller outside/inside widget build.
  • Check if the widget is mounted before calling it
  • Change the AnimationController state to false anytime the widget is hidden.

None has worked. Any idea what is wrong in my code:
spinkitWaveWidget

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
  var _controller;   
  var spinkitWave;
  stt.SpeechToText speech = stt.SpeechToText();
   
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync:this, duration: Duration(seconds:1), lowerBound:0, upperBound:0.1)
     ..addStatusListener((status) {
            if (status == AnimationStatus.completed) {
             if (mounted) {
              _controller.reverse();
             }
  }});         

  @override
  dispose() {
    _controller.dispose(); // you need this
       super.dispose();
  } 

  void startListening() {
   _controller = AnimationController(vsync:this, duration: Duration(seconds:1), lowerBound:0, upperBound:0.1);
    speech.listen(onResult: resultListener,
    onSoundLevelChange: soundLevelListener,
    cancelOnError: true,);
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
      spinkitWave = SpinKitWave(
          color: Colors.redAccent,
          type: SpinKitWaveType.center,
        controller: _controller,
        );    
       
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.red,
        ),
        home: Builder(
            builder: (context) => Scaffold(
                  appBar: AppBar(
                           title: Text("Leurebeng"),
                  ),
                  body: Center(
                        Positioned(
                          bottom: 10,
                          child: Stack(
                              alignment: AlignmentDirectional.bottomCenter,
                              children: <Widget>[
                                SizedBox
                                width: 110.0,
                                  height: 110.0,
                                  child: Visibility(
                                    visible: !speech.isListening,
                                    child: FloatingActionButton(
                                      onPressed:
                                        _available ? startListening : initSpeechState,
                                      tooltip: 'Increment',
                                      child: Icon(Icons.mic),
                                    ),
                                  ),
                                ),
                                Visibility(
                                  visible: speech.isListening, //Turns true or false after red button pressed
                                  child: 
                                  spinkitWave
                                ),
                              ]),
                        ),                 
                    ),
                  ),
          );
    }
  }
}
OroshiX
  • 712
  • 1
  • 8
  • 28
Ruben
  • 816
  • 1
  • 8
  • 21

3 Answers3

0

I think the problem is, that you declare _controller 2 times. First time in initState(){} where it starts playing and second time in startListening(){} where it is overridden and never played again. If you want to start/stop the animation, you can do it like this :

_controller.isAnimating
    ? _controller.stop()
    : _controller.forward();
Christian
  • 834
  • 7
  • 18
0

Few things you need to clear it before applying actual solution here are

  1. Neven use var keyword to initialize a disposable type
  2. Don't reinitialize your variable after declaration like how you have done it in initState and StartListening

To solve your problem you can wrap your dispose method as

if(_animationController){
  _animationController.dispose()
}

And second, try using

if(mounted){
  // your code
}

on the callback you have created which will prevent for the error you are facing

Piyush Dubey
  • 276
  • 1
  • 13
0

dispose() is being called on the _controller when the animation completes.

As noted by other commenters, there are a few things that need to be addressed with your code, but I will focus on the solutions to your AnimationController problems.

There are a few ways that you could prevent this error. The simplest is to call _controller.repeat(reverse: true); after initializing your controller in initState. This will cause it to run back and forth indefinitely. Then you can simply toggle _isListening to show/hide the animation.

You could also remove the initialization in initState and re-initialize upon every call to startListening(). If you do this, you will need to be sure to dispose of the controller before initializing a new one. This can be done by calling _controller.stop(). According to your needs, this can be a callback after a set Duration via Future.delayed(), upon release of a button, or any number of other methods.

Lee3
  • 2,882
  • 1
  • 11
  • 19