Skip to content

Instantly share code, notes, and snippets.

@EsinShadrach
Created April 3, 2025 22:01
Show Gist options
  • Save EsinShadrach/917f39f8dd2c4d90e5ea9e478a1acb58 to your computer and use it in GitHub Desktop.
Save EsinShadrach/917f39f8dd2c4d90e5ea9e478a1acb58 to your computer and use it in GitHub Desktop.
class DashedBorder extends BoxBorder {
const DashedBorder({
this.top = BorderSide.none,
this.right = BorderSide.none,
this.bottom = BorderSide.none,
this.left = BorderSide.none,
this.dashLength = 5.0,
this.dashGap = 3.0,
});
factory DashedBorder.fromBorderSide({
required BorderSide borderSide,
double dashLength = 5.0,
double dashGap = 3.0,
}) {
return DashedBorder(
top: borderSide,
right: borderSide,
bottom: borderSide,
left: borderSide,
dashLength: dashLength,
dashGap: dashGap,
);
}
factory DashedBorder.all({
Color color = const Color(0xFF000000),
double width = 1.0,
BorderStyle style = BorderStyle.solid,
double dashLength = 5.0,
double dashGap = 3.0,
}) {
final BorderSide side = BorderSide(
color: color,
width: width,
style: style,
);
return DashedBorder.fromBorderSide(
borderSide: side,
dashLength: dashLength,
dashGap: dashGap,
);
}
final double dashLength;
final double dashGap;
@override
final BorderSide top;
final BorderSide right;
@override
final BorderSide bottom;
final BorderSide left;
@override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.fromLTRB(
left.width,
top.width,
right.width,
bottom.width,
);
}
@override
bool get isUniform => top == right && right == bottom && bottom == left;
@override
void paint(
Canvas canvas,
Rect rect, {
TextDirection? textDirection,
BoxShape shape = BoxShape.rectangle,
BorderRadius? borderRadius,
}) {
if (shape != BoxShape.rectangle && shape != BoxShape.circle) {
throw ArgumentError('Unsupported shape: $shape');
}
if (shape == BoxShape.circle) {
_paintCircle(canvas, rect);
return;
}
if (borderRadius != null) {
_paintRoundedRectangle(canvas, rect, borderRadius);
return;
}
_paintRectangle(canvas, rect);
}
void _paintCircle(Canvas canvas, Rect rect) {
final Paint paint = Paint()
..color = top.color
..strokeWidth = top.width
..style = PaintingStyle.stroke;
final double radius = math.min(rect.width, rect.height) / 2;
final Offset center = rect.center;
final double circumference = 2 * math.pi * radius;
final int dashCount = (circumference / (dashLength + dashGap)).floor();
final double dashAngle = 2 * math.pi / dashCount;
for (int i = 0; i < dashCount; i++) {
final double startAngle = i * dashAngle;
final double endAngle =
startAngle + (dashLength / circumference) * 2 * math.pi;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
startAngle,
endAngle - startAngle,
false,
paint,
);
}
}
void _paintRoundedRectangle(
Canvas canvas,
Rect rect,
BorderRadius borderRadius,
) {
final RRect rrect = RRect.fromRectAndCorners(
rect,
topLeft: borderRadius.topLeft,
topRight: borderRadius.topRight,
bottomLeft: borderRadius.bottomLeft,
bottomRight: borderRadius.bottomRight,
);
final Paint paint = Paint()
..color = top.color
..strokeWidth = top.width
..style = PaintingStyle.stroke;
canvas.drawPath(
_dashPath(
Path()..addRRect(rrect),
dashLength: dashLength,
dashGap: dashGap,
),
paint,
);
}
void _paintRectangle(Canvas canvas, Rect rect) {
final Paint paint = Paint()
..color = top.color
..strokeWidth = top.width
..style = PaintingStyle.stroke;
if (top.width > 0.0) {
final Path topPath = Path()
..moveTo(rect.left, rect.top)
..lineTo(rect.right, rect.top);
canvas.drawPath(
_dashPath(topPath, dashLength: dashLength, dashGap: dashGap),
paint..color = top.color,
);
}
if (right.width > 0.0) {
final Path rightPath = Path()
..moveTo(rect.right, rect.top)
..lineTo(rect.right, rect.bottom);
canvas.drawPath(
_dashPath(rightPath, dashLength: dashLength, dashGap: dashGap),
paint..color = right.color,
);
}
if (bottom.width > 0.0) {
final Path bottomPath = Path()
..moveTo(rect.right, rect.bottom)
..lineTo(rect.left, rect.bottom);
canvas.drawPath(
_dashPath(bottomPath, dashLength: dashLength, dashGap: dashGap),
paint..color = bottom.color,
);
}
if (left.width > 0.0) {
final Path leftPath = Path()
..moveTo(rect.left, rect.bottom)
..lineTo(rect.left, rect.top);
canvas.drawPath(
_dashPath(leftPath, dashLength: dashLength, dashGap: dashGap),
paint..color = left.color,
);
}
}
Path _dashPath(
Path source, {
required double dashLength,
required double dashGap,
}) {
final Path dest = Path();
final PathMetrics metrics = source.computeMetrics();
for (final PathMetric metric in metrics) {
double distance = 0.0;
bool draw = true;
while (distance < metric.length) {
final double length = draw ? dashLength : dashGap;
if (draw) {
dest.addPath(
metric.extractPath(distance, distance + length),
Offset.zero,
);
}
distance += length;
draw = !draw;
}
}
return dest;
}
@override
ShapeBorder scale(double t) {
return DashedBorder(
top: top.scale(t),
right: right.scale(t),
bottom: bottom.scale(t),
left: left.scale(t),
dashLength: dashLength * t,
dashGap: dashGap * t,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment