目录
动画类别
一、隐式(全自动)动画
二、显式动画(手动控制)
三、其他动画(CustomPainter)
动画类别
Flutter 中有多种类型的动画:
- 隐式动画:通过更改部件属性自动触发的预定义动画,例如
AnimatedContainer
、AnimatedOpacity
、AnimatedPadding
等等。 - 显式动画:通过使用
Animation
和AnimationController
类手动控制动画。 - 物理动画:基于物理规律的动画,例如
SpringSimulation
、FrictionSimulation
等等。
一、隐式(全自动)动画
AnimatedSwitcher(
duration: const Duration(seconds: 2),
transitionBuilder: (child, animation) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: animation,
child: child,
),
);
},
child: const Text("Hello"),
)
补间动画(已经确定了第一帧(0.0)和最后一帧(1.0),如果FPS 60 补充中间的58帧)
TweenAnimationBuilder(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(seconds: 5),
builder: (BuildContext context, Object? value, Widget? child) {
return Opacity(
opacity: value as double,
child: Container(
width: 300,
height: 300,
color: Colors.red[200],
),
);
}))
TweenAnimationBuilder(
tween: Tween(begin: 20.0, end: 50.0),
duration: const Duration(seconds: 5),
builder: (context,double value, Widget? child) {
return Container(
width: 300,
height: 300,
color: Colors.red[200],
child: Text('Hello', style: TextStyle(fontSize: value)));
}))
二、显式动画(手动控制)
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 4))..repeat();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
return Opacity(
opacity: _controller.value,
child: Container(
width: 300,
height: 300,
color: Colors.blue,
));
}));
}
}
上面的渐变动画不用经典的显示动画万能控件AnimatedBuilder的话,可以直接使用FadeTransition
FadeTransition(
opacity: _controller,
child:
Container(width: 200, height: 200, color: Colors.blue),
)),
可以无尽旋转的动画
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
bool _loading = false;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 1));
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: RotationTransition(
turns: _controller, child: const Icon(Icons.refresh, size: 100)),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (_loading) {
_controller.reset();
} else {
_controller.repeat();
}
_loading = !_loading;
},
));
}
}
FadeTransition、ScaleTransition、SlideTransition
控制器串联Tween补间和Curve曲线
ScaleTransition(
scale: _controller.drive(Tween(begin: 0.5, end:2.0)), child: const Icon(Icons.refresh, size: 100)),
),
SlideTransition(
position: _controller.drive(Tween(begin: Offset(0,0), end:Offset(1,0))),
// position:Tween(begin: const Offset(0, 0), end: const Offset(1, 0)).animate(_controller),
child: const Icon(Icons.refresh, size: 100)),
position:Tween(begin: const Offset(0, 0), end: const Offset(1, 0))
.chain(CurveTween(curve: Curves.elasticInOut))
.animate(_controller),
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
final bool _loading = false;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 4))..repeat(reverse:true);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(children: [
SlidingBox(
controller: _controller,
color: Colors.blue[100]!,
interval: const Interval(0.0, 0.2),
),
SlidingBox(
controller: _controller,
color: Colors.blue[300]!,
interval: const Interval(0.2, 0.4),
),
SlidingBox(
controller: _controller,
color: Colors.blue[500]!,
interval: const Interval(0.4, 0.6),
),
SlidingBox(
controller: _controller,
color: Colors.blue[700]!,
interval: const Interval(0.6, 0.8),
),
SlidingBox(
controller: _controller,
color: Colors.blue[900]!,
interval: const Interval(0.8, 1.0),
),
])),
floatingActionButton: FloatingActionButton(
onPressed: () {
_controller.forward();
},
),
);
}
}
class SlidingBox extends StatelessWidget {
final AnimationController controller;
final Color color;
final Interval interval;
const SlidingBox(
{Key? key,
required this.controller,
required this.color,
required this.interval})
: super(key: key);
@override
Widget build(BuildContext context) {
return SlideTransition(
position: Tween(begin: Offset.zero, end: const Offset(0.2, 0))
.chain(CurveTween(curve: Curves.bounceOut))
.chain(CurveTween(curve: interval))
.animate(controller),
//CurveTween(curve: const Interval(0.8, 1.0)) 控制动画从最后的1/5时间开始到结束
child: Container(width: 300, height: 100, color: color));
}
}
自定义呼吸灯动画
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
late AnimationController _expansionController;
late AnimationController _opacityController;
@override
void initState() {
_expansionController = AnimationController(vsync: this);
_opacityController = AnimationController(vsync: this);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: FadeTransition(
opacity: Tween(begin: 1.0, end: 0.5).animate(_opacityController),
child: AnimatedBuilder(
animation: _expansionController,
builder: (BuildContext context, Widget? child) {
return Container(
height: 200,
width: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
gradient: RadialGradient(colors: [
Colors.blue[600]!,
Colors.blue[100]!,
], stops: [
_expansionController.value,
_expansionController.value + 0.1
]),
),
);
}),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
_expansionController.duration = const Duration(seconds: 4);
_expansionController.forward();
await Future.delayed(const Duration(seconds: 4));
_opacityController.duration = const Duration(milliseconds: 1750);
_opacityController.repeat(reverse: true);
await Future.delayed(const Duration(seconds: 7));
_opacityController.reset();
_expansionController.duration = const Duration(seconds: 8);
_expansionController.reverse();
},
));
}
}
三、其他动画(CustomPainter)
直接操作底层的
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
final List<Snowflake> _snowflakes =
List.generate(100, (index) => Snowflake());
@override
void initState() {;
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 4))
..repeat();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.blue, Colors.lightBlue, Colors.white],
stops: [0.0, 0.75, 0.95]),
),
child: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
for (var snow in _snowflakes) {
snow.fall();
}
return CustomPaint(
painter: MyPainter(_snowflakes),
);
},
),
),
));
}
}
class MyPainter extends CustomPainter {
final List<Snowflake> _snowflakes;
MyPainter(this._snowflakes);
@override
void paint(Canvas canvas, Size size) {
final whitePaint = Paint()..color = Colors.white;
canvas.drawCircle(size.center(const Offset(0, 100)), 60.0, whitePaint);
canvas.drawOval(
Rect.fromCenter(
center: size.center(const Offset(0, 260)), width: 230, height: 250),
whitePaint);
_snowflakes.forEach((snowflake) {
canvas.drawCircle(
Offset(snowflake.x, snowflake.y), snowflake.radius, whitePaint);
});
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
class Snowflake {
double x = Random().nextDouble() * 400;
double y = Random().nextDouble() * 800;
double radius = Random().nextDouble() * 2 + 2;
double velocity = Random().nextDouble() * 4 + 2;
void fall() {
y += velocity;
if (y > 800) {
y = 0;
x = Random().nextDouble() * 400;
radius = Random().nextDouble() * 2 + 2;
velocity = Random().nextDouble() * 4 + 2;
}
}
}