flutter_animation_basics
Flutter - 动画基础
Section titled “Flutter - 动画基础”动画通过提供视觉反馈、引导焦点以及创建更动态和引人入胜的界面来增强用户体验。Flutter 提供了一个健壮且灵活的动画系统,既支持简单的预设动画,也支持复杂的自定义动画。
核心动画概念
Section titled “核心动画概念”Flutter 的动画系统围绕这些核心思想构建:
- Animation
: 表示类型为T的动画值的核心类(通常是double,但也可是Color、Size等)。它知道动画的当前值,但不知道 UI 的状态或正在动画什么。它的值会随时间变化。 - AnimationController: 管理动画播放。它是一个特殊的
Animation<double>,在指定的duration内生成从 0.0 到 1.0 的值。它允许你开始 (forward())、停止 (stop())、反向 (reverse()),并跟踪动画的状态 (AnimationStatus:dismissed, forward, reverse, completed)。需要一个TickerProvider(vsync)。 - TickerProvider (
vsync): 提供Ticker对象。Ticker 在需要渲染新帧时发出信号,将动画与屏幕刷新率同步。StatefulWidget的State对象可以实现TickerProviderStateMixin来充当vsync。 - Tween
: 定义AnimationController的 0.0-1.0 范围与 UI 属性所需实际值范围之间的映射(例如,大小从 50 到 150,颜色从红色到蓝色)。它不存储状态;它只定义插值begin和end值。 - Curve: 修改动画的变化速率,使其非线性(例如,缓入、弹性回弹)。使用
CurvedAnimation应用于AnimationController。
动画工作流程
Section titled “动画工作流程”创建显式动画(即你管理控制器)的一般过程包括:
- 创建
AnimationController: 通常在StatefulWidget的initState中。需要duration和vsync(由TickerProviderStateMixin提供)。 - 创建
Tween: 为你想要动画的属性定义开始和结束值。 - 组合 Controller 和 Tween: 使用
Tween.animate()方法,传入AnimationController(或包装控制器的CurvedAnimation),创建一个Animation<T>对象。此对象将生成插值值。 - 监听值变化: 使用
addListener()向Animation对象添加监听器。在监听器内部,调用setState({})以便在动画值变化时触发组件重建。 - 使用动画值: 在
build方法中,使用animation.value来设置你正在动画的组件属性(例如,width: sizeAnimation.value,color: colorAnimation.value)。 - 启动动画: 调用
controller.forward()、controller.repeat()等方法。 - 释放 Controller: 至关重要的是,在
State的dispose方法中释放AnimationController,以防止资源泄露。
使用 AnimatedWidget 和 AnimatedBuilder 简化
Section titled “使用 AnimatedWidget 和 AnimatedBuilder 简化”手动调用 addListener 和 setState 可能很繁琐。Flutter 提供了辅助组件:
AnimatedWidget: 基于给定Animation进行动画的组件基类。你可以继承AnimatedWidget,将Animation传递给其构造函数的super(listenable: animation),然后直接在build方法中使用animation.value,无需addListener或setState。对于创建可复用的动画组件很有用。AnimatedBuilder: 用于构建动画的更通用的组件。它接受一个animation和一个builder函数。builder函数接收BuildContext和一个可选的child组件。AnimatedBuilder监听动画并在每一帧(frame tick)调用builder函数。它很高效,因为它只重建builder返回的组件树部分,而不是包含 builder 的整个组件。对于在更大的 build 方法中实现的特定、不可复用的动画,它通常比AnimatedWidget更受欢迎。
使用 AnimatedBuilder 的示例(淡入淡出动画)
Section titled “使用 AnimatedBuilder 的示例(淡入淡出动画)”import 'package:flutter/material.dart';
class FadeLogo extends StatefulWidget { const FadeLogo({Key? key}) : super(key: key);
@override _FadeLogoState createState() => _FadeLogoState();}
// 需要 TickerProviderStateMixin 提供 vsyncclass _FadeLogoState extends State<FadeLogo> with TickerProviderStateMixin { late AnimationController _controller; late Animation<double> _opacityAnimation;
@override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 2), vsync: this, );
// 创建一个用于透明度(0.0 到 1.0)的 Tween final opacityTween = Tween<double>(begin: 0.0, end: 1.0);
// 选项 1:线性动画 // _opacityAnimation = opacityTween.animate(_controller);
// 选项 2:带曲线的动画 _opacityAnimation = opacityTween.animate( CurvedAnimation(parent: _controller, curve: Curves.easeIn) );
// 启动动画 _controller.forward();
// 可选:使其重复 // _controller.repeat(reverse: true); }
@override void dispose() { _controller.dispose(); // 重要:释放 controller super.dispose(); }
@override Widget build(BuildContext context) { return Center( child: AnimatedBuilder( animation: _opacityAnimation, // 监听此动画 builder: (context, child) { // 此 builder 函数在每个动画帧(tick)运行时都会执行 return Opacity( opacity: _opacityAnimation.value, // 使用当前的动画值 child: child, // 将 child 组件传递通过 ); }, child: const FlutterLogo(size: 150), // 正在进行动画的组件(传递给 builder) ), ); }}
// --- 使用示例 ---// void main() => runApp(MaterialApp(home: Scaffold(body: FadeLogo())));对于更简单的动画,如果你想在一定 duration 内动画一个属性的变化,Flutter 提供了隐式动画组件。这些组件会自动为其属性的变化添加动画效果。
例子包括:
AnimatedContainer: 对其width、height、color、padding、margin、decoration、transform等属性的变化进行动画。AnimatedOpacity: 对其opacity的变化进行动画。AnimatedPositioned: 对其在Stack中的位置变化进行动画。AnimatedPadding: 对其padding的变化进行动画。AnimatedTheme: 对主题变化进行动画。AnimatedDefaultTextStyle: 对默认文本样式变化进行动画。
你只需在 setState 调用中改变目标属性的值,组件内部就会自动处理动画控制器和补间动画(tweening)。
使用 AnimatedContainer 的示例
Section titled “使用 AnimatedContainer 的示例”import 'package:flutter/material.dart';import 'dart:math';
class AnimatedBox extends StatefulWidget { const AnimatedBox({Key? key}) : super(key: key);
@override _AnimatedBoxState createState() => _AnimatedBoxState();}
class _AnimatedBoxState extends State<AnimatedBox> { double _width = 100.0; double _height = 100.0; Color _color = Colors.blue; BorderRadiusGeometry _borderRadius = BorderRadius.circular(8.0);
void _animateBox() { setState(() { final random = Random(); _width = random.nextDouble() * 200 + 50; // Random width 50-250 _height = random.nextDouble() * 200 + 50; // Random height 50-250 _color = Color.fromRGBO( random.nextInt(256), random.nextInt(256), random.nextInt(256), 1, ); _borderRadius = BorderRadius.circular(random.nextDouble() * 50); }); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Implicit Animation')), body: Center( child: AnimatedContainer( // 提供需要进行动画的属性 width: _width, height: _height, decoration: BoxDecoration( color: _color, borderRadius: _borderRadius, ), // 指定动画的 duration 和 curve duration: const Duration(seconds: 1), curve: Curves.fastOutSlowIn, // 示例 curve child: Center(child: Text('Animate Me!', style: TextStyle(color: Colors.white))), ), ), floatingActionButton: FloatingActionButton( onPressed: _animateBox, child: const Icon(Icons.play_arrow), ), ); }}
// --- 使用示例 ---// void main() => runApp(MaterialApp(home: AnimatedBox()));隐式动画非常适合简单的、由状态驱动的 UI 过渡,无需编写显式动画控制器的样板代码。
选择正确的动画类型
Section titled “选择正确的动画类型”- 对于由状态更新触发的简单属性变化,使用隐式动画。
- 对于更复杂的序列、可重复的动画、对播放的精细控制,或者当需要动画的属性不在隐式组件涵盖范围内时,使用显式动画(配合
AnimatedBuilder或AnimatedWidget)。
Flutter 的动画系统功能强大。请查阅 flutter.dev 上丰富的文档和示例,了解基于物理的动画、交错动画、英雄动画(页面过渡)等更多内容。