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

