跳到主要内容

flutter动画:AnimatedOpacity

 应用里 ui 不可避免的会发生变化,如果 ui 变化没有过渡,就会显得不自然。动画能帮助 ui 进行过渡,让应用显得自然又炫酷。

flutter 提供了一套完整的工具,来满足不同的动画需求。本文介绍最基础的,即组件属性变化的动画,例如透明度从1变成0,背景颜色从红色变成蓝色等。

1.透明度变化动画

flutter 提供了AnimatedOpacity来处理透明度变化的动画,它属于 ImplicitlyAnimatedWidget。下面例子,点击按钮后会显示一行文字,再次点击按钮文字会消失。

透明度变化

 动画可以理解为:在指定的时间(Duration),ui 从状态 A 过渡到状态 B(本文例子里,透明度从0变成1,或者从1变成0)。flutter 在处理动画时,每隔一个时间间隔,会重新渲染一次 uiui 状态是 AB 的某个中间态(本文的例子里透明度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('哈哈,我来了'),
],
),
)
]);
}
}

 从代码可以看到,当按钮被点击时,AnimatedOpacityopacity属性会从1变为0,或者从0变为1。当AnimatedOpacityopacity属性变化时,动画会自动触发。

2. 设置动画的curve参数

 动画在执行过程中,每隔固定的时间(例如一秒刷新60次)刷新一次 ui。每次刷新时,会计算一次透明度。透明度的值和时间满足某种函数关系,即:

opacity=f(t)opacity = f(t)

AnimatedOpacity 的构造函数里使用curve参数来设置 f(t),最常见的 curveCurves.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('哈哈,我来了'),
],
),
)
]);
}
}

署名-非商业性使用-禁止演绎 4.0 国际