You can use addStatusListener on your Animation. Check when the animation is completed and then call reverse() on your AnimationController.
If you want to, you can call reverse() inside a Future.delayed() for making a pause.
I've made this example for you:
import 'package:flutter/material.dart'; class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> with TickerProviderStateMixin { AnimationController _animationController; Animation _opacityDontWorry; @override void initState() { super.initState(); _animationController = AnimationController(duration: Duration(seconds: 1), vsync: this); _opacityDontWorry = Tween( begin: 0.0, end: 1.0, ).animate( CurvedAnimation(parent: _animationController, curve: Curves.easeIn), )..addStatusListener((status) { if (status == AnimationStatus.completed) { Future.delayed(Duration(seconds: 3), () { _animationController.reverse(); }); } }); } @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton.extended( label: Text('Animate'), onPressed: () => _animationController.forward(), ), body: Center( child: AnimatedBuilder( animation: _opacityDontWorry, builder: (context, widget) { return Opacity( opacity: _opacityDontWorry.value, child: Text("Don't worry"), ); }, ), ), ); } }
UPDATE
In case you need to play this animation, and call another one after that, you can extract the opacity value to a variable. Then update that value from as many consecutive animations as you need.
_firstAnimation = Tween( begin: 0.0, end: 1.0, ).animate( CurvedAnimation(parent: _animationController, curve: Interval(0.0, 0.20, curve: Curves.easeIn)), )..addListener(() { setState(() => _opacity = _firstAnimation.value); }); // Leave an interval pause if you need _secondAnimation = Tween( begin: 1.0, end: 0.0, ).animate( CurvedAnimation(parent: _animationController, curve: Interval(0.40, 0.60, curve: Curves.easeIn)), )..addListener(() { setState(() => _opacity = _secondAnimation.value); });
In your widget's opacity property, instead of using _firstAnimation.value use _opacity.