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

在Flutter中实现带动态百分比填充的螺旋图形,求更优方案

Flutter 动态填充螺旋图形的优化实现方案

核心思路

不用拼接多个半圆,而是通过CustomPainter直接绘制连续的螺旋路径,再根据百分比动态计算填充的路径长度,实现橙色填充效果。这种方式代码更简洁,性能更优,也能更精准控制填充进度。

具体实现代码

import 'dart:math';
import 'package:flutter/material.dart';

class SpiralProgressPainter extends CustomPainter {
  final double progress; // 0-1之间的百分比
  final Color fillColor;
  final Color strokeColor;
  final double strokeWidth;

  SpiralProgressPainter({
    required this.progress,
    this.fillColor = Colors.orange,
    this.strokeColor = Colors.grey,
    this.strokeWidth = 4.0,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final maxRadius = min(size.width, size.height) / 2 - strokeWidth;
    
    // 创建螺旋路径
    Path spiralPath = Path();
    double angle = 0;
    double radiusStep = maxRadius / (2 * pi * 3); // 控制螺旋圈数,这里设为3圈
    double currentRadius = 0;

    // 生成螺旋路径点
    while (currentRadius < maxRadius) {
      final x = center.dx + currentRadius * cos(angle);
      final y = center.dy + currentRadius * sin(angle);
      if (currentRadius == 0) {
        spiralPath.moveTo(x, y);
      } else {
        spiralPath.lineTo(x, y);
      }
      angle += 0.1; // 步长越小,路径越平滑
      currentRadius += radiusStep * 0.1;
    }

    // 绘制底层灰色螺旋
    final strokePaint = Paint()
      ..color = strokeColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;
    canvas.drawPath(spiralPath, strokePaint);

    // 计算并绘制填充的橙色螺旋部分
    final fillPaint = Paint()
      ..color = fillColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;
    
    // 获取路径的度量,计算要绘制的长度
    final pathMetrics = spiralPath.computeMetrics();
    for (final metric in pathMetrics) {
      final length = metric.length * progress;
      final partialPath = metric.extractPath(0.0, length);
      canvas.drawPath(partialPath, fillPaint);
    }
  }

  @override
  bool shouldRepaint(covariant SpiralProgressPainter oldDelegate) {
    return oldDelegate.progress != progress ||
        oldDelegate.fillColor != fillColor ||
        oldDelegate.strokeColor != strokeColor;
  }
}

// 使用示例
class SpiralProgressWidget extends StatefulWidget {
  const SpiralProgressWidget({super.key});

  @override
  State<SpiralProgressWidget> createState() => _SpiralProgressWidgetState();
}

class _SpiralProgressWidgetState extends State<SpiralProgressWidget> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _progressAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    )..repeat(reverse: true);
    _progressAnimation = Tween<double>(begin: 0, end: 1).animate(_controller);
  }

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

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
        animation: _progressAnimation,
        builder: (context, child) {
          return SizedBox(
            width: 200,
            height: 200,
            child: CustomPaint(
              painter: SpiralProgressPainter(
                progress: _progressAnimation.value,
              ),
            ),
          );
        },
      ),
    );
  }
}

方案优势

  • 性能更优:单一路径绘制,避免多个半圆的拼接计算,减少绘制调用次数
  • 控制精准:通过路径度量(PathMetric)精确计算填充长度,百分比对应关系更准确
  • 灵活性强:可轻松调整螺旋圈数、粗细、颜色等参数,适配不同UI需求
  • 代码简洁:逻辑集中在CustomPainter中,维护成本低

补充说明

如果需要更复杂的螺旋样式(比如变宽螺旋),可以在生成路径时动态调整strokeWidth,或者使用Path的变换方法实现。另外,若要优化性能,可缓存生成的螺旋路径,避免每次paint时重新计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 17:23:20