arrow_path 3.1.0 copy "arrow_path: ^3.1.0" to clipboard
arrow_path: ^3.1.0 copied to clipboard

Draw arrows with Path objects easily. Paths can be composited to add arrows to any curve and draw all at once.

example/lib/main.dart

import 'dart:math' as math;

import 'package:arrow_path/arrow_path.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'Arrow Path Example',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: const ExampleApp(),
      );
}

class ExampleApp extends StatelessWidget {
  const ExampleApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: const Text('Arrow Path Example'),
        ),
        body: SingleChildScrollView(
          child: ClipRect(
            child: CustomPaint(
              size: Size(MediaQuery.of(context).size.width, 700),
              painter: ArrowPainter(),
            ),
          ),
        ),
      );
}

class ArrowPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    /// The arrows usually looks better with rounded caps.
    final Paint paint = Paint()
      ..color = Colors.black
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round
      ..strokeJoin = StrokeJoin.round
      ..strokeWidth = 3.0;

    /// Draw a single arrow.
    {
      Path path = Path();
      path.moveTo(size.width * 0.25, 60);
      path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 0);
      path = ArrowPath.addTip(path);

      canvas.drawPath(path, paint..color = Colors.blue);

      const TextSpan textSpan = TextSpan(
        text: 'Single arrow',
        style: TextStyle(color: Colors.blue),
      );
      final TextPainter textPainter = TextPainter(
        text: textSpan,
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr,
      );
      textPainter.layout(minWidth: size.width);
      textPainter.paint(canvas, const Offset(0, 36));
    }

    /// Draw a double sided arrow.
    {
      Path path = Path();
      path.moveTo(size.width * 0.25, 120);
      path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 0);
      path = ArrowPath.addTip(path);
      path = ArrowPath.addTip(path, isBackward: true);

      canvas.drawPath(path, paint..color = Colors.cyan);

      const TextSpan textSpan = TextSpan(
        text: 'Double sided arrow',
        style: TextStyle(color: Colors.cyan),
      );
      final TextPainter textPainter = TextPainter(
        text: textSpan,
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr,
      );
      textPainter.layout(minWidth: size.width);
      textPainter.paint(canvas, const Offset(0, 96));
    }

    /// Use complex path.
    {
      Path path = Path();
      path.moveTo(size.width * 0.25, 180);
      path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 50);
      path.relativeCubicTo(0, 0, -size.width * 0.25, 0, -size.width * 0.5, 50);
      path.relativeCubicTo(0, 0, size.width * 0.125, 10, size.width * 0.25, -10);
      path = ArrowPath.addTip(path);
      canvas.drawPath(path, paint..color = Colors.blue);

      const TextSpan textSpan = TextSpan(
        text: 'Complex path',
        style: TextStyle(color: Colors.blue),
      );
      final TextPainter textPainter = TextPainter(
        text: textSpan,
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr,
      );
      textPainter.layout(minWidth: size.width);
      textPainter.paint(canvas, const Offset(0, 168));
    }

    /// Path with several forward arrow and one backward arrow.
    /// IMPORTANT: You should never use more than a single backward arrow on the same path, or the results
    /// may be unpredictable due to limitations of the Path API.
    ///
    /// If you need to build a complex path with several backward arrows then use sub-paths and
    /// merge them with Path.addPath (see after).
    {
      Path path = Path();
      path.moveTo(size.width * 0.25, 358);
      path.relativeLineTo(size.width * 0.13, 50);
      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1);
      path.relativeLineTo(size.width * 0.13, -50);
      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1);
      path.relativeLineTo(size.width * 0.08, 50);
      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1);
      path.relativeLineTo(size.width * 0.08, -50);
      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1);
      path.relativeLineTo(size.width * 0.04, 50);
      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1);
      path.relativeLineTo(size.width * 0.04, -50);
      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1);
      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1, isBackward: true);

      canvas.drawPath(path, paint..color = Colors.cyan);

      const TextSpan textSpan = TextSpan(
        text: 'Single Path with multiple forward and one backward arrow tips.',
        style: TextStyle(color: Colors.cyan),
      );
      final TextPainter textPainter = TextPainter(
        text: textSpan,
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr,
      );
      textPainter.layout(minWidth: size.width);
      textPainter.paint(canvas, const Offset(0, 326));
    }

    /// Path with several backward arrow and one forward arrow.
    /// IMPORTANT: Due to limitations of the Path API this can only be achieved reliably by using sub-paths.
    {
      Path path = Path();
      Offset penPosition = Offset(size.width * 0.25, 470);
      path.moveTo(penPosition.dx, penPosition.dy);
      penPosition += Offset(size.width * 0.13, 50);
      path.lineTo(penPosition.dx, penPosition.dy);
      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1, isBackward: true);

      Path subPath = Path();
      subPath.moveTo(penPosition.dx, penPosition.dy);
      penPosition += Offset(size.width * 0.13, -50);
      subPath.lineTo(penPosition.dx, penPosition.dy);
      subPath = ArrowPath.addTip(subPath, tipAngle: math.pi * 0.1, isBackward: true);
      path.addPath(subPath, Offset.zero);

      subPath = Path();
      subPath.moveTo(penPosition.dx, penPosition.dy);
      penPosition += Offset(size.width * 0.08, 50);
      subPath.lineTo(penPosition.dx, penPosition.dy);
      subPath = ArrowPath.addTip(subPath, tipAngle: math.pi * 0.1, isBackward: true);
      path.addPath(subPath, Offset.zero);

      subPath = Path();
      subPath.moveTo(penPosition.dx, penPosition.dy);
      penPosition += Offset(size.width * 0.08, -50);
      subPath.lineTo(penPosition.dx, penPosition.dy);
      subPath = ArrowPath.addTip(subPath, tipAngle: math.pi * 0.1, isBackward: true);
      path.addPath(subPath, Offset.zero);

      subPath = Path();
      subPath.moveTo(penPosition.dx, penPosition.dy);
      penPosition += Offset(size.width * 0.04, 50);
      subPath.lineTo(penPosition.dx, penPosition.dy);
      subPath = ArrowPath.addTip(subPath, tipAngle: math.pi * 0.1, isBackward: true);
      path.addPath(subPath, Offset.zero);

      subPath = Path();
      subPath.moveTo(penPosition.dx, penPosition.dy);
      penPosition += Offset(size.width * 0.04, -50);
      subPath.lineTo(penPosition.dx, penPosition.dy);
      subPath = ArrowPath.addTip(subPath, tipAngle: math.pi * 0.1, isBackward: true);
      path.addPath(subPath, Offset.zero);

      path = ArrowPath.addTip(path, tipAngle: math.pi * 0.1);

      canvas.drawPath(path, paint..color = Colors.blue);

      const TextSpan textSpan = TextSpan(
        text: 'Single Path with multiple backward arrow tips.',
        style: TextStyle(color: Colors.blue),
      );
      final TextPainter textPainter = TextPainter(
        text: textSpan,
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr,
      );
      textPainter.layout(minWidth: size.width);
      textPainter.paint(canvas, const Offset(0, 436));
    }

    /// Adjusted
    {
      Path path = Path();
      path.moveTo(size.width * 0.1, 590);
      path.relativeCubicTo(0, 0, size.width * 0.3, 50, size.width * 0.25, 75);
      path = ArrowPath.addTip(path, isAdjusted: true);
      canvas.drawPath(path, paint..color = Colors.cyan);

      const TextSpan textSpan = TextSpan(
        text: 'Adjusted',
        style: TextStyle(color: Colors.cyan),
      );
      final TextPainter textPainter = TextPainter(
        text: textSpan,
        textAlign: TextAlign.left,
        textDirection: TextDirection.ltr,
      );
      textPainter.layout();
      textPainter.paint(canvas, Offset(size.width * 0.2, 572));
    }

    /// Non adjusted.
    {
      Path path = Path();
      path.moveTo(size.width * 0.6, 590);
      path.relativeCubicTo(0, 0, size.width * 0.3, 50, size.width * 0.25, 75);
      path = ArrowPath.addTip(path, isAdjusted: false);

      canvas.drawPath(path, paint..color = Colors.cyan);

      const TextSpan textSpan = TextSpan(
        text: 'Non adjusted',
        style: TextStyle(color: Colors.cyan),
      );
      final TextPainter textPainter = TextPainter(
        text: textSpan,
        textAlign: TextAlign.left,
        textDirection: TextDirection.ltr,
      );
      textPainter.layout();
      textPainter.paint(canvas, Offset(size.width * 0.65, 572));
    }
  }

  @override
  bool shouldRepaint(ArrowPainter oldDelegate) => false;
}
48
likes
150
points
4.01k
downloads

Publisher

unverified uploader

Weekly Downloads

Draw arrows with Path objects easily. Paths can be composited to add arrows to any curve and draw all at once.

Repository (GitHub)

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on arrow_path