动画是提升用户体验、使应用程序更具吸引力的重要组成部分。Flutter 提供了强大而灵活的动画系统,允许开发者创建各种复杂的动画效果。Flutter 的动画系统基于 Animation、AnimationController、Tween 和 Curve 等核心概念。
1. 核心概念
Animation<T> (动画对象):一个抽象类,表示一个随时间变化的动画值。它知道动画的当前值和状态(例如,是否正在运行、是否已完成)。Animation<double> 是最常见的动画类型,其值通常在 0.0 到 1.0 之间。AnimationController (动画控制器):一个特殊的 Animation<double>,用于控制动画的生命周期,包括启动、停止、前进、反转、重复等。它需要一个 vsync 对象来同步动画与屏幕刷新。Tween<T> (补间动画):定义动画的起始值和结束值,以及如何在这两个值之间进行插值。例如,Tween<double>(begin: 0.0, end: 1.0) 会在 0.0 和 1.0 之间插值。Curve (曲线):定义动画的非线性运动。例如,Curves.easeOut 会使动画开始时快,结束时慢。AnimatedBuilder (动画构建器):一个 Widget,用于在动画值变化时重建其子 Widget。它优化了性能,因为它只重建动画相关的部分,而不是整个 Widget 树。AnimatedContainer、AnimatedOpacity、AnimatedAlign 等。2. 动画类型
AnimationController 的动画。适用于复杂的、自定义的动画。3. 显式动画的基本步骤
AnimationController:在 StatefulWidget 的 State 中创建,并提供 vsync(通常是 this)。Tween:定义动画的起始和结束值。Tween 和 AnimationController:使用 Tween.animate(controller) 创建一个 Animation 对象。addListener() 监听动画值的变化,并调用 setState() 来重建 UI。或者使用 AnimatedBuilder。controller.forward()、controller.reverse() 等方法来控制动画。dispose() 方法中释放 AnimationController。import 'package:flutter/material.dart';
class ExplicitAnimationExample extends StatefulWidget {
const ExplicitAnimationExample({super.key});
@override
State<ExplicitAnimationExample> createState() => _ExplicitAnimationExampleState();
}
class _ExplicitAnimationExampleState extends State<ExplicitAnimationExample> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2), // 动画持续时间
vsync: this, // 提供 vsync
);
_animation = Tween<double>(begin: 0.0, end: 200.0).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeOut, // 动画曲线
),
);
// 监听动画状态变化
_animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
_controller.reverse(); // 动画完成后反向播放
} else if (status == AnimationStatus.dismissed) {
_controller.forward(); // 动画回到起点后正向播放
}
});
_controller.forward(); // 启动动画
}
@override
void dispose() {
_controller.dispose(); // 释放控制器资源
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('显式动画示例'),
),
body: Center(
// 使用 AnimatedBuilder 优化性能,只重建动画相关的部分
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value, // 宽度随动画值变化
height: _animation.value, // 高度随动画值变化
color: Colors.blue,
child: Center(
child: Text(
_animation.value.toStringAsFixed(0),
style: const TextStyle(color: Colors.white, fontSize: 24),
),
),
);
},
),
),
);
}
}
void main() {
runApp(const MaterialApp(home: ExplicitAnimationExample()));
}
动画在 Flutter 应用中无处不在,从简单的按钮点击反馈到复杂的页面过渡和元素变形,动画极大地提升了用户体验。理解并熟练运用 Flutter 的动画系统是构建高质量应用的关键。
案例:一个可展开/收起的面板 (使用隐式动画 AnimatedContainer)
我们将创建一个简单的可展开/收起的面板,当用户点击按钮时,面板的高度会自动平滑过渡。这个案例将演示如何使用 AnimatedContainer 实现隐式动画。
import 'package:flutter/material.dart';
class AnimatedContainerExample extends StatefulWidget {
const AnimatedContainerExample({super.key});
@override
State<AnimatedContainerExample> createState() => _AnimatedContainerExampleState();
}
class _AnimatedContainerExampleState extends State<AnimatedContainerExample> {
bool _isExpanded = false;
double _height = 100.0; // 初始高度
Color _color = Colors.blue; // 初始颜色
void _toggleExpanded() {
setState(() {
_isExpanded = !_isExpanded;
_height = _isExpanded ? 250.0 : 100.0; // 根据状态改变高度
_color = _isExpanded ? Colors.red : Colors.blue; // 根据状态改变颜色
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AnimatedContainer 示例'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// AnimatedContainer 会自动在属性变化时播放动画
AnimatedContainer(
duration: const Duration(milliseconds: 500), // 动画持续时间
curve: Curves.easeInOut, // 动画曲线
width: 200.0,
height: _height, // 动画目标高度
color: _color, // 动画目标颜色
alignment: Alignment.center,
padding: const EdgeInsets.all(10.0),
child: Text(
_isExpanded ? '面板已展开' : '面板已收起',
style: const TextStyle(color: Colors.white, fontSize: 20),
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _toggleExpanded,
child: Text(_isExpanded ? '收起面板' : '展开面板'),
),
],
),
),
);
}
}
void main() {
runApp(const MaterialApp(home: AnimatedContainerExample()));
}
案例分析:
AnimatedContainer:这是一个隐式动画 Widget。当它的属性(如 height、color、alignment、padding 等)发生变化时,它会自动在当前值和新值之间进行平滑过渡,而无需手动管理 AnimationController。duration 和 curve:AnimatedContainer 提供了 duration(动画持续时间)和 curve(动画曲线)属性,用于控制动画的速度和节奏。_toggleExpanded() 方法:当按钮被点击时,_toggleExpanded 方法会改变 _isExpanded 状态,并根据新状态更新 _height 和 _color 的值。由于这些值是 AnimatedContainer 的属性,Flutter 会自动触发动画。这个案例清晰地展示了如何使用 Flutter 的隐式动画 Widget 来实现常见的动画效果。对于大多数简单的动画需求,隐式动画是更简洁、更高效的选择。对于更复杂的动画,例如自定义路径动画或多个动画同时进行的场景,则需要使用显式动画。