flutter动画:AnimatedOpacity
应用里 ui 不可避免的会发生变化,如果 ui 变化没有过渡,就会显得不自然。动画能帮助 ui 进行过渡,让应用显得自然又炫酷。
flutter 提供了一套完整的工具,来满足不同的动画需求。本文介绍最基础的,即组件属性变化的动画,例如透明度从1变成0,背景颜色从红色变成蓝色等。
1.透明度变化动画
flutter 提供了AnimatedOpacity
来处理透明度变化的动画,它属于 ImplicitlyAnimatedWidget。下面例子,点击按钮后会显示一行文字,再次点击按钮文字会消失。
动画可以理解为:在指定的时间(Duration),ui 从状态 A 过渡到状态 B(本文例子里,透明度从0变成1,或者从1变成0)。flutter 在处理动画时,每隔一个时间间隔,会重新渲染一次 ui,ui 状态是 A 到 B 的某个中间态(本文的例子里透明度0.3即为一个中间态)。
import 'package:flutter/material.dart';
void main() {
runApp(const Main());
}
class Main extends StatelessWidget {
const Main({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "app",
home: Scaffold(
appBar: AppBar(
title: const Text("app"),
),
body: const Center(child: Fade()),
),
);
}
}
class Fade extends StatefulWidget {
const Fade({Key? key}) : super(key: key);
@override
MainState createState() => MainState();
}
class MainState extends State<Fade> {
double opacity = 0;
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
TextButton(
style: ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: const BorderSide(color: Colors.red)))),
onPressed: () => setState(() {
opacity = 1 - opacity;
}),
child: const Text(
'Show Details',
style: TextStyle(color: Colors.red),
),
),
AnimatedOpacity(
duration: const Duration(seconds: 1),
opacity: opacity,
child: Column(
children: const [
Text('哈哈,我来了'),
],
),
)
]);
}
}
从代码可以看到,当按钮被点击时,AnimatedOpacity
的opacity
属性会从1变为0,或者从0变为1。当AnimatedOpacity
的opacity
属性变化时,动画会自动触发。
2. 设置动画的curve参数
动画在执行过程中,每隔固定的时间(例如一秒刷新60次)刷新一次 ui。每次刷新时,会计算一次透明度。透明度的值和时间满足某种函数关系,即:
AnimatedOpacity
的构造函数里使用curve
参数来设置 f(t),最常见的 curve 是 Curves.linear,当然也支持用户自定义。自定义 curve 函数将在后面文章里介绍。
3.动画结束回调
AnimatedOpacity
的构造函数中,onEnd
参数指定动画结束时的回调。上面例子里,当点击按钮时,动画开始执行,这时不希望再响应用户的点击事件。当动画完成时,再响应用户的点击事件。代码如下:
import 'package:flutter/material.dart';
void main() {
runApp(const Main());
}
class Main extends StatelessWidget {
const Main({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "app",
home: Scaffold(
appBar: AppBar(
title: const Text("app"),
),
body: const Center(child: Fade()),
),
);
}
}
class Fade extends StatefulWidget {
const Fade({Key? key}) : super(key: key);
@override
MainState createState() => MainState();
}
class MainState extends State<Fade> {
double opacity = 0;
bool running = false;
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
TextButton(
style: ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(
color: running ? Colors.grey : Colors.red)))),
onPressed: () {
if (!running) {
setState(() {
running = true;
opacity = 1 - opacity;
});
}
},
child: Text(
'Show Details',
style: TextStyle(color: running ? Colors.grey : Colors.red),
),
),
AnimatedOpacity(
duration: const Duration(seconds: 2),
opacity: opacity,
onEnd: () => setState(() {
running = false;
}),
child: Column(
children: const [
Text('哈哈,我来了'),
],
),
)
]);
}
}