Flutter 组件切换动画

使用的动画组件

组建切换时如果需要添加切换效果,可以使用 AnimatedSwitch 组件将需求增加切换动画的子组件包裹起来,默认当 AnimatedSwitch 的child属性改变时,自动添加切换动画。

判断子组件child是否改变的条件是:

child组件的类型改变

如果是两个不同类型的组件切换,也可以使用 AnimatedCrossFade 组件进行动画切换。

key 属性值改变

child组件的key属性改变, 可以使用 UniqueKey() 函数生成唯一的key

渐隐渐显

AnimatedSwitchchild 属性值变为 null 可以实现渐变消失效果

默认动画效果

AnimatedSwitch 组件属性 transitionBuilder 可以自定义动画效果,默认的是 FadeTransition 动画。

另外,动画效果是可以嵌套的,如:

1
2
3
4
5
6
7
8
9
transitionBuilder: (child, animation) {
return FadeTransition(
opacity: animation,
child: RotationTransition(
turns: animation,
child: child,
),
);
},

代码实现

main.dart
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

const primaryColor = Color(0xff0E185F);
const accentColor = Color(0xff2FA4FF);

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter组件切换动画',
home: HomePage(),
);
}
}

class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
double width = 200;
double height = 100;

bool switchByClassFlag = false;
bool switchByKeyFlag = false;
bool switchByNull = false;
bool switchTransitionFlag = false;

/// 根据子组件类型生成动画效果
Widget _switchByClass() {
if (switchByClassFlag) {
return Container(
child: const Text(
'根据类型切换组件',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 16,
),
),
);
} else {
return Image.network('https://picsum.photos/200');
}
}

/// 根据子组件 Key 生成动画效果
Widget _switchByKey() {
if (switchByKeyFlag) {
return Image.network(
'https://picsum.photos/id/1004/200/300',
key: const ValueKey(1004),
);
} else {
return Image.network(
'https://picsum.photos/id/1005/200/300',
key: const ValueKey(1005),
);
}
}

/// 渐隐渐显
Widget? _switchByNull() {
if (!switchByNull) {
return Image.network(
'https://picsum.photos/seed/picsum/200/300',
key: const ValueKey(1004),
);
} else {
return null;
}
}

/// 自定义动画效果
Widget _switchTransition() {
if (switchTransitionFlag) {
return Image.network(
'https://picsum.photos/id/1004/200/300',
key: UniqueKey(),
);
} else {
return Image.network(
'https://picsum.photos/id/1005/200/300',
key: UniqueKey(),
);
}
}

/// 动画容器
Widget _buildAnimatedSwitch({Widget? child, AnimatedSwitcherTransitionBuilder transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder}) {
return Container(
color: primaryColor,
height: 300,
child: Center(
child: AnimatedSwitcher(
transitionBuilder: transitionBuilder,
duration: const Duration(milliseconds: 500),
child: child,
),
),
);
}

// 按钮
Widget _buildButton({String title = '切换组件', required VoidCallback onPress}) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
primary: accentColor,
elevation: 0,
),
onPressed: onPress,
child: Text(title),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter组件切换动画'),
backgroundColor: primaryColor,
),
body: Container(
padding: const EdgeInsets.all(10),
child: ListView(
children: [
_buildAnimatedSwitch(child: _switchByClass()),
_buildButton(
title: '根据组件类型判断是否是不同的组件',
onPress: () {
setState(() {
switchByClassFlag = !switchByClassFlag;
});
},
),
const SizedBox(height: 10),
_buildAnimatedSwitch(child: _switchByKey()),
_buildButton(
title: '根据组件Key判断是否是不同的组件',
onPress: () {
setState(() {
switchByKeyFlag = !switchByKeyFlag;
});
},
),
const SizedBox(height: 10),
_buildAnimatedSwitch(child: _switchByNull()),
_buildButton(
title: '渐隐渐显',
onPress: () {
setState(() {
switchByNull = !switchByNull;
});
},
),
const SizedBox(height: 10),
_buildAnimatedSwitch(
child: _switchTransition(),
transitionBuilder: (child, animation) {
return FadeTransition(
opacity: animation,
child: RotationTransition(
turns: animation,
child: child,
),
);
}),
_buildButton(
title: '自定义动画效果',
onPress: () {
setState(() {
switchTransitionFlag = !switchTransitionFlag;
});
},
),
],
),
),
);
}
}

源码下载