|
...
|
...
|
@@ -256,7 +256,7 @@ class _ActivityBurnRingPainter extends CustomPainter { |
|
|
|
if (progress <= 0) return;
|
|
|
|
|
|
|
|
final rect = Rect.fromCircle(center: center, radius: radius);
|
|
|
|
final safeProgress = math.max(0, progress);
|
|
|
|
final safeProgress = math.max(0.0, progress);
|
|
|
|
final hasCompletedLap = safeProgress >= 1;
|
|
|
|
final remainder = safeProgress % 1;
|
|
|
|
final visibleProgress = hasCompletedLap ? remainder : safeProgress;
|
|
...
|
...
|
@@ -266,7 +266,7 @@ class _ActivityBurnRingPainter extends CustomPainter { |
|
|
|
..color = progressColor
|
|
|
|
..style = PaintingStyle.stroke
|
|
|
|
..strokeWidth = width
|
|
|
|
..strokeCap = StrokeCap.round;
|
|
|
|
..strokeCap = StrokeCap.butt;
|
|
|
|
|
|
|
|
final fullLapPaint = Paint()
|
|
|
|
..color = progressColor
|
|
...
|
...
|
@@ -286,7 +286,14 @@ class _ActivityBurnRingPainter extends CustomPainter { |
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
final endAngle = startAngle + math.pi * 2 * visibleProgress;
|
|
|
|
final endAngle = isExactLap
|
|
|
|
? startAngle
|
|
|
|
: _endCapAngle(
|
|
|
|
startAngle: startAngle,
|
|
|
|
radius: radius,
|
|
|
|
width: width,
|
|
|
|
progress: visibleProgress,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Head layer: its shadow is painted first, then the colored current lap.
|
|
|
|
// For an exact lap there is no partial arc, so paint a colored head at the
|
|
...
|
...
|
@@ -309,16 +316,71 @@ class _ActivityBurnRingPainter extends CustomPainter { |
|
|
|
color: progressColor,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
canvas.drawArc(
|
|
|
|
rect,
|
|
|
|
startAngle,
|
|
|
|
math.pi * 2 * visibleProgress,
|
|
|
|
false,
|
|
|
|
progressPaint,
|
|
|
|
_drawPartialRingWithRoundedCaps(
|
|
|
|
canvas,
|
|
|
|
rect: rect,
|
|
|
|
startAngle: startAngle,
|
|
|
|
radius: radius,
|
|
|
|
progress: visibleProgress,
|
|
|
|
paint: progressPaint,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Matches the home-page ring: shrink the butt-capped arc, then add two
|
|
|
|
/// circular caps so that rounded ends never consume the remaining gap.
|
|
|
|
void _drawPartialRingWithRoundedCaps(
|
|
|
|
Canvas canvas, {
|
|
|
|
required Rect rect,
|
|
|
|
required double startAngle,
|
|
|
|
required double radius,
|
|
|
|
required double progress,
|
|
|
|
required Paint paint,
|
|
|
|
}) {
|
|
|
|
final sweep = (math.pi * 2 * progress.clamp(0, 1)).toDouble();
|
|
|
|
if (sweep <= 0) return;
|
|
|
|
|
|
|
|
final capAngle = math.asin((paint.strokeWidth / 2) / radius);
|
|
|
|
final actualCapAngle = math.min(capAngle, sweep / 2).toDouble();
|
|
|
|
final arcStart = startAngle + actualCapAngle;
|
|
|
|
final arcSweep = sweep - actualCapAngle * 2;
|
|
|
|
|
|
|
|
if (arcSweep > 0) {
|
|
|
|
canvas.drawArc(rect, arcStart, arcSweep, false, paint);
|
|
|
|
}
|
|
|
|
|
|
|
|
final capPaint = Paint()
|
|
|
|
..color = paint.color
|
|
|
|
..style = PaintingStyle.fill;
|
|
|
|
final center = rect.center;
|
|
|
|
canvas.drawCircle(
|
|
|
|
_pointOnRing(center, radius, arcStart),
|
|
|
|
paint.strokeWidth / 2,
|
|
|
|
capPaint,
|
|
|
|
);
|
|
|
|
canvas.drawCircle(
|
|
|
|
_pointOnRing(center, radius, arcStart + arcSweep),
|
|
|
|
paint.strokeWidth / 2,
|
|
|
|
capPaint,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
double _endCapAngle({
|
|
|
|
required double startAngle,
|
|
|
|
required double radius,
|
|
|
|
required double width,
|
|
|
|
required double progress,
|
|
|
|
}) {
|
|
|
|
final sweep = (math.pi * 2 * progress.clamp(0, 1)).toDouble();
|
|
|
|
final capAngle = math.asin((width / 2) / radius);
|
|
|
|
return startAngle + sweep - math.min(capAngle, sweep / 2).toDouble();
|
|
|
|
}
|
|
|
|
|
|
|
|
Offset _pointOnRing(Offset center, double radius, double angle) => Offset(
|
|
|
|
center.dx + math.cos(angle) * radius,
|
|
|
|
center.dy + math.sin(angle) * radius,
|
|
|
|
);
|
|
|
|
|
|
|
|
void _drawRingEndCap(
|
|
|
|
Canvas canvas, {
|
|
|
|
required Offset center,
|
...
|
...
|
|