Kevin Yang
Kevin Yang

Reputation: 737

flutter) How do I make arrow lines with canvas?

I want to put an arrow at the end (or on both sides) of a line drawn with a drawLine() of canvas.

Like this

enter image description here

Can I know how to do this?

Upvotes: 3

Views: 3811

Answers (3)

Saad Ardati
Saad Ardati

Reputation: 419

Here's a solution derived from obscure's answer to subtract the head of the arrow from the line so it does not stick out:

  void drawArrow(Offset a, Offset b, Canvas canvas, Color color) {
    final paint = Paint()
      ..color = color
      ..strokeWidth = 2
      ..strokeCap = StrokeCap.round;
    const arrowSize = 10;
    const arrowAngle = pi / 6;

    final dX = b.dx - a.dx;
    final dY = b.dy - a.dy;
    final angle = atan2(dY, dX);

    // Recalculate b such that it's the end of the line minus the arrow.
    final Offset subtractedB = Offset(
      b.dx - (arrowSize - 2) * cos(angle),
      b.dy - (arrowSize - 2) * sin(angle),
    );

    canvas.drawLine(a, subtractedB, paint);
    final path = Path();

    path.moveTo(b.dx - arrowSize * cos(angle - arrowAngle),
        b.dy - arrowSize * sin(angle - arrowAngle));
    path.lineTo(b.dx, b.dy);
    path.lineTo(b.dx - arrowSize * cos(angle + arrowAngle),
        b.dy - arrowSize * sin(angle + arrowAngle));
    path.close();
    canvas.drawPath(path, paint);
  }

Upvotes: 2

Raihan Hossain
Raihan Hossain

Reputation: 169

Simple:

class ArrowPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 2.0
      ..style = PaintingStyle.stroke;

    // Drawing the line
    Offset startPoint = Offset(size.width / 2, size.height);
    Offset endPoint = Offset(size.width / 2, 0);
    canvas.drawLine(startPoint, endPoint, paint);

    // Drawing the arrowhead
    double arrowSize = 10; // Size of the arrowhead
    double angle = math.pi / 6; // Angle of the arrowhead
    Path path = Path();
    path.moveTo(size.width / 2, 0);
    path.lineTo(size.width / 2 - arrowSize * math.cos(angle), arrowSize * math.sin(angle));
    path.moveTo(size.width / 2, 0);
    path.lineTo(size.width / 2 + arrowSize * math.cos(angle), arrowSize * math.sin(angle));

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

Call the class:

CustomPaint(
            size: Size(10, 200), // Width and height of the arrow
            painter: ArrowPainter(),
            )

Upvotes: 1

obscure
obscure

Reputation: 12891

That's actually not that hard to do - there's just a little math involved.

Let's have a line and paint set up like this:

final paint = Paint()
  ..color = Colors.black
  ..strokeWidth = 2;

final p1 = Offset(50, 50);
final p2 = Offset(250, 150);

canvas.drawLine(p1, p2, paint); 

this will give us:

Now if we think of this line as a vector, we remember a vector has a direction. To get the direction we must find the angle between the start and the end of the line. This can be done using the atan2 function of dart's math library.

So import the math library

import 'dart:math' as math;

and add the following lines

final dX = p2.dx - p1.dx;
final dY = p2.dy - p1.dy;
final angle = math.atan2(dY, dX);

Afterwards let's determine a size and an angle for the arrow we're about to put at the end of the line:

final arrowSize = 15;
final arrowAngle=  25 * math.pi / 180;

Everything that's left is actually drawing the arrow. This is basically done by drawing a triangle at the proper angle the line is facing using a path.

final path = Path();

path.moveTo(p2.dx - arrowSize * math.cos(angle - arrowAngle),
    p2.dy - arrowSize * math.sin(angle - arrowAngle));
path.lineTo(p2.dx, p2.dy);
path.lineTo(p2.dx - arrowSize * math.cos(angle + arrowAngle),
    p2.dy - arrowSize * math.sin(angle + arrowAngle));
path.close();
canvas.drawPath(path, paint);

Which will finally give us:

Upvotes: 13

Related Questions