drawSmoothLine function
void
drawSmoothLine(})
Draws a single smooth line series with an optional gradient area fill, a
tracing left-to-right reveal animation, and an optional highlighted end dot.
The Flutter equivalent of DrawScope.drawSmoothLine.
Implementation
void drawSmoothLine(
Canvas canvas, {
required List<Offset> points,
required Color color,
required double baseline,
required double progress,
double strokeWidth = 6,
bool fill = true,
bool endDot = true,
bool smooth = true,
}) {
if (points.length < 2) return;
final clamped = progress.clamp(0.0, 1.0);
final linePath = smooth ? smoothPath(points) : polylinePath(points);
final startX = points.first.dx;
final endX = points.last.dx;
final revealRight = startX + (endX - startX) * clamped;
// Soft gradient area fill, clipped to the reveal frontier.
if (fill) {
var topY = double.infinity;
for (final p in points) {
if (p.dy < topY) topY = p.dy;
}
final fillPath = Path.from(linePath)
..lineTo(endX, baseline)
..lineTo(startX, baseline)
..close();
canvas
..save()
..clipRect(
Rect.fromLTWH(startX, topY, revealRight - startX, baseline - topY),
)
..drawPath(
fillPath,
Paint()
..style = PaintingStyle.fill
..shader = ui.Gradient.linear(
Offset(0, topY),
Offset(0, baseline),
[
color.withValues(alpha: 0.32),
color.withValues(alpha: 0.144),
color.withValues(alpha: 0),
],
const [0.0, 0.5, 1.0],
),
)
..restore();
}
// Trace the stroke exactly up to the reveal frontier for a clean "drawing"
// feel. Compute the path metrics once and reuse them for both the trim and
// the end-dot tangent below (computeMetrics flattens the whole path, so doing
// it once instead of twice halves the per-frame cost on line charts).
final metrics = linePath.computeMetrics().toList();
final drawn = Path();
for (final metric in metrics) {
drawn.addPath(metric.extractPath(0, metric.length * clamped), Offset.zero);
}
canvas.drawPath(
drawn,
Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round,
);
// Glowing dot at the leading edge of the reveal.
if (endDot && clamped > 0.001) {
Offset? pos;
for (final metric in metrics) {
final t = metric.getTangentForOffset(metric.length * clamped);
if (t != null) pos = t.position;
}
if (pos != null) {
canvas
..drawCircle(
pos,
strokeWidth * 1.5,
Paint()..color = const Color(0xFFFFFFFF),
)
..drawCircle(pos, strokeWidth * 0.95, Paint()..color = color);
}
}
}