- CurvedAnimation(曲线动画)
- Tween(补间动画/伸缩动画)
- Loading加载动画(ProgressDialog)
- Fade 渐入淡出动画
- AnimatedBuilder(曲线动画)
- StaggerAnimation(交错动画)
- AnimatedSwitcher(切换动画)
1.CurvedAnimation(曲线动画)
效果图
关键代码
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(补间动画/伸缩动画)
效果图
在补间动画中,定义了开始点和结束点、时间线以及定义转换时间和速度的曲线。然后由框架计算如何从开始点过渡到结束点。
Animation 对象,是 Flutter 动画库中的核心类,插入用于引导动画的值。
Animation 对象知道当前动画的状态(如:动画是否开始,停止,前进或者后退),但对屏幕上显示的内容一无所知。
AnimationController 对象管理着 Animation。
CurvedAnimation 将动画定义成非线性运动的动画。
关键代码
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)
效果图
关键代码
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 渐入淡出动画
效果图
关键代码
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(曲线动画)
效果图
- 创建动画的widget
- Key key,
- @required Listenable animation,
- @required this.builder,
- this.child,
- animation:Animationcontroller //动画
- child 动画作用的view
- builder:每次controller值改变都会回到builder 重新生成view
关键代码
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控制所有动画。
每个动画对象在间隔期间指定动画。
效果图
关键代码
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切换、路由切换。为了增强用户体验,通常在切换时都会指定一个动画,以使切换过程显得平滑。
效果图
关键代码
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;
},
);
},
),
),
),
);
}
}