flutter-animations
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFlutter Animations
Flutter 动画
Overview
概述
Create smooth, performant animations in Flutter using the right approach for each use case. This skill covers complete animation workflow: from choosing between implicit/explicit approaches to implementing complex effects like hero transitions and staggered animations.
针对不同使用场景采用合适的方法,在Flutter中创建流畅、高性能的动画。本指南涵盖完整的动画工作流程:从选择隐式/显式实现方式,到实现Hero过渡、交错动画等复杂效果。
Animation Type Decision Tree
动画类型决策树
Choose the right animation type based on your requirements:
Implicit Animations - Use when:
- Animating a single property (color, size, position)
- Animation is triggered by state change
- No need for fine-grained control
Explicit Animations - Use when:
- Need full control over animation lifecycle
- Animating multiple properties simultaneously
- Need to react to animation state changes
- Creating custom animations or transitions
Hero Animations - Use when:
- Sharing an element between two screens
- Creating shared element transitions
- User expects element to "fly" between routes
Staggered Animations - Use when:
- Multiple animations should run sequentially or overlap
- Creating ripple effects or sequential reveals
- Animating list items in sequence
Physics-Based Animations - Use when:
- Animations should feel natural/physical
- Spring-like behavior, scrolling gestures
- Draggable interactions
根据需求选择合适的动画类型:
隐式动画 - 适用于以下场景:
- 为单个属性(颜色、尺寸、位置)添加动画
- 动画由状态变化触发
- 无需细粒度控制
显式动画 - 适用于以下场景:
- 需要完全控制动画生命周期
- 同时为多个属性添加动画
- 需要响应动画状态变化
- 创建自定义动画或过渡效果
Hero动画 - 适用于以下场景:
- 在两个页面之间共享某个元素
- 创建共享元素过渡效果
- 用户期望元素在页面间“飞动”
交错动画 - 适用于以下场景:
- 多个动画需要按顺序执行或重叠
- 创建涟漪效果或顺序展示动画
- 为列表项添加顺序动画
基于物理的动画 - 适用于以下场景:
- 动画需要呈现自然的物理效果
- 弹簧式行为、滚动手势
- 可拖拽交互
Implicit Animations
隐式动画
Implicit animations automatically handle the animation when properties change. No controller needed.
当属性发生变化时,隐式动画会自动处理动画过程,无需控制器。
Common Implicit Widgets
常用隐式组件
AnimatedContainer - Animates multiple properties (size, color, decoration, padding):
dart
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100,
height: _expanded ? 200 : 100,
color: _expanded ? Colors.blue : Colors.red,
child: const FlutterLogo(),
)AnimatedOpacity - Simple fade animation:
dart
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: const Text('Hello'),
)TweenAnimationBuilder - Custom tween animation without boilerplate:
dart
TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0, end: 1),
duration: const Duration(seconds: 1),
builder: (context, value, child) {
return Opacity(
opacity: value,
child: Transform.scale(
scale: value,
child: child,
),
);
},
child: const FlutterLogo(),
)Other implicit widgets:
- - Padding animation
AnimatedPadding - - Position animation (in Stack)
AnimatedPositioned - - Alignment animation
AnimatedAlign - - Multiple properties
AnimatedContainer - - Cross-fade between widgets
AnimatedSwitcher - - Text style animation
AnimatedDefaultTextStyle
AnimatedContainer - 为多个属性(尺寸、颜色、装饰、内边距)添加动画:
dart
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100,
height: _expanded ? 200 : 100,
color: _expanded ? Colors.blue : Colors.red,
child: const FlutterLogo(),
)AnimatedOpacity - 简单的淡入淡出动画:
dart
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: const Text('Hello'),
)TweenAnimationBuilder - 无需样板代码的自定义补间动画:
dart
TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0, end: 1),
duration: const Duration(seconds: 1),
builder: (context, value, child) {
return Opacity(
opacity: value,
child: Transform.scale(
scale: value,
child: child,
),
);
},
child: const FlutterLogo(),
)其他隐式组件:
- - 内边距动画
AnimatedPadding - - 位置动画(在Stack中使用)
AnimatedPositioned - - 对齐动画
AnimatedAlign - - 多属性动画
AnimatedContainer - - 组件间交叉淡入淡出
AnimatedSwitcher - - 文本样式动画
AnimatedDefaultTextStyle
Best Practices
最佳实践
- Prefer implicit animations for simple cases
- Use appropriate curves for natural motion (see class)
Curves - Set and
curvefor predictable behaviorduration - Use callback when needed
onEnd - Avoid nested implicit animations for performance
- 简单场景优先使用隐式动画
- 使用合适的曲线实现自然动效(参考类)
Curves - 设置和
curve以保证行为可预测duration - 必要时使用回调
onEnd - 避免嵌套隐式动画以保证性能
Explicit Animations
显式动画
Explicit animations provide full control with AnimationController.
显式动画通过AnimationController提供完全控制。
Core Components
核心组件
AnimationController - Drives the animation:
dart
late AnimationController _controller;
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
}
void dispose() {
_controller.dispose();
super.dispose();
}Tween - Interpolates between begin and end values:
dart
animation = Tween<double>(begin: 0, end: 300).animate(_controller);CurvedAnimation - Applies a curve to the animation:
dart
animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);AnimationController - 驱动动画:
dart
late AnimationController _controller;
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
}
void dispose() {
_controller.dispose();
super.dispose();
}Tween - 在起始值和结束值之间插值:
dart
animation = Tween<double>(begin: 0, end: 300).animate(_controller);CurvedAnimation - 为动画应用曲线:
dart
animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);AnimatedWidget Pattern
AnimatedWidget 模式
Best for reusable animated widgets:
dart
class AnimatedLogo extends AnimatedWidget {
const AnimatedLogo({super.key, required Animation<double> animation})
: super(listenable: animation);
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
return Center(
child: Container(
height: animation.value,
width: animation.value,
child: const FlutterLogo(),
),
);
}
}适用于可复用的动画组件:
dart
class AnimatedLogo extends AnimatedWidget {
const AnimatedLogo({super.key, required Animation<double> animation})
: super(listenable: animation);
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
return Center(
child: Container(
height: animation.value,
width: animation.value,
child: const FlutterLogo(),
),
);
}
}AnimatedBuilder Pattern
AnimatedBuilder 模式
Best for complex widgets with animations:
dart
class GrowTransition extends StatelessWidget {
const GrowTransition({
required this.child,
required this.animation,
super.key,
});
final Widget child;
final Animation<double> animation;
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: animation,
builder: (context, child) {
return SizedBox(
height: animation.value,
width: animation.value,
child: child,
);
},
child: child,
),
);
}
}适用于包含动画的复杂组件:
dart
class GrowTransition extends StatelessWidget {
const GrowTransition({
required this.child,
required this.animation,
super.key,
});
final Widget child;
final Animation<double> animation;
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: animation,
builder: (context, child) {
return SizedBox(
height: animation.value,
width: animation.value,
child: child,
);
},
child: child,
),
);
}
}Monitoring Animation State
监控动画状态
dart
animation.addStatusListener((status) {
switch (status) {
case AnimationStatus.completed:
_controller.reverse();
break;
case AnimationStatus.dismissed:
_controller.forward();
break;
default:
break;
}
});dart
animation.addStatusListener((status) {
switch (status) {
case AnimationStatus.completed:
_controller.reverse();
break;
case AnimationStatus.dismissed:
_controller.forward();
break;
default:
break;
}
});Multiple Simultaneous Animations
同时执行多个动画
dart
class AnimatedLogo extends AnimatedWidget {
const AnimatedLogo({super.key, required Animation<double> animation})
: super(listenable: animation);
static final _opacityTween = Tween<double>(begin: 0.1, end: 1);
static final _sizeTween = Tween<double>(begin: 0, end: 300);
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
return Center(
child: Opacity(
opacity: _opacityTween.evaluate(animation),
child: Container(
height: _sizeTween.evaluate(animation),
width: _sizeTween.evaluate(animation),
child: const FlutterLogo(),
),
),
);
}
}dart
class AnimatedLogo extends AnimatedWidget {
const AnimatedLogo({super.key, required Animation<double> animation})
: super(listenable: animation);
static final _opacityTween = Tween<double>(begin: 0.1, end: 1);
static final _sizeTween = Tween<double>(begin: 0, end: 300);
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
return Center(
child: Opacity(
opacity: _opacityTween.evaluate(animation),
child: Container(
height: _sizeTween.evaluate(animation),
width: _sizeTween.evaluate(animation),
child: const FlutterLogo(),
),
),
);
}
}Built-in Explicit Transitions
内置显式过渡组件
Flutter provides ready-to-use transitions:
- - Fade animation
FadeTransition - - Scale animation
ScaleTransition - - Slide animation
SlideTransition - - Size animation
SizeTransition - - Rotation animation
RotationTransition - - Position animation (in Stack)
PositionedTransition
Example:
dart
FadeTransition(
opacity: _animation,
child: const FlutterLogo(),
)Flutter提供了现成的过渡组件:
- - 淡入淡出动画
FadeTransition - - 缩放动画
ScaleTransition - - 平移动画
SlideTransition - - 尺寸动画
SizeTransition - - 旋转动画
RotationTransition - - 位置动画(在Stack中使用)
PositionedTransition
示例:
dart
FadeTransition(
opacity: _animation,
child: const FlutterLogo(),
)Performance Tips
性能优化技巧
- Dispose controllers when widget is removed
- Use for optimal rebuilds
AnimatedBuilder - Avoid in animation listeners (use
setState()/AnimatedWidget)AnimatedBuilder - Use to slow animations during debugging
timeDilation
- 组件移除时销毁控制器
- 使用实现最优重建
AnimatedBuilder - 避免在动画监听器中调用(使用
setState()/AnimatedWidget)AnimatedBuilder - 调试时使用减慢动画速度
timeDilation
Hero Animations
Hero动画
Hero animations create shared element transitions between screens.
Hero动画实现页面间的共享元素过渡效果。
Basic Hero Animation
基础Hero动画
Source screen:
dart
Hero(
tag: 'hero-image',
child: Image.asset('images/logo.png'),
)Destination screen:
dart
Hero(
tag: 'hero-image', // Same tag!
child: Image.asset('images/logo.png'),
)源页面:
dart
Hero(
tag: 'hero-image',
child: Image.asset('images/logo.png'),
)目标页面:
dart
Hero(
tag: 'hero-image', // 标签必须相同!
child: Image.asset('images/logo.png'),
)Complete Example
完整示例
dart
class PhotoHero extends StatelessWidget {
const PhotoHero({
super.key,
required this.photo,
this.onTap,
required this.width,
});
final String photo;
final VoidCallback? onTap;
final double width;
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: Hero(
tag: photo,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: Image.asset(photo, fit: BoxFit.contain),
),
),
),
);
}
}Navigating between screens:
dart
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) {
return Scaffold(
appBar: AppBar(title: const Text('Detail')),
body: Center(
child: PhotoHero(
photo: 'images/logo.png',
width: 300.0,
onTap: () => Navigator.of(context).pop(),
),
),
);
},
),
);dart
class PhotoHero extends StatelessWidget {
const PhotoHero({
super.key,
required this.photo,
this.onTap,
required this.width,
});
final String photo;
final VoidCallback? onTap;
final double width;
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: Hero(
tag: photo,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: Image.asset(photo, fit: BoxFit.contain),
),
),
),
);
}
}页面导航:
dart
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) {
return Scaffold(
appBar: AppBar(title: const Text('详情页')),
body: Center(
child: PhotoHero(
photo: 'images/logo.png',
width: 300.0,
onTap: () => Navigator.of(context).pop(),
),
),
);
},
),
);Radial Hero Animation
径向Hero动画
Transform from circle to rectangle during transition:
dart
class RadialExpansion extends StatelessWidget {
const RadialExpansion({
super.key,
required this.maxRadius,
this.child,
}) : clipRectSize = 2.0 * (maxRadius / math.sqrt2);
final double maxRadius;
final double clipRectSize;
final Widget? child;
Widget build(BuildContext context) {
return ClipOval(
child: Center(
child: SizedBox(
width: clipRectSize,
height: clipRectSize,
child: ClipRect(child: child),
),
),
);
}
}Use with for center-based interpolation:
MaterialRectCenterArcTweendart
static RectTween _createRectTween(Rect? begin, Rect? end) {
return MaterialRectCenterArcTween(begin: begin, end: end);
}过渡过程中从圆形变换为矩形:
dart
class RadialExpansion extends StatelessWidget {
const RadialExpansion({
super.key,
required this.maxRadius,
this.child,
}) : clipRectSize = 2.0 * (maxRadius / math.sqrt2);
final double maxRadius;
final double clipRectSize;
final Widget? child;
Widget build(BuildContext context) {
return ClipOval(
child: Center(
child: SizedBox(
width: clipRectSize,
height: clipRectSize,
child: ClipRect(child: child),
),
),
);
}
}结合实现基于中心的插值:
MaterialRectCenterArcTweendart
static RectTween _createRectTween(Rect? begin, Rect? end) {
return MaterialRectCenterArcTween(begin: begin, end: end);
}Hero Best Practices
Hero动画最佳实践
- Use unique, consistent tags (often the data object itself)
- Keep hero widget trees similar between routes
- Wrap images in with transparent color for "pop" effect
Material - Use to debug transitions
timeDilation - Consider to disable hero animations when needed
HeroMode
- 使用唯一且一致的标签(通常使用数据对象本身)
- 保持页面间Hero组件树结构相似
- 将图片包裹在透明颜色的组件中以实现返回效果
Material - 使用调试过渡效果
timeDilation - 必要时使用禁用Hero动画
HeroMode
Staggered Animations
交错动画
Run multiple animations with different timing.
按不同时间执行多个动画。
Basic Staggered Animation
基础交错动画
All animations share one controller:
dart
class StaggerAnimation extends StatelessWidget {
StaggerAnimation({super.key, required this.controller})
: opacity = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(0.0, 0.100, curve: Curves.ease),
),
),
width = Tween<double>(begin: 50.0, end: 150.0).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(0.125, 0.250, curve: Curves.ease),
),
);
final AnimationController controller;
final Animation<double> opacity;
final Animation<double> width;
Widget _buildAnimation(BuildContext context, Widget? child) {
return Container(
alignment: Alignment.bottomCenter,
child: Opacity(
opacity: opacity.value,
child: Container(
width: width.value,
height: 150,
color: Colors.blue,
),
),
);
}
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: _buildAnimation,
);
}
}所有动画共享同一个控制器:
dart
class StaggerAnimation extends StatelessWidget {
StaggerAnimation({super.key, required this.controller})
: opacity = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(0.0, 0.100, curve: Curves.ease),
),
),
width = Tween<double>(begin: 50.0, end: 150.0).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(0.125, 0.250, curve: Curves.ease),
),
);
final AnimationController controller;
final Animation<double> opacity;
final Animation<double> width;
Widget _buildAnimation(BuildContext context, Widget? child) {
return Container(
alignment: Alignment.bottomCenter,
child: Opacity(
opacity: opacity.value,
child: Container(
width: width.value,
height: 150,
color: Colors.blue,
),
),
);
}
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: _buildAnimation,
);
}
}Interval-Based Timing
基于时间间隔的时序控制
Each animation has an Interval between 0.0 and 1.0:
dart
animation = Tween<double>(begin: 0, end: 300).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.25, // Start at 25% of controller duration
0.50, // End at 50% of controller duration
curve: Curves.ease,
),
),
);每个动画在0.0到1.0之间有一个时间间隔:
dart
animation = Tween<double>(begin: 0, end: 300).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.25, // 在控制器时长的25%处开始
0.50, // 在控制器时长的50%处结束
curve: Curves.ease,
),
),
);Common Tweens
常用补间动画
dart
borderRadius = BorderRadiusTween(
begin: BorderRadius.circular(4),
end: BorderRadius.circular(75),
).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(0.375, 0.500, curve: Curves.ease),
),
);dart
borderRadius = BorderRadiusTween(
begin: BorderRadius.circular(4),
end: BorderRadius.circular(75),
).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(0.375, 0.500, curve: Curves.ease),
),
);Staggered Menu Animation
交错菜单动画
dart
class _MenuState extends State<Menu> with SingleTickerProviderStateMixin {
static const _initialDelayTime = Duration(milliseconds: 50);
static const _itemSlideTime = Duration(milliseconds: 250);
static const _staggerTime = Duration(milliseconds: 50);
static const _buttonDelayTime = Duration(milliseconds: 150);
static const _buttonTime = Duration(milliseconds: 500);
final _animationDuration =
_initialDelayTime +
(_staggerTime * _menuTitles.length) +
_buttonDelayTime +
_buttonTime;
late AnimationController _controller;
void initState() {
super.initState();
_controller = AnimationController(
duration: _animationDuration,
vsync: this,
);
_controller.forward();
}
void dispose() {
_controller.dispose();
super.dispose();
}
}dart
class _MenuState extends State<Menu> with SingleTickerProviderStateMixin {
static const _initialDelayTime = Duration(milliseconds: 50);
static const _itemSlideTime = Duration(milliseconds: 250);
static const _staggerTime = Duration(milliseconds: 50);
static const _buttonDelayTime = Duration(milliseconds: 150);
static const _buttonTime = Duration(milliseconds: 500);
final _animationDuration =
_initialDelayTime +
(_staggerTime * _menuTitles.length) +
_buttonDelayTime +
_buttonTime;
late AnimationController _controller;
void initState() {
super.initState();
_controller = AnimationController(
duration: _animationDuration,
vsync: this,
);
_controller.forward();
}
void dispose() {
_controller.dispose();
super.dispose();
}
}Stagger Best Practices
交错动画最佳实践
- Use to offset animations in time
Interval - Ensure controller duration covers all intervals
- Use curves for natural motion within intervals
- Consider to debug timing
timeDilation - Stagger menu items with increasing delay for ripple effect
- 使用来偏移动画的执行时间
Interval - 确保控制器时长覆盖所有时间间隔
- 在时间间隔内使用曲线实现自然动效
- 调试时序时使用
timeDilation - 为菜单项设置递增的延迟时间以实现涟漪效果
Physics-Based Animations
基于物理的动画
Create natural-feeling animations using physics simulations.
使用物理模拟创建自然的动画效果。
Fling Animation
抛掷动画
dart
_controller.fling(
velocity: 2.0, // Units per second
);dart
_controller.fling(
velocity: 2.0, // 单位:每秒
);Custom Physics Simulation
自定义物理模拟
dart
_controller.animateWith(
SpringSimulation(
spring: const SpringDescription(
mass: 1,
stiffness: 100,
damping: 10,
),
start: 0.0,
end: 1.0,
velocity: 0.0,
),
);dart
_controller.animateWith(
SpringSimulation(
spring: const SpringDescription(
mass: 1,
stiffness: 100,
damping: 10,
),
start: 0.0,
end: 1.0,
velocity: 0.0,
),
);Common Physics Simulations
常用物理模拟
- - Spring physics
SpringSimulation - - Scroll with bounce
BouncingScrollSimulation - - Scroll without bounce
ClampingScrollSimulation - - Gravity-based
GravitySimulation
- - 弹簧物理效果
SpringSimulation - - 带回弹的滚动效果
BouncingScrollSimulation - - 无回弹的滚动效果
ClampingScrollSimulation - - 基于重力的效果
GravitySimulation
Best Practices
最佳实践
DO
建议
- Dispose AnimationController in widget disposal
- Use /
AnimatedBuilderinstead ofAnimatedWidgetin listenerssetState() - Choose appropriate curves for natural motion
- Use for debugging animations
timeDilation - Consider performance (avoid heavy widgets in animation builds)
- Test animations on various devices
- Support reverse animations for intuitive feel
- 在组件销毁时释放AnimationController
- 使用/
AnimatedBuilder替代监听器中的AnimatedWidgetsetState() - 选择合适的曲线实现自然动效
- 使用调试动画
timeDilation - 考虑性能(避免在动画构建中使用重型组件)
- 在不同设备上测试动画
- 支持反向动画以提升直观性
DON'T
禁忌
- Forget to dispose AnimationController (memory leak)
- Use in animation listeners when
setState()sufficesAnimatedBuilder - Assume animation completes instantly (handle )
AnimationStatus - Over-animate (animations can distract users)
- Create animations that feel "jerky" (use smooth curves)
- Ignore accessibility (respect preference)
disableAnimations
- 忘记释放AnimationController(会导致内存泄漏)
- 当足够时,仍在动画监听器中使用
AnimatedBuildersetState() - 假设动画会立即完成(处理)
AnimationStatus - 过度使用动画(动画可能会分散用户注意力)
- 创建“卡顿”的动画(使用平滑曲线)
- 忽略可访问性(尊重偏好设置)
disableAnimations
Resources
资源
references/
references/
implicit.md - Complete reference for implicit animation widgets with examples and best practices.
explicit.md - Deep dive into explicit animations, AnimationController, and patterns.
hero.md - Hero animations guide with standard and radial transitions.
staggered.md - Staggered animation patterns and timing strategies.
physics.md - Physics-based animations and simulations.
curves.md - Reference for Curves class and choosing appropriate curves.
implicit.md - 隐式动画组件的完整参考,包含示例和最佳实践。
explicit.md - 深入讲解显式动画、AnimationController及相关模式。
hero.md - Hero动画指南,包含标准和径向过渡效果。
staggered.md - 交错动画模式和时序策略。
physics.md - 基于物理的动画和模拟。
curves.md - Curves类参考及曲线选择指南。
assets/templates/
assets/templates/
Template code for common animation patterns:
- - Implicit animation examples
implicit_animation.dart - - Explicit animation setup
explicit_animation.dart - - Hero animation boilerplate
hero_transition.dart - - Staggered animation template
staggered_animation.dart
常见动画模式的模板代码:
- - 隐式动画示例
implicit_animation.dart - - 显式动画设置模板
explicit_animation.dart - - Hero动画样板代码
hero_transition.dart - - 交错动画模板
staggered_animation.dart