142 lines
4.3 KiB
Dart
142 lines
4.3 KiB
Dart
import 'dart:ui' as ui;
|
|
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
// from https://stackoverflow.com/a/71099304/786656
|
|
class DashedPathPainter extends CustomPainter {
|
|
final Path originalPath;
|
|
final Color pathColor;
|
|
final double strokeWidth;
|
|
final double dashGapLength;
|
|
final double dashLength;
|
|
late DashedPathProperties _dashedPathProperties;
|
|
|
|
DashedPathPainter({
|
|
required this.originalPath,
|
|
required this.pathColor,
|
|
this.strokeWidth = 3.0,
|
|
this.dashGapLength = 5.0,
|
|
this.dashLength = 10.0,
|
|
});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
_dashedPathProperties = DashedPathProperties(
|
|
path: Path(),
|
|
dashLength: dashLength,
|
|
dashGapLength: dashGapLength,
|
|
);
|
|
final dashedPath = _getDashedPath(originalPath, dashLength, dashGapLength);
|
|
canvas.drawPath(
|
|
dashedPath,
|
|
Paint()
|
|
..style = PaintingStyle.stroke
|
|
..color = pathColor
|
|
..strokeWidth = strokeWidth,
|
|
);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(DashedPathPainter oldDelegate) => oldDelegate.originalPath != originalPath || oldDelegate.pathColor != pathColor || oldDelegate.strokeWidth != strokeWidth || oldDelegate.dashGapLength != dashGapLength || oldDelegate.dashLength != dashLength;
|
|
|
|
Path _getDashedPath(
|
|
Path originalPath,
|
|
double dashLength,
|
|
double dashGapLength,
|
|
) {
|
|
final metricsIterator = originalPath.computeMetrics().iterator;
|
|
while (metricsIterator.moveNext()) {
|
|
final metric = metricsIterator.current;
|
|
_dashedPathProperties.extractedPathLength = 0.0;
|
|
while (_dashedPathProperties.extractedPathLength < metric.length) {
|
|
if (_dashedPathProperties.addDashNext) {
|
|
_dashedPathProperties.addDash(metric, dashLength);
|
|
} else {
|
|
_dashedPathProperties.addDashGap(metric, dashGapLength);
|
|
}
|
|
}
|
|
}
|
|
return _dashedPathProperties.path;
|
|
}
|
|
}
|
|
|
|
class DashedPathProperties {
|
|
double extractedPathLength;
|
|
Path path;
|
|
|
|
final double _dashLength;
|
|
double _remainingDashLength;
|
|
double _remainingDashGapLength;
|
|
bool _previousWasDash;
|
|
|
|
DashedPathProperties({
|
|
required this.path,
|
|
required double dashLength,
|
|
required double dashGapLength,
|
|
}) : assert(dashLength > 0.0, 'dashLength must be > 0.0'),
|
|
assert(dashGapLength > 0.0, 'dashGapLength must be > 0.0'),
|
|
_dashLength = dashLength,
|
|
_remainingDashLength = dashLength,
|
|
_remainingDashGapLength = dashGapLength,
|
|
_previousWasDash = false,
|
|
extractedPathLength = 0.0;
|
|
|
|
bool get addDashNext {
|
|
if (!_previousWasDash || _remainingDashLength != _dashLength) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void addDash(ui.PathMetric metric, double dashLength) {
|
|
// Calculate lengths (actual + available)
|
|
final end = _calculateLength(metric, _remainingDashLength);
|
|
final availableEnd = _calculateLength(metric, dashLength);
|
|
// Add path
|
|
final pathSegment = metric.extractPath(extractedPathLength, end);
|
|
path.addPath(pathSegment, Offset.zero);
|
|
// Update
|
|
final delta = _remainingDashLength - (end - extractedPathLength);
|
|
_remainingDashLength = _updateRemainingLength(
|
|
delta: delta,
|
|
end: end,
|
|
availableEnd: availableEnd,
|
|
initialLength: dashLength,
|
|
);
|
|
extractedPathLength = end;
|
|
_previousWasDash = true;
|
|
}
|
|
|
|
void addDashGap(ui.PathMetric metric, double dashGapLength) {
|
|
// Calculate lengths (actual + available)
|
|
final end = _calculateLength(metric, _remainingDashGapLength);
|
|
final availableEnd = _calculateLength(metric, dashGapLength);
|
|
// Move path's end point
|
|
ui.Tangent tangent = metric.getTangentForOffset(end)!;
|
|
path.moveTo(tangent.position.dx, tangent.position.dy);
|
|
// Update
|
|
final delta = end - extractedPathLength;
|
|
_remainingDashGapLength = _updateRemainingLength(
|
|
delta: delta,
|
|
end: end,
|
|
availableEnd: availableEnd,
|
|
initialLength: dashGapLength,
|
|
);
|
|
extractedPathLength = end;
|
|
_previousWasDash = false;
|
|
}
|
|
|
|
double _calculateLength(ui.PathMetric metric, double addedLength) {
|
|
return math.min(extractedPathLength + addedLength, metric.length);
|
|
}
|
|
|
|
double _updateRemainingLength({
|
|
required double delta,
|
|
required double end,
|
|
required double availableEnd,
|
|
required double initialLength,
|
|
}) {
|
|
return (delta > 0 && availableEnd == end) ? delta : initialLength;
|
|
}
|
|
}
|