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

Flutter CustomPainter状态栏动画如何控制圆点触发时机

问题根因

圆点突兀提前弹出的核心原因,是绘制已完成状态圆点的循环判断用了i < currentState逻辑:AnimationController在动画过程中输出的是连续变化的浮点值(比如从1到2的动画过程中会依次经过1.1、1.2…1.9这类中间值),只要浮点值超过整数阈值,循环就会直接把对应序号的圆点完整绘制出来,没有给圆点预留和进度线匹配的过渡区间。
举个实际运行的例子:当动画值刚到1.0001时,i=1就满足1 < 1.0001的判断,第二个圆点会直接被完整填充,这时候进度线才刚走到第一个圆点之后一点点,自然就会出现圆点提前弹出、没有过渡的效果。而进度线动画正常,是因为绘制线时直接用连续浮点值计算终点坐标,天然支持平滑过渡。

可行修复方案

核心思路是给每个圆点单独设置触发阈值,给圆点加上和进度线联动的补间动画,放弃用整数截断判断直接绘制完整圆点的逻辑。

第一步:修正动画控制器边界配置

把动画控制器的起始边界改成0,减少索引偏移计算的出错概率:

@override
void initState() {
  super.initState();
  _animationController = AnimationController(
      vsync: this,
      duration: Duration(seconds: 1),
      lowerBound: 0, // 0对应第一个状态位
      upperBound: (widget.states.length - 1).toDouble()); // 最大值对应最后一个状态位索引
}

@override
void didUpdateWidget(covariant StatusBar oldWidget) {
  super.didUpdateWidget(oldWidget);
  // 如果业务层currentState是从1开始计数,要减1转成0基索引
  _animationController.animateTo((widget.currentState - 1).toDouble());
}

第二步:重写CustomPainter的圆点绘制逻辑

不要直接通过整数循环判断画完整填充圆点,而是逐点计算当前动画进度下圆点的填充状态,同步修改进度线的终点计算逻辑适配0基索引:

@override
void paint(Canvas canvas, Size size) {
  // 省略原有各类Paint的定义,注意同步修改填充进度线的终点计算:
  // canvas.drawLine(
  //   Offset(0, size.height / 2),
  //   Offset(size.width * currentState / (numberOfStates - 1), size.height / 2),
  //   filledpaint,
  // );

  // 先绘制所有背景默认状态圆点
  for (int i = 0; i < numberOfStates; i++) {
    final double x = size.width * i / (numberOfStates - 1);
    canvas.drawCircle(Offset(x, size.height / 2), 10, dotspaint);
  }

  // 逐点计算填充进度
  for (int i = 0; i < numberOfStates; i++) {
    final double x = size.width * i / (numberOfStates - 1);
    // 控制圆点触发时机的阈值,可自行调整
    final triggerOffset = 0.3;
    double dotProgress = 0;

    if (currentState >= i) {
      dotProgress = 1; // 已经完全走过的圆点保持全填充状态
    } else if (currentState > i - triggerOffset) {
      // 进度进入触发区间后,计算0-1的平滑补间值
      dotProgress = (currentState - (i - triggerOffset)) / triggerOffset;
    }

    if (dotProgress <= 0) continue;

    // 配置随进度变化的透明度和半径,实现平滑出现效果
    final filledDotPaint = Paint()
      ..color = failed
          ? Theme.of(context).colorScheme.error.withOpacity(dotProgress)
          : Theme.of(context).colorScheme.secondary.withOpacity(dotProgress)
      ..style = PaintingStyle.fill;
    canvas.drawCircle(Offset(x, size.height / 2), 10 * dotProgress, filledDotPaint);
  }
}

自定义圆点触发时机的调整方法

你只需要修改代码里的triggerOffset参数,就能自由控制圆点的动画启动时机:

  • 把值改大(比如设为0.5):圆点会更早开始动画,进度线走到两个圆点中间位置时,下一个圆点就开始渐显
  • 把值改小(比如设为0.1):圆点会更晚开始动画,进度线几乎走到圆点位置时,圆点才开始渐显
  • 如果想要进度线完全抵达圆点位置再弹出,直接删除区间判断逻辑,仅保留currentState >= i时绘制完整圆点即可。
动画底层逻辑说明

Flutter的AnimationController在动画执行周期内,会按照设备刷新率持续输出连续的浮点插值,不存在整数跳变的节点。你之前的代码问题本质是把连续的动画值做了整数截断判断,只要值跨过整数边界就直接渲染最终状态,完全跳过了两个状态之间的过渡过程,才会导致圆点没有动画、直接弹出。只要把圆点的渲染参数(半径、透明度、颜色)和连续的动画值做绑定计算,就能实现和进度线完全同步的平滑动画效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 15:36:24