flutter如何实现多属性动画
本文是Flutter动画系列的第四篇,建议读者阅读前面的教程,做到无缝衔接。
在第三篇教程里介绍了AnimatedWidget
,这里引出一个问题:如果 Widget 有多个属性需要变化,该如何实现。例如下图中,背景颜色和组件大小同时发生变化。
1. Tween evaluate
Tween
对象提供了evaluate
方法,它的入参是Animation
,返回当前对应的值。这样每次Animation
生成新值时,可以通过evaluate
方法计算Tween
对应的值。
evaluate(Animation<double> animation) → Color?
The current value of this object for the given Animation. [...]
对于多个属性,可以定义多个Tween
,属性的值通过evaluate
计算即可。
颜色和组件同时变化实现如下,这里定义了CurvedAnimation
来设置 forward 和 reverse 的 curve ,然后将CurvedAnimation
传入AnimatedWidget
,作为Tween
的计算入参,得到对应的属性值。
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> with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation<double> curved;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
curved = CurvedAnimation(parent: controller, curve: Curves.bounceOut);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@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: () {
if (controller.isDismissed) {
controller.forward();
} else if (controller.isCompleted) {
controller.reverse();
}
},
child: const Text('change', style: TextStyle(color: Colors.red)),
),
TextWidget(curved: curved)
]);
}
}
class TextWidget extends AnimatedWidget {
TextWidget({
Key? key,
required Animation<double> curved,
}) : super(key: key, listenable: curved);
final sizeTween = Tween<double>(begin: 0, end: 300);
final colorTween = ColorTween(begin: Colors.blue, end: Colors.green);
@override
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
return Container(
color: colorTween.evaluate(animation),
width: sizeTween.evaluate(animation),
height: sizeTween.evaluate(animation),
child: const Text("我在这"),
);
}
}