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

Flutter动态颜色填充动画实现方案咨询

在Flutter中实现动态颜色填充效果

你要实现的这种从底部向上渐变填充的效果,Flutter里没有现成的Widget,但可以通过两种方式快速实现:

方法一:Stack + AnimatedContainer(简单场景首选)

这种方式代码简洁,适合矩形填充的基础场景,核心思路是用Stack将填充层放在底层,通过动画控制填充高度:

class FillAnimationWidget extends StatefulWidget {
  const FillAnimationWidget({super.key});

  @override
  State<FillAnimationWidget> createState() => _FillAnimationWidgetState();
}

class _FillAnimationWidgetState extends State<FillAnimationWidget> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _fillAnimation;

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器,时长2秒,循环往复
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    )..repeat(reverse: true);

    // 定义从0到1的填充进度动画
    _fillAnimation = Tween<double>(begin: 0, end: 1).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 120,
      height: 120,
      // 外层边框模拟演示效果
      decoration: BoxDecoration(
        border: Border.all(color: Colors.black, width: 2),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Stack(
        fit: StackFit.expand,
        children: [
          // 动态填充层
          AnimatedBuilder(
            animation: _fillAnimation,
            builder: (context, child) {
              return Align(
                alignment: Alignment.bottomCenter,
                child: Container(
                  // 根据动画进度计算填充高度
                  height: _fillAnimation.value * 120,
                  width: double.infinity,
                  color: Colors.blue,
                ),
              );
            },
          ),
          // 可选:上层叠加内容(比如文字)
          const Center(child: Text("填充动画")),
        ],
      ),
    );
  }
}

方法二:自定义CustomPainter(灵活适配复杂形状)

如果需要圆形、不规则图形的填充,或者更精细的动画控制,用CustomPainter自定义绘制是更好的选择:

// 自定义绘制填充逻辑
class FillPainter extends CustomPainter {
  final double fillProgress;
  final Color fillColor;
  final Color borderColor;

  FillPainter({
    required this.fillProgress,
    required this.fillColor,
    required this.borderColor,
  });

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制边框
    final borderPaint = Paint()
      ..color = borderColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;
    final rect = RRect.fromRectAndRadius(
      Rect.fromLTWH(0, 0, size.width, size.height),
      const Radius.circular(8),
    );
    canvas.drawRRect(rect, borderPaint);

    // 绘制填充区域
    final fillPaint = Paint()..color = fillColor;
    final fillRect = Rect.fromLTWH(
      0,
      size.height * (1 - fillProgress),
      size.width,
      size.height * fillProgress,
    );
    final fillRRect = RRect.fromRectAndRadius(fillRect, const Radius.circular(8));
    canvas.drawRRect(fillRRect, fillPaint);
  }

  @override
  bool shouldRepaint(covariant FillPainter oldDelegate) {
    // 只有填充进度变化时才重绘
    return oldDelegate.fillProgress != fillProgress;
  }
}

// 使用自定义Painter的动画Widget
class CustomFillAnimation extends StatefulWidget {
  const CustomFillAnimation({super.key});

  @override
  State<CustomFillAnimation> createState() => _CustomFillAnimationState();
}

class _CustomFillAnimationState extends State<CustomFillAnimation> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _fillAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    )..repeat(reverse: true);

    // 加入缓动曲线让动画更自然
    _fillAnimation = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
      parent: _controller,
      curve: Curves.easeInOut,
    ));
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 120,
      height: 120,
      child: AnimatedBuilder(
        animation: _fillAnimation,
        builder: (context, child) {
          return CustomPaint(
            painter: FillPainter(
              fillProgress: _fillAnimation.value,
              fillColor: Colors.blue,
              borderColor: Colors.black,
            ),
            child: const Center(child: Text("自定义填充")),
          );
        },
      ),
    );
  }
}

额外说明

  • 如果不需要循环动画,只需修改_controller的触发逻辑,比如在点击事件中调用_controller.forward()启动填充,_controller.reset()重置状态。
  • 可以通过修改Tween的begin和end值,调整填充的起始和结束位置(比如从左到右填充)。

内容的提问来源于stack exchange,提问作者Sal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 12:55:17