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;
}
42
likes
130
pub points
91%
popularity

Publisher

unverified uploader

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

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (LICENSE)

Dependencies

flutter

More

Packages that depend on arrow_path