动画类型
- 补间动画
- 基于物理基础的动画
常见动画模式
- 列表或网格动画
- 共享元素转换
- 交织动画
基本动画概念和类
- Animation,Flutter 动画库中的核心类,插入用于指导动画的值。
- Animation 对象知道动画目前的状态(例如,是否开始,暂停,前进或倒退),但是对屏幕上显示的内容一无所知。
- AnimationController 管理 Animation,范围为[0.0, 1.0]
- CurvedAnimation 定义进程为非线性曲线。
- Tween 为动画对象插入一个范围值。例如,Tween 可以定义插入值由红到蓝,或从 0 到 255。
- 使用 Listener 和 StatusListener 监视动画状态变化。
CurvedAnimation 和 AnimationController(下面将会详细说明)都是 Animation 类型
动画通知
一个 Animation 对象可以有不止一个 Listener 和 StatusListener,用 addListener() 和 addStatusListener() 来定义。当动画值改变时调用 Listener。 Listener 最常用的操作是调用 setState() 进行重建。当一个动画开始,结束,前进或后退时,会调用 StatusListener,用 AnimationStatus 来定义
AnimationController
AnimationController
是用于控制动画的核心类。它提供了一些常用的方法和属性来管理动画的状态和行为。以下是一些常用的控制函数及其作用:
forward()
作用:使动画从当前值向前推进到结束值。
用法:通常在动画开始时调用
reverse()
作用:使动画从当前值向后推进到起始值。
用法:通常在动画结束后调用,以反向播放动画
stop()
作用:停止动画并保持在当前值。
用法:可以在需要时停止动画,例如用户交互时。
repeat({bool reverse = false})
作用:使动画在达到结束值后重新开始,可以选择是否反向播放。
用法:用于创建循环动画
forward(from)
作用:从指定的值开始向前推进动画。
用法:可以用于从动画的中间状态开始播放。
animateTo(double value, {Duration? duration, Curve curve = Curves.linear})
作用:将动画控制器的值动画到指定的值。
用法:用于在特定时间内平滑过渡到一个新的值
dispose()
作用:释放动画控制器占用的资源。
用法:在不再需要动画控制器时调用,通常在 dispose 方法中。
动画示例
初始化
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn)
..addStatusListener((status) {
print('$status');
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
scaleAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(animation);
class ScaleBasicAnimation extends StatelessWidget {
const ScaleBasicAnimation(
{required this.child, required this.scaleAnimation, super.key});
final Widget child;
final Animation<double> scaleAnimation;
@override
Widget build(BuildContext context) {
return Center(
child: ScaleTransition(
scale: scaleAnimation,
child: child,
),
);
}
}
ScaleBasicAnimation(scaleAnimation: scaleAnimation, child: const LogoWidget()),
旋转
rotationAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(animation);
class RotationBasicAnimation extends StatelessWidget {
const RotationBasicAnimation(
{required this.child, required this.rotateAnimation, super.key});
final Widget child;
final Animation<double> rotateAnimation;
@override
Widget build(BuildContext context) {
return Center(
child: RotationTransition(
turns: rotateAnimation,
child: child,
),
);
}
}
RotationBasicAnimation(
rotateAnimation: rotationAnimation,
child: const LogoWidget()),
淡入淡出
opacityAnimation = Tween<double>(begin: 0.1, end: 1.0).animate(animation);
class FadeBasicAnimation extends StatelessWidget {
const FadeBasicAnimation(
{required this.child, required this.opacityAnimation, super.key});
final Widget child;
final Animation<double> opacityAnimation;
@override
Widget build(BuildContext context) {
return Center(
child: FadeTransition(opacity: opacityAnimation, child: child),
);
}
}
FadeBasicAnimation(opacityAnimation: opacityAnimation,
child: const LogoWidget()),
平移
offsetAnimation = Tween<Offset>(
begin: const Offset(0.0, 0.0), end: const Offset(0.0, 1.0))
.animate(animation);
class SildeBasicAnimation extends StatelessWidget {
const SildeBasicAnimation(
{required this.child, required this.offsetAnimation, super.key});
final Widget child;
final Animation<Offset> offsetAnimation;
@override
Widget build(BuildContext context) {
return SlideTransition(
position: offsetAnimation,
child: child,
);
}
}
SildeBasicAnimation(offsetAnimation: offsetAnimation, child: const LogoWidget()),
过渡Container
AnimatedContainer(
// Use the properties stored in the State class.
width: _width,
height: _height,
decoration: BoxDecoration(
color: _color,
borderRadius: _borderRadius,
),
// Define how long the animation should take.
duration: const Duration(seconds: 1),
// Provide an optional curve to make the animation feel smoother.
curve: Curves.fastOutSlowIn,
),
),
onPressed: () {
// Use setState to rebuild the widget with new values.
setState(() {
// Create a random number generator.
final random = Random();
// Generate a random width and height.
_width = random.nextInt(300).toDouble();
_height = random.nextInt(300).toDouble();
// Generate a random color.
_color = Color.fromRGBO(
random.nextInt(256),
random.nextInt(256),
random.nextInt(256),
1,
);
// Generate a random border radius.
_borderRadius =
BorderRadius.circular(random.nextInt(100).toDouble());
});
淡入淡出widget
AnimatedOpacity(
// If the widget is visible, animate to 0.0 (invisible).
// If the widget is hidden, animate to 1.0 (fully visible).
opacity: _visible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
// The green box must be a child of the AnimatedOpacity widget.
child: Container(
width: 200,
height: 200,
color: Colors.green,
),
)