动画,顾名思义就是让页面元素动起来
介绍 AnimationController,Tween,Transform,AnimationBuilder
AnimationController 动画控制,本质上看 是设置了变化区间为0.0到1.0的 Animation
Tween 专注于动画值的计算,用法是将AnimationController的变化范围映射为具体的效果范围,例如尺寸动画,颜色动画等
dart
// 1. 尺寸动画:50 -> 200
_sizeAnimation = Tween<double>(begin: 50, end: 200).animate(_controller);
// 2. 颜色动画:蓝色 -> 红色
_colorAnimation = ColorTween(
begin: Colors.blue,
end: Colors.red,
).animate(_controller);
// 3. 边框动画:方形 -> 圆形
_borderAnimation = BorderRadiusTween(
begin: BorderRadius.circular(0),
end: BorderRadius.circular(100),
).animate(_controller);
// 4. 位置动画:从左上到右下
_offsetAnimation = Tween<Offset>(
begin: const Offset(-100, -100),
end: const Offset(100, 100),
).animate(_controller);
使用 Curve 和 CurvedAnimation 设置动画曲线
dart
_controller
= AnimationController(
vsync
: this,
duration
: const Duration(seconds: 3),
)..repeat(reverse: true);
// 使用不同的曲线
_linearAnimation
= Tween(begin: 0.0, end: 300.0).animate(
CurvedAnimation(
parent
: _controller,
curve
: Curves.linear,
),
);
Transform 用于翻转,缩放,旋转 Widget
dart
AnimatedBuilder(
animation: _transformAnimation,
builder: (context, child) {
return Transform.translate(
offset: Offset(_transformAnimation.value * 100, 0),
child: _buildBox('平移', Colors.blue),
);
},
),
// 2. 旋转变换
AnimatedBuilder(
animation: _transformAnimation,
builder: (context, child) {
return Transform.rotate(
angle: _transformAnimation.value * 3.14159, // 0-π 弧度
child: _buildBox('旋转', Colors.green),
);
},
),
// 3. 缩放变换
AnimatedBuilder(
animation: _transformAnimation,
builder: (context, child) {
return Transform.scale(
scale: 0.5 + _transformAnimation.value * 0.5, // 0.5-1.0
child: _buildBox('缩放', Colors.red),
);
},
),
// 4. 3D 变换(沿 Y 轴旋转)
AnimatedBuilder(
animation: _transformAnimation,
builder: (context, child) {
return Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001) // 透视
..rotateY(_transformAnimation.value * 3.14159),
alignment: Alignment.center,
child: _buildBox('3D旋转', Colors.purple),
);
},
),
隐式动画及自定义动画介绍
隐式动画
一般用于:简单的动画交互展示,比如按钮从不可点击到可点击,从方变圆. 为动画渐变效果
dart
import 'package:flutter/material.dart';
class AnimatedWidgetsTest extends StatefulWidget {
const AnimatedWidgetsTest({Key? key}) : super(key: key);
@override
_AnimatedWidgetsTestState createState() => _AnimatedWidgetsTestState();
}
class _AnimatedWidgetsTestState extends State<AnimatedWidgetsTest> {
double _padding = 10;
var _align = Alignment.topRight;
double _height = 100;
double _left = 0;
Color _color = Colors.red;
TextStyle _style = const TextStyle(color: Colors.black);
Color _decorationColor = Colors.blue;
double _opacity = 1;
@override
Widget build(BuildContext context) {
var duration = const Duration(milliseconds: 400);
return SingleChildScrollView(
child: Column(
children: <Widget>[
ElevatedButton(
onPressed: () {
setState(() {
_padding = 20;
});
},
child: AnimatedPadding(
duration: duration,
padding: EdgeInsets.all(_padding),
child: const Text("AnimatedPadding"),
),
),
SizedBox(
height: 50,
child: Stack(
children: <Widget>[
AnimatedPositioned(
duration: duration,
left: _left,
child: ElevatedButton(
onPressed: () {
setState(() {
_left = 100;
});
},
child: const Text("AnimatedPositioned"),
),
)
],
),
),
Container(
height: 100,
color: Colors.grey,
child: AnimatedAlign(
duration: duration,
alignment: _align,
child: ElevatedButton(
onPressed: () {
setState(() {
_align = Alignment.center;
});
},
child: const Text("AnimatedAlign"),
),
),
),
AnimatedContainer(
duration: duration,
height: _height,
color: _color,
child: TextButton(
onPressed: () {
setState(() {
_height = 150;
_color = Colors.blue;
});
},
child: const Text(
"AnimatedContainer",
style: TextStyle(color: Colors.white),
),
),
),
AnimatedDefaultTextStyle(
child: GestureDetector(
child: const Text("hello world"),
onTap: () {
setState(() {
_style = const TextStyle(
color: Colors.blue,
decorationStyle: TextDecorationStyle.solid,
decorationColor: Colors.blue,
);
});
},
),
style: _style,
duration: duration,
),
AnimatedOpacity(
opacity: _opacity,
duration: duration,
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue)),
onPressed: () {
setState(() {
_opacity = 0.2;
});
},
child: const Text(
"AnimatedOpacity",
style: TextStyle(color: Colors.white),
),
),
),
// AnimatedDecoratedBox1(
// duration: Duration(
// milliseconds: _decorationColor == Colors.red ? 400 : 2000),
// decoration: BoxDecoration(color: _decorationColor),
// child: Builder(builder: (context) {
// return TextButton(
// onPressed: () {
// setState(() {
// _decorationColor = _decorationColor == Colors.blue
// ? Colors.red
// : Colors.blue;
// });
// },
// child: const Text(
// "AnimatedDecoratedBox toggle",
// style: TextStyle(color: Colors.white),
// ),
// );
// }),
// )
].map((e) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: e,
);
}).toList(),
),
);
}
}
性能优化
1. 使用AnimatedBuilder优化性能
使用AnimatedBuilder 代替setState()({}), 用部分重绘代替全页面重绘
2. 使用TweenAnimationBuilder简化代码
dart
TweenAnimationBuilder<Color?>(
duration: Duration(milliseconds: 2000),
tween: ColorTween(
begin: Colors.red,
end: Colors.blue,
),
builder: (context, color, child) {
return Container(
width: 100,
height: 100,
color: color,
child: child,
);
},
child: Icon(Icons.star, color: Colors.white),
)
自定义动画
总结
1.AnimationController 是大脑:控制时间线,生成0.0-1.0的基础值
作用:控制动画的播放,包括开始、停止、前进、后退等。它生成一个介于0.0到1.0之间的值(默认),可以指定动画的时长、重复次数等。
关系:它是动画的"驱动器",是Animation的子类,因此本身就是一个动画(输出0.0到1.0的线性值)。它通常与Tween配合,将0.0到1.0映射到其他值(如颜色、大小等)。
2.Tween 是翻译官:将基础值翻译成具体的应用值(颜色、大小等)
作用:定义动画的起始值和结束值,并定义了如何在这两个值之间进行插值(线性或非线性)。例如,Tween可以定义从10.0到200.0的补间,ColorTween可以定义从红色到蓝色的补间。
关系:Tween本身并不是动画,它需要结合Animation(比如AnimationController)才能生成具体的值。通过animate方法传入一个动画(比如AnimationController),返回一个Animation对象。
3.Animation 是数据流:承载具体的动画数值,通知监听器
作用:是一个值的容器,这个值在动画的每一帧中都会改变。它知道当前动画的值和状态(比如是否完成),但不知道如何渲染。
关系:Tween.animate(AnimationController) 返回一个Animation对象,这个对象在动画过程中会输出从Tween.begin到Tween.end的值。
4.AnimatedBuilder 是桥梁:连接动画数据和UI构建
作用:是一个Widget,它监听Animation对象,当动画值改变时,会重新调用builder方法重建子Widget树。它能够将动画与Widget构建分离,提高性能。
关系:它需要传入一个Animation对象和一个builder函数。在builder函数中,我们可以根据动画的当前值来构建Widget。
5.Transform/AnimatedContainer 是执行者:将动画值应用到UI变化上
Transform
作用:是一个Widget,可以对子Widget进行矩阵变换(如平移、旋转、缩放等)。
关系:在动画中,我们经常使用Transform来根据动画值改变Widget的位置、大小或旋转角度。
AnimatedContainer
作用:是一个特殊的Container,当它的属性(如宽度、高度、颜色等)改变时,会自动以动画方式过渡到新值。它内部实际上封装了AnimationController、Tween和AnimatedBuilder等。
关系:它简化了动画的创建,只需要设置新的属性值,动画就会自动执行。适用于简单的属性动画。