Flutter for OpenHarmony二手物品置换App实战 - 动画效果实现
本文介绍了Flutter中三种动画实现方式:1)隐式动画使用Animated系列组件自动处理过渡,如收藏按钮的变色和图标切换;2)通用点击缩放组件通过AnimatedScale实现按压反馈;3)显式动画使用AnimationController精细控制,如启动页的淡入缩放效果。隐式动画简单易用,显式动画灵活可控,开发者可根据场景需求选择合适方案,通过动画提升应用交互体验。

动画能让App更生动、交互更流畅。今天我们来讲解Flutter动画的实现方式,包括隐式动画、显式动画和页面转场动画。Flutter动画分为两类:隐式动画简单易用,显式动画更灵活可控。
隐式动画
隐式动画使用AnimatedXxx系列组件,只需要改变属性值,动画自动执行。下面是收藏按钮的动画效果:
class FavoriteButton extends StatefulWidget {
final bool isFavorite;
final VoidCallback onTap;
const FavoriteButton({
super.key,
required this.isFavorite,
required this.onTap,
});
State<FavoriteButton> createState() => _FavoriteButtonState();
}
class _FavoriteButtonState extends State<FavoriteButton> {
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: widget.isFavorite
? Colors.red.withOpacity(0.1)
: Colors.transparent,
shape: BoxShape.circle,
),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: Icon(
widget.isFavorite ? Icons.favorite : Icons.favorite_border,
key: ValueKey(widget.isFavorite),
color: widget.isFavorite ? Colors.red : Colors.grey,
),
),
),
);
}
}
这个收藏按钮组件展示了隐式动画的典型用法。AnimatedContainer会在属性变化时自动执行平滑过渡动画,比如背景色从透明变为淡红色。AnimatedSwitcher则负责在图标切换时添加过渡效果,通过ValueKey来标识不同状态。整个动画时长设为200毫秒,既不会太快让用户察觉不到,也不会太慢影响操作流畅度。这种方式代码简洁,非常适合简单的状态切换场景。
点击缩放效果
class ScaleOnTap extends StatefulWidget {
final Widget child;
final VoidCallback? onTap;
const ScaleOnTap({
super.key,
required this.child,
this.onTap,
});
State<ScaleOnTap> createState() => _ScaleOnTapState();
}
class _ScaleOnTapState extends State<ScaleOnTap> {
bool _isPressed = false;
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (_) => setState(() => _isPressed = true),
onTapUp: (_) => setState(() => _isPressed = false),
onTapCancel: () => setState(() => _isPressed = false),
onTap: widget.onTap,
child: AnimatedScale(
scale: _isPressed ? 0.95 : 1.0,
duration: const Duration(milliseconds: 100),
child: widget.child,
),
);
}
}
这是一个通用的点击缩放包装组件,可以给任何Widget添加按压反馈效果。通过监听onTapDown和onTapUp事件来控制缩放状态,按下时缩小到0.95倍,松开后恢复原大小。AnimatedScale会自动处理缩放过渡动画,100毫秒的时长让反馈既及时又自然。这种微交互虽然简单,但能显著提升用户的操作体验,让界面感觉更有"质感"。
显式动画
显式动画使用AnimationController手动控制,适合复杂的动画场景。下面是启动页的淡入动画:
class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _fadeAnimation;
late Animation<double> _scaleAnimation;
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
);
_scaleAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
);
_controller.forward();
Future.delayed(const Duration(seconds: 2), () {
Get.off(() => const MainPage());
});
}
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF07C160), Color(0xFF06AD56)],
),
),
child: Center(
child: FadeTransition(
opacity: _fadeAnimation,
child: ScaleTransition(
scale: _scaleAnimation,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: const Icon(Icons.swap_horiz, size: 60, color: Color(0xFF07C160)),
),
const SizedBox(height: 24),
const Text('闲置换', style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white)),
],
),
),
),
),
),
);
}
}
启动页动画展示了显式动画的完整流程。首先创建AnimationController作为动画的"指挥官",通过Tween定义动画的起止值。CurvedAnimation添加了easeOut缓动曲线,让动画开始快结束慢,更符合物理直觉。Logo和文字同时执行淡入和缩放两个动画,从0.8倍放大到1倍,透明度从0到1。记得在dispose中释放Controller,避免内存泄漏。
列表项动画
商品列表加载时的错落动画效果:
class AnimatedListItem extends StatefulWidget {
final int index;
final Widget child;
const AnimatedListItem({super.key, required this.index, required this.child});
State<AnimatedListItem> createState() => _AnimatedListItemState();
}
class _AnimatedListItemState extends State<AnimatedListItem>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _fadeAnimation;
late Animation<Offset> _slideAnimation;
void initState() {
super.initState();
_controller = AnimationController(duration: const Duration(milliseconds: 400), vsync: this);
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
);
_slideAnimation = Tween<Offset>(begin: const Offset(0, 0.2), end: Offset.zero)
.animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));
Future.delayed(Duration(milliseconds: widget.index * 50), () {
if (mounted) _controller.forward();
});
}
void dispose() { _controller.dispose(); super.dispose(); }
Widget build(BuildContext context) {
return FadeTransition(
opacity: _fadeAnimation,
child: SlideTransition(position: _slideAnimation, child: widget.child),
);
}
}
列表项动画的关键在于"错落感"。每个item根据自己的index延迟执行动画,第一个立即开始,第二个延迟50ms,第三个延迟100ms,以此类推。这样商品卡片会像瀑布一样依次出现,比同时出现更有层次感。SlideTransition让卡片从下方滑入,配合淡入效果,整体观感非常流畅。注意检查mounted状态,避免组件销毁后还执行动画导致报错。
页面转场与Hero动画
GetX支持多种页面转场效果:
Get.to(() => const ProductDetailPage(productId: 1), transition: Transition.rightToLeft);
Get.to(() => const PublishPage(), transition: Transition.downToUp);
Get.to(() => const SearchPage(), transition: Transition.fade);
Hero动画实现商品图片的"飞行"效果:
// 列表页
Hero(tag: 'product_image_${product['id']}', child: Image.network(product['image']))
// 详情页
Hero(tag: 'product_image_${widget.productId}', child: Image.network(product['image']))
页面转场动画能让导航更自然,rightToLeft适合常规页面跳转,downToUp适合弹出式页面如发布页,fade适合搜索等轻量级页面。Hero动画是Flutter的特色功能,只需在两个页面的Widget上设置相同的tag,系统会自动计算位置差异并执行平滑的飞行动画,非常适合商品图片从列表"飞"到详情页的场景。
小结
这篇讲解了Flutter动画的核心实现方式。隐式动画适合简单场景,显式动画适合复杂控制,Hero动画让页面切换更流畅。适当的动画能让App更生动,但也要注意不要过度使用,保持60fps的流畅体验才是关键。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)