Flutter实现图标颜色切换时的平滑过渡效果
Flutter 图标颜色平滑过渡动画实现方案
问题描述
需要为Flutter中的图标实现颜色切换时的平滑过渡效果,时长125ms,当前图标代码如下:
Icon( widget.icon!, color: isFocused ? Colors.blue : Colors.grey, ),
解决方案
可以通过AnimationController配合ColorTween以及AnimatedBuilder实现颜色平滑过渡,还可扩展添加其他交互动画(比如尺寸弹跳)。以下是完整的可复用示例(以点赞按钮为例,包含颜色过渡+尺寸弹跳效果):
import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:linkify/utilities/post_service.dart'; class MyLikeButton extends StatefulWidget { String post; bool isLiked; int likeCount; MyLikeButton({ super.key, required this.post, required this.isLiked, required this.likeCount, }); @override State<MyLikeButton> createState() => _MyLikeButtonState(); } class _MyLikeButtonState extends State<MyLikeButton> with TickerProviderStateMixin { final PostService _postService = PostService(); late AnimationController sizeAnimationController; late Animation<double> sizeAnimation; late AnimationController colorAnimationController; late Animation<Color?> colorAnimation; @override void initState() { // 尺寸弹跳动画控制器 sizeAnimationController = AnimationController( duration: const Duration(milliseconds: 250), vsync: this, ); sizeAnimation = Tween(begin: 25.0, end: 35.0).animate(sizeAnimationController); // 颜色过渡动画控制器(设置为需求的125ms时长) colorAnimationController = AnimationController( duration: const Duration(milliseconds: 125), vsync: this, ); colorAnimation = ColorTween(begin: Colors.black, end: Colors.red) .animate(colorAnimationController); // 初始化时若已点赞,直接显示目标颜色 if (widget.isLiked) { colorAnimationController.forward(); } super.initState(); } @override void dispose() { // 销毁控制器避免内存泄漏 sizeAnimationController.dispose(); colorAnimationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return GestureDetector( onTap: () { _postService.likePost(context, widget.post); // 触发尺寸弹跳动画 sizeAnimationController.forward(); // 根据当前状态切换颜色动画方向 if (widget.isLiked) { colorAnimationController.reverse(); } else { colorAnimationController.forward(); } // 延迟恢复图标尺寸 Future.delayed( const Duration(milliseconds: 250), () => sizeAnimationController.reverse(), ); // 更新本地点赞状态(实际业务中建议根据接口返回结果更新) setState(() { widget.isLiked = !widget.isLiked; }); }, child: Container( width: 100, height: 35, decoration: BoxDecoration( color: Colors.red[100], borderRadius: BorderRadius.circular(18.0), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 合并动画监听,简化嵌套结构 AnimatedBuilder( animation: Listenable.merge([sizeAnimation, colorAnimation]), builder: (context, child) { return Icon( Icons.favorite, size: sizeAnimation.value, color: colorAnimation.value, ); }, ), // 显示点赞数 if (widget.likeCount != 0) Row( children: [ const Gap(5), Text(widget.likeCount.toString()), ], ), ], ), ), ); } }
补充说明
- 严格按照需求将颜色动画时长设置为125ms,确保过渡速度符合要求。
- 使用
Listenable.merge合并多个动画,减少AnimatedBuilder嵌套层级,代码更简洁。 - 添加了控制器销毁逻辑,避免内存泄漏问题。
- 若只需要单纯的颜色过渡效果,可直接移除尺寸相关的动画代码,仅保留颜色动画部分即可。
内容的提问来源于stack exchange,提问作者Furiy
相关产品推荐
相关产品推荐

