J'anime un widget en utilisant AnimationController (Le widget est la vague rouge montrée dans l'image ci-jointe). Le widget commence par visibility = false
et devient vrai pendant des périodes de 10 secondes après que l'utilisateur a appuyé sur le bouton rouge pour parler. Le problème auquel je suis confronté est que lorsque j'appuie sur le bouton rouge une deuxième fois, j'obtiens l'erreur:
AnimationController.stop () appelé après AnimationController.dispose ().
Et le widget ne s'affiche plus jamais. Puisque je ne suis pas en train de supprimer le widget, je ne peux pas comprendre ce qui se passe. J'ai essayé jusqu'à présent:
_controller
extérieur / intérieur.AnimationController
sur false à chaque fois que le widget est masqué. Aucun n'a fonctionné. Toute idée de ce qui ne va pas dans mon code:
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 ), ]), ), ), ), ); } } }
3 Réponses :
Je pense que le problème est que vous déclarez _controller
2 fois. Première fois dans initState(){}
où il commence à jouer et deuxième fois dans startListening(){}
où il est écrasé et jamais lu à nouveau. Si vous souhaitez démarrer / arrêter l'animation, vous pouvez le faire comme ceci:
_controller.isAnimating ? _controller.stop() : _controller.forward();
Peu de choses dont vous avez besoin pour le nettoyer avant d'appliquer la solution réelle sont ici
Pour résoudre votre problème, vous pouvez emballer votre méthode d'élimination comme
if(mounted){ // your code }
Et deuxièmement, essayez d'utiliser
if(_animationController){ _animationController.dispose() }
sur le rappel que vous avez créé qui empêchera l'erreur que vous rencontrez
dispose()
est appelé sur le _controller
fois l'animation terminée.
Comme indiqué par d'autres commentateurs, il y a quelques points à résoudre avec votre code, mais je me concentrerai sur les solutions à vos problèmes d' AnimationController
.
Vous pouvez éviter cette erreur de plusieurs manières. Le plus simple est d'appeler _controller.repeat(reverse: true);
après avoir initialisé votre contrôleur dans initState
. Cela le fera aller et venir indéfiniment. Ensuite, vous pouvez simplement basculer _isListening
pour afficher / masquer l'animation.
Vous pouvez également supprimer l'initialisation dans initState
et la réinitialiser à chaque appel à startListening()
. Si vous faites cela, vous devrez vous assurer de disposer du contrôleur avant d'en initialiser un nouveau. Cela peut être fait en appelant _controller.stop()
. Selon vos besoins, cela peut être un rappel après une Duration
définie via Future.delayed()
, lors du relâchement d'un bouton, ou de toute autre méthode.