在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
相关产品推荐
相关产品推荐

