You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:05:55