官方文档
必要的概念和类
-
Animation对象,是Flutter动画库中的核心类,插入用于引导动画的值 -
Animation对象知道当前动画的状态(如:动画是否开始,停止,前进或者后退),但对屏幕上显示的内容一无所知 -
AnimationController对象管理着Animation -
CurvedAnimation将动画定义成非线性运动的动画 -
Tween在被动画对象使用的数据范围之间进行插值。例如,Tween可能会定义从红色到蓝色或从 0 到 255 的插值 - 使用
Listeners和StatusListeners来监听动画状态的变化
步骤
-
初始化一个
AnimationController对象AnimationController controller = AnimationController(duration: const Duration(milliseconds: 500), vsync: this); -
初始化一个
Animation对象, 并将AnimationController作为参数传递进去。这里的Animation对象是通过Tween对象的animate方法创建的Animation<int> alpha = IntTween(begin: 0, end: 255).animate(controller); -
调用
AnimationController的forward()方法执行动画controller.forward(); -
在
Widget的dispose()方法中调用释放资源controller.dispose();
案例
下面的案例是在给定的时间内改变 widget 的宽高
- 我们需要实时获取
Animation的value来赋给 widget 的宽高 - 要改变 widget 的宽高,那么就需要
setState(...)来让 widget 重绘
首先我们需要一个可以改变状态的 widget,也就是继承自 StatefulWidget ,然后按照我们上述的步骤来,代码如下:
class AnimateApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _AnimateAppState();
}
}
class _AnimateAppState extends State<AnimateApp> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
@override
void initState() {
super.initState();
// 创建 AnimationController 对象
controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 2000));
// 通过 Tween 对象 创建 Animation 对象
animation = Tween(begin: 50.0, end: 200.0).animate(controller)
..addListener(() {
// 注意:这句不能少,否则 widget 不会重绘,也就看不到动画效果
setState(() {});
})
// 执行动画
controller.forward();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AnimateApp',
theme: ThemeData(
primaryColor: Colors.redAccent
),
home: Scaffold(
appBar: AppBar(
title: Text('AnimateApp'),
),
body: Center(
child: Container(
// 获取动画的值赋给 widget 的宽高
width: animation.value,
height: animation.value,
decoration: BoxDecoration(
color: Colors.redAccent
),
),
)
)
);
}
@override
void dispose() {
// 资源释放
controller.dispose();
super.dispose();
}
}
效果如下:
可以看出上面的动画是线性运动的,我们可以通过 CurvedAnimation 来实现非线性运动的动画代码如下:
controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 2000));
// 非线性动画
final CurvedAnimation curve = CurvedAnimation(
parent: controller, curve: Curves.elasticOut);
animation = Tween(begin: 50.0, end: 200.0).animate(curve)
..addListener(() {
setState(() {});
});
效果如下:
然后我们还可以给动画添加状态监听,通过给 Animation 添加 addStatusListener(...) 来监听当前动画的状态,如:动画是否播放完成。我们可以给上面的例子加一个状态监听,让动画无限执行:
animation = Tween(begin: 50.0, end: 200.0).animate(curve)
..addListener(() {
setState(() {});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
AnimationStatus.completed 表示动画在结束时停止的状态,这个时候我们让动画反向执行(从后往前);AnimationStatus.dismissed 表示动画在开始时就停止的状态,这个时候我们让动画正常执行(从前往后)。这样就可以让动画无限执行了。
Tween 还可以接受 Color 类型的参数,实现颜色渐变的效果,下面让 widget 的颜色从 红色 渐变到 蓝色,部分代码如下:
controller = AnimationController(
duration: const Duration(milliseconds: 3000), vsync: this);
animation = ColorTween(begin: Colors.redAccent, end: Colors.blue).animate(
controller)
..addListener(() {
setState(() {});
});
controller.forward();
...
child: Container(
decoration: BoxDecoration(
color: animation.value
),
margin: EdgeInsets.symmetric(vertical: 10.0),
height: 200.0,
width: 200.0,
),
效果如下:
小提示
with
我们可以看到 _AnimateAppState 类后面跟了一个 with SingleTickerProviderStateMixin,这个 with 什么意思呢 ? with 是 Dart 中的一个关键字,可以把它理解为 混入(mixin) 。可以看 stackoverflow 上的这个回答。
上述代码中 _AnimateAppState 类 继承了 State 类,但是初始化 AnimationController 的时候需要一个 TickerProvider 类型的 vsync 参数,所以我们 混入了 TickerProvider 的子类 SingleTickerProviderStateMixin
看一个简单的混入的小例子
void main() {
var a = new A();
a.methodB();
a.methodC();
a.methodD();
new E(a);
}
class A extends B with C, D {
}
class B {
void methodB() {
print('Class B methodB is call');
}
}
class C {
void methodC() {
print('Class C methodC is call');
}
}
class D {
void methodD() {
print('Class D methodD is call');
}
}
class E {
final C c;
E(this.c) {
c.methodC();
}
}
可以看出类 A 继承了类 B,然后 混入 了类 C 和类 D,然后在 main 方法中可以使用类 A 的实例去调用 类 B, C, D 中的方法。然后还有类 E,构造方法中需要一个类 C 作为参数,然后因为类 A 混入了类 C,所以可以把类 A 的实例当作参数传入到类 E 的构造方法中。
运行输出如下:
Class B methodB is call
Class C methodC is call
Class D methodD is call
Class C methodC is call
..addListener
在上面的例子中,我们看到了这样的写法
animation = Tween(begin: 50.0, end: 200.0).animate(curve)
..addListener(() {
setState(() {});
});
注意到 addListener 前面的两个点号 ..了吧,什么意思呢?直接看个小例子吧!
void main() {
List<String> list = getList()
..add("android")
..add("flutter")
..add("kotlin")
..removeAt(0);
list.forEach((item) {
print(item);
});
// ----------等同于
print('---------------------------');
List<String> list2 = getList();
list2.add("android");
list2.add("flutter");
list2.add("kotlin");
list2.removeAt(0);
list2.forEach((item) {
print(item);
});
}
List<String> getList() {
return new List();
}
输入如下:
flutter
kotlin
---------------------------
flutter
kotlin
如有错误,还请指出,谢谢!










