- CurvedAnimation(曲线动画)
- Tween(补间动画/伸缩动画)
- Loading加载动画(ProgressDialog)
- Fade 渐入淡出动画
- AnimatedBuilder(曲线动画)
- StaggerAnimation(交错动画)
- AnimatedSwitcher(切换动画)
1.CurvedAnimation(曲线动画)
效果图

曲线.gif
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import 'package:flutter/material.dart'; class CurvedAnimationPage extends StatefulWidget { CurvedAnimationPage({Key key, this.title}) : super(key: key); final String title; @override _CurvedAnimationState createState() => _CurvedAnimationState(); } class _CurvedAnimationState extends State<CurvedAnimationPage> with SingleTickerProviderStateMixin { Animation<double> animation; AnimationController controller; @override void initState() { super.initState(); controller = AnimationController( duration: const Duration(milliseconds: 2000), vsync: this); animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn) ..addStatusListener((status) { if (status == AnimationStatus.completed) { controller.reverse(); } else if (status == AnimationStatus.dismissed) { controller.forward(); } }); controller.forward(); } dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedLogo(animation: animation); } } class AnimatedLogo extends AnimatedWidget { // The Tweens are static because they don't change. static final _opacityTween = Tween<double>(begin: 0.1, end: 1.0); static final _sizeTween = Tween<double>(begin: 0.0, end: 300.0); AnimatedLogo({Key key, Animation<double> animation}) : super(key: key, listenable: animation); Widget build(BuildContext context) { final Animation<double> animation = listenable; return Scaffold( appBar: AppBar( title: Text("曲线动画"), ), body: Center( child: Opacity( opacity: _opacityTween.evaluate(animation), child: Container( margin: EdgeInsets.symmetric(vertical: 10.0), height: _sizeTween.evaluate(animation), width: _sizeTween.evaluate(animation), child: FlutterLogo(), )), )); } } |
- parent参数传入一个Animation对象,比如AnimationController
- curve传入传入的就是动画曲线的具体实现,Curves类中已经帮我们默认实现了很多常用的动画曲线,比如减速运动Curves.decelerate
Curves曲线 动画过程
linear 匀速的
decelerate 匀减速
ease 开始加速,后面减速
easeIn 开始慢,后面快
easeOut 开始快,后面慢
easeInOut 开始慢,然后加速,最后再减速
2.Tween(补间动画/伸缩动画)
效果图

补间动画.gif
在补间动画中,定义了开始点和结束点、时间线以及定义转换时间和速度的曲线。然后由框架计算如何从开始点过渡到结束点。
Animation 对象,是 Flutter 动画库中的核心类,插入用于引导动画的值。
Animation 对象知道当前动画的状态(如:动画是否开始,停止,前进或者后退),但对屏幕上显示的内容一无所知。
AnimationController 对象管理着 Animation。
CurvedAnimation 将动画定义成非线性运动的动画。
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | import 'package:flutter/material.dart'; class TweenAnimation extends StatefulWidget { TweenAnimation({Key key, this.title}) : super(key: key); final String title; @override _TweenAnimationState createState() => _TweenAnimationState(); } class _TweenAnimationState extends State<TweenAnimation> with SingleTickerProviderStateMixin { Animation<double> animation; AnimationController controller; @override void initState() { super.initState(); controller = AnimationController( duration: const Duration(milliseconds: 2000), vsync: this); animation = Tween(begin: 0.0, end: 300.0).animate(controller) ..addListener(() { setState(() { print(animation.value); }); }) ..addStatusListener((status) { if (status == AnimationStatus.completed) { controller.reverse(); } else if (status == AnimationStatus.dismissed) { controller.forward(); } }); controller.forward(); } dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("补间动画"), ), body: Container( margin: EdgeInsets.symmetric(vertical: 10.0), height: animation.value, width: animation.value, child: FlutterLogo(), )); } } |
3.Loading加载动画(ProgressDialog)
效果图

Loading.gif
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | import 'package:flutter/material.dart'; import 'package:flutter_animations/progress_dialog.dart'; class LoadingPage extends StatefulWidget { LoadingPage({Key key, this.title}) : super(key: key); final String title; @override _LoadingState createState() => _LoadingState(); } class _LoadingState extends State<LoadingPage> { bool _loading = false; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("加载动画"), ), body: ProgressDialog( isLoading: _loading, message: '正在加载...', alpha: 0.35, child: Center( child: RaisedButton( onPressed: () => _onRefresh(), child: Text('显示加载动画'), ), ), ), ); } Future<Null> _onRefresh() async { setState(() { _loading = !_loading; }); // 模拟耗时操作 await Future.delayed(Duration(seconds: 5), () { setState(() { _loading = !_loading; }); }); } } |
4.Fade 渐入淡出动画
效果图

渐入淡出.gif
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | import 'package:flutter/material.dart'; class Fade extends StatefulWidget { Fade({Key key, this.title}) : super(key: key); final String title; @override _FadeState createState() => _FadeState(); } class _FadeState extends State<Fade> { bool _visible = true; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("渐入淡出动画"), ), body: Center( child: AnimatedOpacity( opacity: _visible ? 1.0 : 0.0, duration: Duration(milliseconds: 500), child: FlutterLogo( size: 100.0, )), ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { _visible = !_visible; }); }, tooltip: "Toggole Opacity", child: Icon(Icons.flip), ), ); } } |
5.AnimatedBuilder(曲线动画)
效果图

AnimatedBuilder.gif
- 创建动画的widget
- Key key,
- @required Listenable animation,
- @required this.builder,
- this.child,
- animation:Animationcontroller //动画
- child 动画作用的view
- builder:每次controller值改变都会回到builder 重新生成view
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | import 'package:flutter/material.dart'; class AnimatedBuilderPage extends StatefulWidget { AnimatedBuilderPage({Key key, this.title}) : super(key: key); final String title; @override _AnimatedBuilderState createState() => _AnimatedBuilderState(); } class _AnimatedBuilderState extends State<AnimatedBuilderPage> with SingleTickerProviderStateMixin { Animation<double> animation; AnimationController controller; @override void initState() { super.initState(); controller = AnimationController( duration: const Duration(milliseconds: 2000), vsync: this); final CurvedAnimation curve = CurvedAnimation(parent: controller, curve: Curves.bounceIn); animation = Tween(begin: 0.0, end: 300.0).animate(curve) ..addStatusListener((status) { if (status == AnimationStatus.completed) { controller.reverse(); } else if (status == AnimationStatus.dismissed) { controller.forward(); } }); controller.forward(); } dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return GrowTransition(child: LogoWidget(), animation: animation); } } class LogoWidget extends StatelessWidget { build(BuildContext context) { return Container( margin: EdgeInsets.symmetric(vertical: 10.0), child: FlutterLogo(), ); } } class GrowTransition extends StatelessWidget { GrowTransition({this.child, this.animation}); final Widget child; final Animation<double> animation; Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("曲线动画AnimatedBuilder实现"), ), body: Center( child: AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget child) { return Container( height: animation.value, width: animation.value, child: child); }, child: child), ), ); } } |
6.StaggerAnimation(交错动画)
交错动画由序列或重叠的动画组成,
要创建交错动画,使用多个动画对象,
一个AnimationController控制所有动画。
每个动画对象在间隔期间指定动画。
效果图

StaggerAnimation.gif
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter/scheduler.dart' show timeDilation; class StaggerAnimation extends StatelessWidget { StaggerAnimation({Key key, this.controller}): opacity = Tween<double>( begin: 0.0, end: 1.0, ).animate( CurvedAnimation( parent: controller, curve: Interval( 0.0, 0.100, curve: Curves.ease, ), ), ), width = Tween<double>( begin: 50.0, end: 150.0, ).animate( CurvedAnimation( parent: controller, curve: Interval( 0.125, 0.250, curve: Curves.ease, ), ), ), height = Tween<double>(begin: 50.0, end: 150.0).animate( CurvedAnimation( parent: controller, curve: Interval( 0.250, 0.375, curve: Curves.ease, ), ), ), padding = EdgeInsetsTween( begin: const EdgeInsets.only(bottom: 16.0), end: const EdgeInsets.only(bottom: 75.0), ).animate( CurvedAnimation( parent: controller, curve: Interval( 0.250, 0.375, curve: Curves.ease, ), ), ), borderRadius = BorderRadiusTween( begin: BorderRadius.circular(4.0), end: BorderRadius.circular(75.0), ).animate( CurvedAnimation( parent: controller, curve: Interval( 0.375, 0.500, curve: Curves.ease, ), ), ), color = ColorTween( begin: Colors.indigo[100], end: Colors.orange[400], ).animate( CurvedAnimation( parent: controller, curve: Interval( 0.500, 0.750, curve: Curves.ease, ), ), ), super(key: key); final Animation<double> controller; final Animation<double> opacity; final Animation<double> width; final Animation<double> height; final Animation<EdgeInsets> padding; final Animation<BorderRadius> borderRadius; final Animation<Color> color; // This function is called each time the controller "ticks" a new frame. // When it runs, all of the animation's values will have been // updated to reflect the controller's current value. Widget _buildAnimation(BuildContext context, Widget child) { return Container( padding: padding.value, alignment: Alignment.bottomCenter, child: Opacity( opacity: opacity.value, child: Container( width: width.value, height: height.value, decoration: BoxDecoration( color: color.value, border: Border.all( color: Colors.indigo[300], width: 3.0, ), borderRadius: borderRadius.value, ), ), ), ); } @override Widget build(BuildContext context) { return AnimatedBuilder( builder: _buildAnimation, animation: controller, ); } } class StaggerDemo extends StatefulWidget { @override _StaggerDemoState createState() => _StaggerDemoState(); } class _StaggerDemoState extends State<StaggerDemo> with TickerProviderStateMixin { AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 2000), vsync: this); } @override void dispose() { _controller.dispose(); super.dispose(); } Future<void> _playAnimation() async { try { await _controller.forward().orCancel; await _controller.reverse().orCancel; } on TickerCanceled { // the animation got canceled, probably because we were disposed } } @override Widget build(BuildContext context) { timeDilation = 10.0; // 1.0 is normal animation speed. return Scaffold( appBar: AppBar( title: const Text('Staggered Animation'), ), body: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { _playAnimation(); }, child: Center( child: Container( width: 300.0, height: 300.0, decoration: BoxDecoration( color: Colors.black.withOpacity(0.1), border: Border.all( color: Colors.black.withOpacity(0.5), ), ), child: StaggerAnimation(controller: _controller.view), ), ), ), ); } } |
7.AnimatedSwitcher(切换动画)
实际开发中,我们经常会遇到切换UI元素的场景,比如Tab切换、路由切换。为了增强用户体验,通常在切换时都会指定一个动画,以使切换过程显得平滑。
效果图

切换动画.gif
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | import 'package:flutter/material.dart'; class AnimatedSwitcherDemo extends StatefulWidget { AnimatedSwitcherDemo({Key key, this.title}) : super(key: key); final String title; @override _AnimatedSwitcherState createState() => _AnimatedSwitcherState(); } class _AnimatedSwitcherState extends State<AnimatedSwitcherDemo> with SingleTickerProviderStateMixin { IconData _actionIcon = Icons.favorite_border; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('点击中间的??'), actions: <Widget>[], ), body: Center( child: AnimatedSwitcher( transitionBuilder: (child, anim) { return ScaleTransition(child: child, scale: anim); }, duration: Duration(milliseconds: 350), child: IconButton( iconSize: 100, key: ValueKey(_actionIcon), icon: Icon( _actionIcon, color: Colors.pink, ), onPressed: () { setState( () { if (_actionIcon == Icons.favorite_border) _actionIcon = Icons.favorite; else _actionIcon = Icons.favorite_border; }, ); }, ), ), ), ); } } |