greenzebra
greenzebra

Reputation: 577

Drawing curved corner borders in Flutter with dart:ui Canvas

I am using the https://pub.dev/packages/ncnn_yolox_flutter package for object detection in my Flutter app. I want the border outline of detections to look similar to the image below; curved corners only. (Different opacity not necessary, although if anyone knows how to draw that dynamically I would be interested).

What I'm looking for:

enter image description here

I currently have two options working.

  1. Curved corners, but drawing the entire rectangle... Code and picture below (e references my detection results and drawRectPaint is my decoration):
final rect = ui.Rect.fromLTWH(
        e.x,
        e.y,
        e.width,
        e.height,
      );
      canvas.drawRRect(
        RRect.fromRectAndRadius(rect, Radius.circular(10.0)),
        drawRectPaint,
      );

enter image description here

  1. Corners only but without the curve. (Code for just the top left corner below):
var topLeftPath = Path();

topLeftPath.addPolygon([
        Offset((e.x), (e.y + 40)),
        Offset((e.x), (e.y)),
        Offset((e.x + 40), (e.y)),
      ], false);

canvas.drawPath(
        topLeftPath,
        drawRectPaint,
      );

enter image description here

Upvotes: 1

Views: 1936

Answers (3)

Kairat
Kairat

Reputation: 788

Canvas.drawArc(...) is optimized for drawing arcs and should be faster than Path.arcTo:

@override
  void paint(Canvas canvas, Size size) {
    const rect = Rect.fromLTRB(0, 0, 24, 24);
    final paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 5
      ..strokeCap = StrokeCap.round;
    const len = 50.0;
    canvas.drawArc(rect, pi, pi / 2, false, paint);
    canvas.drawLine(const Offset(0, 12), const Offset(0, len), paint);
    canvas.drawLine(const Offset(12, 0), const Offset(len, 0), paint);
    canvas.drawArc(rect.translate(_w - 24, 0), 3 * pi / 2, pi / 2, false, paint);
    canvas.drawLine(Offset(_w, 12), Offset(_w, len), paint);
    canvas.drawLine(Offset(_w - len, 0), Offset(_w - 12, 0), paint);
    canvas.drawArc(rect.translate(0, _w - 24), pi / 2, pi / 2, false, paint);
    canvas.drawLine(Offset(12, _w), Offset(len, _w), paint);
    canvas.drawLine(Offset(0, _w - len), Offset(0, _w - 12), paint);
    canvas.drawArc(rect.translate(_w - 24, _w - 24), 0, pi / 2, false, paint);
    canvas.drawLine(Offset(_w - len, _w), Offset(_w - 12, _w), paint);
    canvas.drawLine(Offset(_w, _w - len), Offset(_w, _w - 12), paint);
  }

Upvotes: 0

PixelToast
PixelToast

Reputation: 965

Clever use of PathFillType.evenOdd to darken the outside, and arcTo for the outer lines:

https://dartpad.dartlang.org/?id=20afacc59439722a28c2f7ccea4782bf

enter image description here

class ViewfinderPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final canvasRect = Offset.zero & size;
    const rectWidth = 300.0;
    final rect = Rect.fromCircle(
      center: canvasRect.center,
      radius: rectWidth / 2,
    );
    const radius = 16.0;
    const strokeWidth = 6.0;
    const extend = radius + 24.0;
    const arcSize = Size.square(radius * 2);

    canvas.drawPath(
      Path()
        ..fillType = PathFillType.evenOdd
        ..addRRect(
          RRect.fromRectAndRadius(
            rect,
            const Radius.circular(radius),
          ).deflate(strokeWidth / 2),
        )
        ..addRect(canvasRect),
      Paint()..color = Colors.black26,
    );

    canvas.save();
    canvas.translate(rect.left, rect.top);
    final path = Path();
    for (var i = 0; i < 4; i++) {
      final l = i & 1 == 0;
      final t = i & 2 == 0;
      path
        ..moveTo(l ? 0 : rectWidth, t ? extend : rectWidth - extend)
        ..arcTo(
            Offset(l ? 0 : rectWidth - arcSize.width,
                    t ? 0 : rectWidth - arcSize.width) &
                arcSize,
            l ? pi : pi * 2,
            l == t ? pi / 2 : -pi / 2,
            false)
        ..lineTo(l ? extend : rectWidth - extend, t ? 0 : rectWidth);
    }
    canvas.drawPath(
      path,
      Paint()
        ..color = Colors.deepOrange
        ..strokeWidth = strokeWidth
        ..style = PaintingStyle.stroke,
    );
    canvas.restore();
  }

  @override
  bool shouldRepaint(ViewfinderPainter oldDelegate) => false;
}

Upvotes: 1

TripleNine
TripleNine

Reputation: 1932

You have to use moveTo, lineTo and arcTo to draw border outlines with rounded corners. You need to adjust the values to your needs.

Try this:

topLeftPath.moveTo(e.x, 40);
topLeftPath.relativeLineTo(e.x, -20);
topLeftPath.arcTo(
  Rect.fromCenter(
    center: Offset(20,20), 
    width: 40, 
    height: 40
  ), math.pi, math.pi/2, false);
topLeftPath.relativeLineTo(20, e.y);
canvas.drawPath(topLeftPath,drawRectPaint);

Upvotes: 1

Related Questions