目录
2. 显式动画(使用AnimationController)
四、结合AnimatedContainer与AnimationController
Flutter提供了丰富的动画解决方案,从简单的隐式动画(如 AnimatedContainer
)到复杂的显式动画(使用 AnimationController
)。本文将全面介绍这两种动画方式,并展示如何将它们结合使用。
一、隐式动画 vs 显式动画
1. 隐式动画(如AnimatedContainer)
隐式动画是最简单的动画实现方式,开发者只需定义起始和结束状态,Flutter会自动处理中间的过渡过程。
特点:
使用简单,代码量少
适合简单的属性过渡动画
自动管理动画生命周期
性能开销相对较大(对于复杂动画)
2. 显式动画(使用AnimationController)
显式动画提供了更精细的控制,开发者需要手动管理动画控制器、动画值和状态。
特点:
控制精细,可实现复杂动画
需要更多代码
手动管理动画生命周期
性能更优(对于复杂动画)
二、AnimatedContainer深度解析
AnimatedContainer
是Flutter中最常用的隐式动画组件之一,它可以自动在不同属性值之间进行动画过渡。
1. 基本实现
import 'package:flutter/material.dart';
class ScAnimationPage extends StatefulWidget {
const ScAnimationPage({super.key});
@override
State<ScAnimationPage> createState() => _ScAnimationPageState();
}
class _ScAnimationPageState extends State<ScAnimationPage> {
double _width = 100;
double _height = 100;
Color _color = Colors.blue;
void _changeContainer() {
setState(() {
_width = _width == 100 ? 200 : 100;
_height = _height == 100 ? 200 : 100;
_color = _color == Colors.blue ? Colors.red : Colors.blue;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('AnimatedContainer 示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedContainer(
duration: Duration(seconds: 1),
curve: Curves.easeInOut,
width: _width,
height: _height,
color: _color,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _changeContainer,
child: Text('改变大小和颜色'),
),
],
),
),
);
}
}
2. 可动画属性
AnimatedContainer
几乎可以对其所有视觉属性进行动画处理:
大小:width, height
颜色:color
边框:border, borderRadius
阴影:boxShadow
边距:margin, padding
对齐:alignment
变换:transform
三、AnimationController与显式动画
1. AnimationController基础
AnimationController
是一个特殊的Animation
对象,它可以控制动画的播放:
启动(forward)
停止(stop)
反转(reverse)
重复(repeat)
基本使用步骤:
import 'package:flutter/material.dart';
class ScAnimationPage extends StatefulWidget {
const ScAnimationPage({super.key});
@override
State<ScAnimationPage> createState() => _ScAnimationPageState();
}
class _ScAnimationPageState extends State<ScAnimationPage>
with SingleTickerProviderStateMixin {
AnimationController? _controller;
Animation<double>? _scaleAnimation;
@override
void initState() {
super.initState();
// 确保在 initState 中初始化
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_scaleAnimation = CurvedAnimation(
parent: _controller!,
curve: Curves.easeInOut,
);
}
@override
void dispose() {
_controller?.dispose(); // 使用安全调用
super.dispose();
}
void _toggleAnimation() {
// 添加空安全检查
if (_controller == null) return;
// 更安全的切换逻辑
if (_controller!.status == AnimationStatus.completed) {
_controller!.reverse();
} else {
_controller!.forward();
}
}
@override
Widget build(BuildContext context) {
// 添加控制器空检查
if (_controller == null || _scaleAnimation == null) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
return Scaffold(
body: Center(
child: GestureDetector(
onTap: _toggleAnimation,
child: ScaleTransition(
scale: _scaleAnimation!,
child: const FlutterLogo(size: 200),
),
),
),
);
}
}
2. 动画曲线与值映射
Flutter提供了CurvedAnimation
和Tween
来增强动画控制:
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
final CurvedAnimation curvedAnimation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);
final Animation<double> animation = Tween<double>(
begin: 0,
end: 1,
).animate(curvedAnimation);
四、结合AnimatedContainer与AnimationController
虽然AnimatedContainer
是隐式动画,但我们可以结合AnimationController
来实现更复杂的效果。
同步控制的动画容器
import 'package:flutter/material.dart';
class ScAnimationPage extends StatefulWidget {
const ScAnimationPage({super.key});
@override
State<ScAnimationPage> createState() => _ScAnimationPageState();
}
class _ScAnimationPageState extends State<ScAnimationPage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _sizeAnimation;
late Animation<Color?> _colorAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_sizeAnimation = Tween<double>(
begin: 100,
end: 200,
).animate(_controller);
_colorAnimation = ColorTween(
begin: Colors.blue,
end: Colors.red,
).animate(_controller);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('组合动画示例')),
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return AnimatedContainer(
duration: Duration(milliseconds: 100), // 短时间保证同步
width: _sizeAnimation.value,
height: _sizeAnimation.value,
decoration: BoxDecoration(
color: _colorAnimation.value,
borderRadius: BorderRadius.circular(_controller.value * 30),
),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (_controller.isAnimating) {
_controller.stop();
} else {
_controller.repeat(reverse: true);
}
},
child: Icon(_controller.isAnimating ? Icons.stop : Icons.play_arrow),
),
);
}
}
五、性能优化与最佳实践
选择合适的动画方式:
简单属性变化:使用隐式动画(AnimatedContainer等)
复杂动画序列:使用显式动画(AnimationController)
重用动画控制器:
避免频繁创建和销毁AnimationController
在StatefulWidget的dispose方法中正确释放控制器
使用AnimatedBuilder优化:
只重建需要动画的部分widget树
合理设置vsync:
使用SingleTickerProviderStateMixin或TickerProviderStateMixin
避免不必要的屏幕外动画
考虑使用Transform代替布局属性动画:
变换动画(如缩放、旋转)通常性能更好
六、实际应用场景
1. 加载状态指示器
class LoadingIndicator extends StatefulWidget {
@override
_LoadingIndicatorState createState() => _LoadingIndicatorState();
}
class _LoadingIndicatorState extends State<LoadingIndicator>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return AnimatedContainer(
duration: Duration(milliseconds: 100),
width: 50,
height: 50,
transform: Matrix4.rotationZ(_controller.value * 2 * pi),
child: CircularProgressIndicator(),
);
},
),
);
}
}
2. 交互式卡片展开
class ExpandableCard extends StatefulWidget {
@override
_ExpandableCardState createState() => _ExpandableCardState();
}
class _ExpandableCardState extends State<ExpandableCard> {
bool _expanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_expanded = !_expanded;
});
},
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
width: double.infinity,
height: _expanded ? 300 : 100,
margin: EdgeInsets.all(16),
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
spreadRadius: 2,
),
],
),
child: Column(
children: [
Text(
'可展开卡片',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
if (_expanded) ...[
SizedBox(height: 20),
Text('这里是详细内容...'),
// 更多展开后的内容
],
Spacer(),
Icon(
_expanded ? Icons.expand_less : Icons.expand_more,
size: 30,
),
],
),
),
);
}
}
七、总结
Flutter的动画系统非常灵活,从简单的AnimatedContainer
到复杂的AnimationController
,可以满足各种动画需求:
简单动画:优先使用隐式动画组件(AnimatedContainer, AnimatedOpacity等)
中等复杂度动画:考虑组合多个隐式动画组件
复杂动画:使用AnimationController + Tween + AnimatedBuilder
性能关键动画:使用显式动画并优化重建范围
记住,良好的动画应该:
有明确的目的(不是为了动画而动画)
保持适度(不要过度使用)
考虑性能影响
提供流畅的用户体验
相关推荐
快速使用 Flutter 中的 SnackBar 和 Toast-CSDN博客文章浏览阅读586次,点赞16次,收藏9次。本篇文章将详细介绍 Snackbar 的基本用法,包括如何创建、定制样式、添加交互按钮,并探索不同的显示方式。此外,还将对 ScaffoldMessenger 进行讲解,帮助开发者更灵活地控制 Snackbar 的展示方式。同时,文章还将介绍 fluttertoast 插件的使用方法,为开发者提供更多消息提示的选择。通过示例代码,读者可以快速掌握 Snackbar 和 fluttertoast 的用法,提高应用的用户体验https://shuaici.blog.csdn.net/article/details/146083690Flutter setState() 状态管理详细使用指南-CSDN博客文章浏览阅读1.8k次,点赞55次,收藏51次。在 Flutter 开发中,setState() 是管理 Widget 状态变化最基础的方法。它用于更新 StatefulWidget 中的 UI,使 Flutter 重新构建该 Widget 及其子组件。本文将详细介绍 setState() 的基本原理、使用方法,并通过代码示例展示如何正确使用 setState() 进行状态更新。此外,我们还会探讨 setState() 的局限性,以及在复杂应用中可能需要的更高级状态管理方案。
https://shuaici.blog.csdn.net/article/details/146083853