前言
Flutter动画其实很简单,之前一直感觉动画很难不敢尝试,这几天看了b站的视频感觉其实也不难,万事开头难,实现动画其实只需要几行代码,故在此做个笔记,顺便分享给大家。
实现简单的动画
首先写一个简单的动画组件,然后只需要修改一行代码,再hot reload一下就可以实现一个简单的动画效果。
代码如下:
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyHome(),
),
);
}
class MyHome extends StatefulWidget {
const MyHome({super.key});
@override
State<MyHome> createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedContainer( // 动画组件
duration: const Duration(seconds: 1), // 动画效果需要花费的时候
width: 300,
height: 300,
color: Colors.red,
),
),
);
}
}
修改宽高来实现动画
修改颜色来实现动画
实现基础组件切换的动画
我们不难发现在AnimatedContainer中进行代码的变更就可以实现动画,那我们继续拓展,如果对AnimatedContainer控件的child进行编辑能否实现动画呢
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyHome(),
),
);
}
class MyHome extends StatefulWidget {
const MyHome({super.key});
@override
State<MyHome> createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedContainer(
duration: const Duration(seconds: 1),
width: 300,
height: 300,
color: Colors.green,
child: Image.network("https://p9-passport.byteacctimg.com/img/user-avatar/80afee19cc3f022dd5c82e660dfff50c~90x90.awebp"),
// child: Center(child: CircularProgressIndicator())
),
),
);
}
}
我们发现在hot realod的之后图像立即切换了,这是因为没有包裹AnimatedSwitcher,包裹后我们再看看
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyHome(),
),
);
}
class MyHome extends StatefulWidget {
const MyHome({super.key});
@override
State<MyHome> createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedContainer(
duration: const Duration(seconds: 1),
width: 300,
height: 300,
color: Colors.green,
child: AnimatedSwitcher(
duration: const Duration(seconds: 1),
child: Image.network("https://p9-passport.byteacctimg.com/img/user-avatar/80afee19cc3f022dd5c82e660dfff50c~90x90.awebp")
// child: Center(child: CircularProgressIndicator())
),
),
),
);
}
}
现在可以发现有默认的淡入淡出的动画效果了
一些常见的特殊情况
如果是对Text组件进行切换会发生什么呢
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyHome(),
),
);
}
class MyHome extends StatefulWidget {
const MyHome({super.key});
@override
State<MyHome> createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedContainer(
duration: const Duration(seconds: 1),
width: 300,
height: 300,
color: Colors.green,
child: const AnimatedSwitcher(
duration: Duration(seconds: 1),
child: Text("ovo", style: TextStyle(fontSize: 50),)
),
),
),
);
}
}
奇怪的事情发生了,Text的切换并没有动画效果。其实是因为Text组件里面的文本虽然被修改了,但是被flutter判定为同一个组件了,这样子就不被视为切换组件,因此没有动画效果。所以这个时候key就派上用场了,可以参考我的这篇文章 一个例子直观的告诉你flutter中key的作用,这样不被视为一个组件就可以执行切换动画
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyHome(),
),
);
}
class MyHome extends StatefulWidget {
const MyHome({super.key});
@override
State<MyHome> createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedContainer(
duration: const Duration(seconds: 1),
width: 300,
height: 300,
color: Colors.green,
child: AnimatedSwitcher(
duration: const Duration(seconds: 1),
child: Text("ovo", style: const TextStyle(fontSize: 50), key: UniqueKey(),)
),
),
),
);
}
}
图片也是同理,感兴趣的读者可以自己增删key: UniqueKey()来试试效果
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyHome(),
),
);
}
class MyHome extends StatefulWidget {
const MyHome({super.key});
@override
State<MyHome> createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
int _count = 0;
String _url = "https://q9.itc.cn/q_70/images01/20250112/a68f493962224b89b779ec9c1d017340.png";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedContainer(
duration: const Duration(seconds: 1),
width: 300,
height: 300,
color: Colors.green,
child: AnimatedSwitcher(
transitionBuilder: (child, animation) {
// return FadeTransition(opacity: animation, child: child,); //淡入淡出动画
// return RotationTransition(turns: animation,child: child,); // 转圈动画
// return ScaleTransition(scale: animation, child: child,); // 大小动画
return FadeTransition(opacity: animation, child: ScaleTransition(scale: animation, child: child,),);
},
duration: const Duration(seconds: 1),
child:
// Image.network("https://q9.itc.cn/q_70/images01/20250112/a68f493962224b89b779ec9c1d017340.png", key: UniqueKey(),)
Image.network(_url,
key: UniqueKey() // 通过增删这里来测试动画效果
)
),
),
),
floatingActionButton: FloatingActionButton(onPressed: () {
print(_count);
setState(() {
if (_count == 0){
_count ++;
_url = "https://p1.itc.cn/q_70/images03/20231204/ce139ddd9b0e4f4397fcd86afdd4b1b9.png";
} else {
_count --;
_url = "https://q9.itc.cn/q_70/images01/20250112/a68f493962224b89b779ec9c1d017340.png";
}
});
}),
);
}
}
简单切换动画方式
AnimatedContainer中默认采用的动画效果是FadeTransition,我们可以把他换成RotationTransition或者ScaleTransition来实现其他的动画,感兴趣的读者可以自己试试,做gif很麻烦我就不展示啦。值得一提的是,这里如果用FadeTransition嵌套ScaleTransition,可以实现淡入淡出+大小切换的动画效果
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyHome(),
),
);
}
class MyHome extends StatefulWidget {
const MyHome({super.key});
@override
State<MyHome> createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedContainer(
duration: const Duration(seconds: 1),
width: 300,
height: 300,
color: Colors.green,
child: AnimatedSwitcher(
transitionBuilder: (child, animation) {
// return FadeTransition(opacity: animation, child: child,); //淡入淡出动画
// return RotationTransition(turns: animation,child: child,); // 转圈动画
// return ScaleTransition(scale: animation, child: child,); // 大小动画
return FadeTransition(opacity: animation, child: ScaleTransition(scale: animation, child: child,),);
},
duration: const Duration(seconds: 1),
child: Text("ovo", style: const TextStyle(fontSize: 50), key: UniqueKey(),)
),
),
),
);
}
}