【笔记】Flutter的动画

前言

Flutter的动画学习笔记

隐式动画

duration:定义动画持续时间
curve:定义动画类型

Curves.linear:缺省值,线性
Curves.linear:线性

AnimatedList动画列表

淡入淡出动画

animation:在新增操作时,animation的值是从0到1;在删除操作时,animation的值是从1到0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
// 定义全局键
final _globalKey = GlobalKey<AnimatedListState>();

// 定义数据
List<String> list = ["1", "2"];

// 定义锁,如果上锁就不能做删除操作
bool lock = false;

Widget _buildItem(index) {
return ListTile(
title: Text(list[index]),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
_deleteItem(index);
},
),
);
}

_deleteItem(index) {
if (lock) {
return;
}
// 上锁
lock = true;
_globalKey.currentState!.removeItem(index, (context, animation) {
var removeItem = _buildItem(index);
list.removeAt(index);
return FadeTransition(
opacity: animation,
child: removeItem, // 执行动画的元素
);
});
Timer.periodic(const Duration(milliseconds: 500), (timer) {
// 解锁
lock = false;
timer.cancel();
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
list.add("0");
_globalKey.currentState!.insertItem(list.length - 1);
},
child: const Icon(Icons.add),
),
body: AnimatedList(
key: _globalKey,
initialItemCount: list.length,
itemBuilder: ((context, index, animation) {
return FadeTransition(
opacity: animation,
child: _buildItem(index), // 执行动画的元素
);
}),
),
);
}
}

缩放动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
// 定义全局键
final _globalKey = GlobalKey<AnimatedListState>();

// 定义数据
List<String> list = ["1", "2"];

// 定义锁,如果上锁就不能做删除操作
bool lock = false;

Widget _buildItem(index) {
return ListTile(
title: Text(list[index]),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
_deleteItem(index);
},
),
);
}

_deleteItem(index) {
if (lock) {
return;
}
// 上锁
lock = true;
_globalKey.currentState!.removeItem(index, (context, animation) {
var removeItem = _buildItem(index);
list.removeAt(index);
return ScaleTransition(
scale: animation,
child: removeItem, // 执行动画的元素
);
});
Timer.periodic(const Duration(milliseconds: 500), (timer) {
// 解锁
lock = false;
timer.cancel();
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
list.add("0");
_globalKey.currentState!.insertItem(list.length - 1);
},
child: const Icon(Icons.add),
),
body: AnimatedList(
key: _globalKey,
initialItemCount: list.length,
itemBuilder: ((context, index, animation) {
return ScaleTransition(
scale: animation,
child: _buildItem(index), // 执行动画的元素
);
}),
),
);
}
}

AnimatedContainer动画容器

duration:定义动画持续时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
bool flag = true;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
flag = !flag;
});
},
child: const Icon(Icons.add),
),
body: AnimatedContainer(
duration: const Duration(
seconds: 1,
),
width: flag ? 200 : 400,
height: flag ? 200 : 400,
color: Colors.blue,
),
);
}
}

AnimatedPadding动画内边距

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
bool flag = true;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
flag = !flag;
});
},
child: const Icon(Icons.add),
),
body: AnimatedPadding(
padding: EdgeInsets.fromLTRB(0, flag ? 0 : 200, 0, 0),
curve: Curves.linear,
duration: const Duration(
seconds: 1,
),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

AnimatedOpacity动画透明度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
bool flag = true;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
flag = !flag;
});
},
child: const Icon(Icons.add),
),
body: AnimatedOpacity(
opacity: flag ? 1 : 0.5,
duration: const Duration(
seconds: 1,
),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

AnimatedPositioned动画定位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
bool flag = true;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
flag = !flag;
});
},
child: const Icon(Icons.add),
),
body: Stack(
children: [
AnimatedPositioned(
top: flag ? 0 : 200,
duration: const Duration(
seconds: 1,
),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
)
],
),
);
}
}

AnimatedDefaultTextStyle动画默认文字样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
bool flag = true;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
flag = !flag;
});
},
child: const Icon(Icons.add),
),
body: AnimatedDefaultTextStyle(
style: TextStyle(
color: Colors.blue,
fontSize: flag ? 20 : 40,
),
duration: const Duration(
seconds: 1,
),
child: const Text("文本内容"),
),
);
}
}

AnimatedSwitcher动画切换

  • 当子元素发生任意改变时触发动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
bool flag = true;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
flag = !flag;
});
},
child: const Icon(Icons.add),
),
body: AnimatedSwitcher(
duration: const Duration(
seconds: 1,
),
child: flag ? const Text("文本内容1") : const Text("文本内容2"),
),
);
}
}

显式动画

vsync:配置程序的刷新率与手机保持一致

_animationController.forward():正向运动一次
_animationController.reverse():逆向运动一次,需要在正向运动后再执行
_animationController.repeat():正向运动无限循环

_animationController.repeat(reverse: true):正向运动然后逆向运动无限循环

_animationController.stop():停止运动
_animationController.reset():重置

旋转动画

通过构造方法配置controller

lowerBound:动画初始值

0:缺省值

upperBound:动画终止值

1:缺省值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
lowerBound: 0,
upperBound: 1,
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: RotationTransition(
turns: _animationController,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

通过drive方法配置controller

begin:动画初始值

0:缺省值

end:动画终止值

1:缺省值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: RotationTransition(
turns: _animationController.drive(Tween(begin: 0, end: 1)),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

显示/隐藏动画

通过构造方法配置controller

lowerBound:动画初始值

0:缺省值

upperBound:动画终止值

1:缺省值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
lowerBound: 0,
upperBound: 1,
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: FadeTransition(
opacity: _animationController,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

通过drive方法配置controller

begin:动画初始值

0:缺省值

end:动画终止值

1:缺省值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: FadeTransition(
opacity: _animationController.drive(Tween(begin: 0, end: 1)),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

放大/缩小动画

通过构造方法配置controller

lowerBound:动画初始值

0:缺省值

upperBound:动画终止值

1:缺省值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
lowerBound: 0,
upperBound: 1,
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: ScaleTransition(
scale: _animationController,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

通过drive方法配置controller

begin:动画初始值

0:缺省值

end:动画终止值

1:缺省值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: ScaleTransition(
scale: _animationController.drive(Tween(begin: 0, end: 1)),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

位移动画

通过drive方法配置controller

begin:动画初始位置

1.0:表示1 * width
0.5:表示0.5 * height

end:动画终止位置

0.5:表示0.5 * width
1.0:表示1 * height

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: SlideTransition(
position: _animationController
.drive(Tween(begin: const Offset(1.0, 0.5), end: const Offset(0.5, 1.0))),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

通过Tween类配置controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: SlideTransition(
position: Tween(begin: const Offset(1, 0.5), end: const Offset(0.5, 1))
.animate(_animationController),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}
添加动画链
  • 通过chain方法追加其他方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import 'dart:async';

import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
home: App(),
));
}

class App extends StatefulWidget {
const App({super.key});

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool flag = true;

@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("文本内容"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
flag
? _animationController.forward()
: _animationController.reverse();
flag = !flag;
},
child: const Icon(Icons.add),
),
body: SlideTransition(
position: Tween(begin: const Offset(1, 0.5), end: const Offset(0.5, 1))
.chain(CurveTween(curve: Curves.bounceInOut))
.chain(CurveTween(curve: const Interval(0, 1)))
.animate(_animationController),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
);
}
}

完成

参考文献

哔哩哔哩——筱筱知晓