How to add line dash in Flutter

Viewed 49998

How to make a line dash in Flutter like this?

Image

20 Answers

As a workaround, in your case, you can do something like this

class MySeparator extends StatelessWidget {
  const MySeparator({Key? key, this.height = 1, this.color = Colors.black})
      : super(key: key);
  final double height;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        final boxWidth = constraints.constrainWidth();
        const dashWidth = 10.0;
        final dashHeight = height;
        final dashCount = (boxWidth / (2 * dashWidth)).floor();
        return Flex(
          children: List.generate(dashCount, (_) {
            return SizedBox(
              width: dashWidth,
              height: dashHeight,
              child: DecoratedBox(
                decoration: BoxDecoration(color: color),
              ),
            );
          }),
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          direction: Axis.horizontal,
        );
      },
    );
  }
}

and use it const MySeparator()


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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Material(
        child: Container(
          color: Colors.blue,
          child: Center(
            child: Container(
              height: 600,
              width: 350,
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.all(Radius.circular(16.0)),
              ),
              child: Flex(
                direction: Axis.vertical,
                children: [
                  Expanded(child: Container()),
                  const MySeparator(color: Colors.grey),
                  Container(height: 200),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

demo

class DashedLinePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    double dashWidth = 9, dashSpace = 5, startX = 0;
    final paint = Paint()
      ..color = Colors.grey
      ..strokeWidth = 1;
    while (startX < size.width) {
      canvas.drawLine(Offset(startX, 0), Offset(startX + dashWidth, 0), paint);
      startX += dashWidth + dashSpace;
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}
// garis putus putus
Row(
children: List.generate(150~/10, (index) => Expanded(
 child: Container(
  color: index%2==0?Colors.transparent
  :Colors.grey,
  height: 2,
 ),
 )),
),

CustomPainter can help here as well. In this example is a vertical dash line but could be changed easily.

class LineDashedPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()..strokeWidth = 2;
    var max = 35;
    var dashWidth = 5;
    var dashSpace = 5;
    double startY = 0;
    while (max >= 0) {
      canvas.drawLine(Offset(0, startY), Offset(0, startY + dashWidth), paint);
      final space = (dashSpace + dashWidth);
      startY += space;
      max -= space;
    }
  }

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

and that use CustomPaint Widget:

CustomPaint(painter: LineDashedPainter())

I have written flutter_dash library for drawing that dash. Just one line and you should have a dash :D

Dash(length: 200, dashColor: Colors.red)

Give it a try!

The following code creates a dashed path not only for lines, but for any path you want dashed.

Demo:

enter image description here

The idea is to take the originalPath and move along it, alternately adding dashes and gaps until the entire path has been extracted:

Path _getDashedPath(
  Path originalPath,
  double dashLength,
  double dashGapLength,
) {
  final metricsIterator = originalPath.computeMetrics().iterator;
  while (metricsIterator.moveNext()) {
    final metric = metricsIterator.current;
    _dashedPathProperties.extractedPathLength = 0.0;
    while (_dashedPathProperties.extractedPathLength < metric.length) {
      if (_dashedPathProperties.addDashNext) {
        _dashedPathProperties.addDash(metric, dashLength);
      } else {
        _dashedPathProperties.addDashGap(metric, dashGapLength);
      }
    }
  }
  return _dashedPathProperties.path;
}

I created a class DashedPathProperties to track things like the currently extractedPathLength or the _remainingDashLength, which becomes relevant if the originalPath consists of several sub-paths and a dash (or dash gap) must be continued on the next sub-path:

class DashedPathProperties {
  double extractedPathLength;
  Path path;

  final double _dashLength;
  double _remainingDashLength;
  double _remainingDashGapLength;
  bool _previousWasDash;

  DashedPathProperties({
    required this.path,
    required double dashLength,
    required double dashGapLength,
  })  : assert(dashLength > 0.0, 'dashLength must be > 0.0'),
        assert(dashGapLength > 0.0, 'dashGapLength must be > 0.0'),
        _dashLength = dashLength,
        _remainingDashLength = dashLength,
        _remainingDashGapLength = dashGapLength,
        _previousWasDash = false,
        extractedPathLength = 0.0;

  //...
}

You can use like this (you could wrap your CustomPaint in a ClipRect if you want to make sure that the painter cannot paint outside of the bounds):

CustomPaint(
  painter: DashedPathPainter(
    originalPath: Path()
      ..addOval(
        const Rect.fromLTWH(0, 0, 100, 100),
      ),
    pathColor: Colors.white,
  ),
  size: const Size(100.0, 100.0),
)

Full example code you can run in DartPad:

import 'dart:ui' as ui;
import 'dart:math' as math;

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: const Scaffold(
        body: Center(
          child: ExampleDashedPath(),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const SizedBox(height: 50),
        CustomPaint(
          painter: DashedPathPainter(
            originalPath: Path()..lineTo(100, 0),
            pathColor: Colors.red,
            strokeWidth: 5.0,
            dashGapLength: 10.0,
            dashLength: 10.0,
          ),
          size: const Size(100.0, 2.0),
        ),
        const SizedBox(height: 50),
        CustomPaint(
          painter: DashedPathPainter(
            originalPath: Path()
              ..addOval(
                const Rect.fromLTWH(0, 0, 100, 100),
              ),
            pathColor: Colors.white,
          ),
          size: const Size(100.0, 100.0),
        ),
        const SizedBox(height: 50),
        CustomPaint(
          painter: DashedPathPainter(
            originalPath: Path()
              ..addRect(
                const Rect.fromLTWH(0, 0, 100, 100),
              )
              ..lineTo(100, 100),
            pathColor: Colors.grey,
            strokeWidth: 2.0,
            dashLength: 25.0,
          ),
          size: const Size(100.0, 100.0),
        ),
      ],
    );
  }
}

class DashedPathPainter extends CustomPainter {
  final Path originalPath;
  final Color pathColor;
  final double strokeWidth;
  final double dashGapLength;
  final double dashLength;
  late DashedPathProperties _dashedPathProperties;

  DashedPathPainter({
    required this.originalPath,
    required this.pathColor,
    this.strokeWidth = 3.0,
    this.dashGapLength = 5.0,
    this.dashLength = 10.0,
  });

  @override
  void paint(Canvas canvas, Size size) {
    _dashedPathProperties = DashedPathProperties(
      path: Path(),
      dashLength: dashLength,
      dashGapLength: dashGapLength,
    );
    final dashedPath = _getDashedPath(originalPath, dashLength, dashGapLength);
    canvas.drawPath(
      dashedPath,
      Paint()
        ..style = PaintingStyle.stroke
        ..color = pathColor
        ..strokeWidth = strokeWidth,
    );
  }

  @override
  bool shouldRepaint(DashedPathPainter oldDelegate) =>
      oldDelegate.originalPath != originalPath ||
      oldDelegate.pathColor != pathColor ||
      oldDelegate.strokeWidth != strokeWidth ||
      oldDelegate.dashGapLength != dashGapLength ||
      oldDelegate.dashLength != dashLength;

  Path _getDashedPath(
    Path originalPath,
    double dashLength,
    double dashGapLength,
  ) {
    final metricsIterator = originalPath.computeMetrics().iterator;
    while (metricsIterator.moveNext()) {
      final metric = metricsIterator.current;
      _dashedPathProperties.extractedPathLength = 0.0;
      while (_dashedPathProperties.extractedPathLength < metric.length) {
        if (_dashedPathProperties.addDashNext) {
          _dashedPathProperties.addDash(metric, dashLength);
        } else {
          _dashedPathProperties.addDashGap(metric, dashGapLength);
        }
      }
    }
    return _dashedPathProperties.path;
  }
}

class DashedPathProperties {
  double extractedPathLength;
  Path path;

  final double _dashLength;
  double _remainingDashLength;
  double _remainingDashGapLength;
  bool _previousWasDash;

  DashedPathProperties({
    required this.path,
    required double dashLength,
    required double dashGapLength,
  })  : assert(dashLength > 0.0, 'dashLength must be > 0.0'),
        assert(dashGapLength > 0.0, 'dashGapLength must be > 0.0'),
        _dashLength = dashLength,
        _remainingDashLength = dashLength,
        _remainingDashGapLength = dashGapLength,
        _previousWasDash = false,
        extractedPathLength = 0.0;

  bool get addDashNext {
    if (!_previousWasDash || _remainingDashLength != _dashLength) {
      return true;
    }
    return false;
  }

  void addDash(ui.PathMetric metric, double dashLength) {
    // Calculate lengths (actual + available)
    final end = _calculateLength(metric, _remainingDashLength);
    final availableEnd = _calculateLength(metric, dashLength);
    // Add path
    final pathSegment = metric.extractPath(extractedPathLength, end);
    path.addPath(pathSegment, Offset.zero);
    // Update
    final delta = _remainingDashLength - (end - extractedPathLength);
    _remainingDashLength = _updateRemainingLength(
      delta: delta,
      end: end,
      availableEnd: availableEnd,
      initialLength: dashLength,
    );
    extractedPathLength = end;
    _previousWasDash = true;
  }

  void addDashGap(ui.PathMetric metric, double dashGapLength) {
    // Calculate lengths (actual + available)
    final end = _calculateLength(metric, _remainingDashGapLength);
    final availableEnd = _calculateLength(metric, dashGapLength);
    // Move path's end point
    ui.Tangent tangent = metric.getTangentForOffset(end)!;
    path.moveTo(tangent.position.dx, tangent.position.dy);
    // Update
    final delta = end - extractedPathLength;
    _remainingDashGapLength = _updateRemainingLength(
      delta: delta,
      end: end,
      availableEnd: availableEnd,
      initialLength: dashGapLength,
    );
    extractedPathLength = end;
    _previousWasDash = false;
  }

  double _calculateLength(ui.PathMetric metric, double addedLength) {
    return math.min(extractedPathLength + addedLength, metric.length);
  }

  double _updateRemainingLength({
    required double delta,
    required double end,
    required double availableEnd,
    required double initialLength,
  }) {
    return (delta > 0 && availableEnd == end) ? delta : initialLength;
  }
}

Vertical dashed line:
I modifed maksimr's example:

class DashedLine extends StatelessWidget {
  final double height;
  final double heightContainer;
  final Color color;

  const DashedLine({this.height = 3, this.color = Colors.black, this.heightContainer = 70});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: heightContainer,
      child: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          final boxHeight = constraints.constrainHeight();
          final dashWidth = 10.0;
          final dashHeight = height;
          final dashCount = (boxHeight / (2 * dashHeight)).floor();
          return Flex(
            children: List.generate(dashCount, (_) {
              return SizedBox(
                width: dashWidth,
                height: dashHeight,
                child: DecoratedBox(
                  decoration: BoxDecoration(color: color),
                ),
              );
            }),
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            direction: Axis.vertical,
          );
        },
      ),
    );
  }
}

You can use CustomPainter with a linear gradient dashed shader for your lines.

   // GradientRotation(3.14 / 2) — for vertical lines with dashes
   // GradientRotation(0) — for horizontal lines with dashes
   // .createShader(Rect.fromLTWH(0, 0, 10, 10) — 10 is the size of repeated shaders part
   // This method can be tricky if you need a line oriented by some angle.

Paint()..shader = LinearGradient(
                  colors: [Colors.blue, Colors.transparent],
                  stops: [0.5, 0.5],
                  tileMode: TileMode.repeated,
                  transform: GradientRotation(3.14 / 2))
              .createShader(Rect.fromLTWH(0, 0, 10, 10))
          ..style = PaintingStyle.stroke
          ..strokeWidth = 6

I created a CustomPainter by integrating the solution here and the math from here. This CustomPainter allows to draw a solid line or a dashed line by specifying the length of the dash and the length of the space between dashes. But the best thing is you can even draw the solid or dashed line in all directions. I mean horizontal, vertical, and even diagonal!

This is the code for the CustomPainter:

import 'dart:math';

import 'package:flutter/material.dart';

class LinePainter extends CustomPainter {
  final Offset firstOffset;
  final Offset secondOffset;
  final Color color;
  final double strokeWidth;
  final double dashLength;
  final double dashSpace;

  const LinePainter({
    required this.firstOffset,
    required this.secondOffset,
    this.color = Colors.black,
    this.strokeWidth = 2.0,
    this.dashLength = 4.0,
    this.dashSpace = 4.0,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = color
      ..strokeWidth = strokeWidth;
    _drawDashedLine(
        dashLength, dashSpace, firstOffset, secondOffset, canvas, size, paint);
  }

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

  void _drawDashedLine(double dashLength, double dashSpace, Offset firstOffset,
      Offset secondOffset, Canvas canvas, Size size, Paint paint) {
    var startOffset = firstOffset;

    var intervals = _getDirectionVector(firstOffset, secondOffset).length /
        (dashLength + dashSpace);

    for (var i = 0; i < intervals; i++) {
      var endOffset = _getNextOffset(startOffset, secondOffset, dashLength);

      /// Draw a small line.
      canvas.drawLine(startOffset, endOffset, paint);

      /// Update the starting offset.
      startOffset = _getNextOffset(endOffset, secondOffset, dashSpace);
    }
  }

  Offset _getNextOffset(
    Offset firstOffset,
    Offset secondOffset,
    double smallVectorLength,
  ) {
    var directionVector = _getDirectionVector(firstOffset, secondOffset);

    var rescaleFactor = smallVectorLength / directionVector.length;
    if (rescaleFactor.isNaN || rescaleFactor.isInfinite) {
      rescaleFactor = 1;
    }

    var rescaledVector = Offset(directionVector.vector.dx * rescaleFactor,
        directionVector.vector.dy * rescaleFactor);

    var newOffset = Offset(
        firstOffset.dx + rescaledVector.dx, firstOffset.dy + rescaledVector.dy);

    return newOffset;
  }

  DirectionVector _getDirectionVector(Offset firstVector, Offset secondVector) {
    var directionVector = Offset(
        secondVector.dx - firstVector.dx, secondVector.dy - firstVector.dy);

    var directionVectorLength =
        sqrt(pow(directionVector.dx, 2) + pow(directionVector.dy, 2));

    return DirectionVector(
      vector: directionVector,
      length: directionVectorLength,
    );
  }
}

class DirectionVector {
  final Offset vector;
  final double length;

  const DirectionVector({
    required this.vector,
    required this.length,
  });
}

You can use this CustomPainter by setting up the painter parameter of a CustomPaint widget, like this:

CustomPaint(
  painter: LinePainter(
    firstOffset: Offset(0, 0),
    secondOffset: Offset(10, 10),
  ),
),

The result is shown in the following image:

Dashed line using the CustomPainter

 Row(
    children: List.generate(20, (index) {
                 return Expanded(
                   child: Padding(
                     padding: const EdgeInsets.only(left: 8.0),
                     child: Container(
                       height: 5,
                       width: 10,
                       color: Color(0XFFf2f2f2),
                     ),
                   ),
                 );
               }),
             )

enter image description here

enter image description here

Create this class:

class DotWidget extends StatelessWidget {
  final double totalWidth, dashWidth, emptyWidth, dashHeight;

  final Color dashColor;

  const DotWidget({
    this.totalWidth = 300,
    this.dashWidth = 10,
    this.emptyWidth = 5,
    this.dashHeight = 2,
    this.dashColor = Colors.black,
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: List.generate(
        totalWidth ~/ (dashWidth + emptyWidth),
            (_) => Container(
          width: dashWidth,
          height: dashHeight,
          color: dashColor,
          margin: EdgeInsets.only(left: emptyWidth / 2, right: emptyWidth / 2),
        ),
      ),
    );
  }
}

Usage:

Use it like any other widget

 child: DotWidget(
   dashColor: Colors.black,
   dashHeight: 2,
   dashWidth: 100,
 )

Here is the code for horizontal dashed line, like your image. CustomPaint is highly recommended by flutter team for stuff like this. It is fast and efficient for rendering also. You can play with Offset to change the direction.

 class MyClass extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: CustomPaint(
        painter: MyLinePainter(),
      ),
    );
  }
}

class MyLinePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var max = 100;
    var dashWidth, dashSpace = 5;
    double startX = 0;
    final paint = Paint()..color = Colors.grey;
    while (max >= 0) {
      canvas.drawLine(Offset(startX, 0), Offset(startX + dashWidth, 0), paint..strokeWidth = 1);
      final space = (dashSpace + dashWidth);
      startX += space;
      max -= space;
    }
  }

You can use this:

Widget dashedHorizontalLine(){
  return Row(
    children: [
      for (int i = 0; i < 20; i++)
        Expanded(
          child: Row(
            children: [
              Expanded(
                child: Divider(
                  color: AppColors.darkGreen,
                  thickness: 2,
                ),
              ),
              Expanded(
                child: Container(),
              ),
            ],
          ),
        ),
    ],
  );
}

I came up with this solution.

Row(                        // Dashed line
    children: [
      for (int i = 0; i < 25; i++)
        Container(
          width: 5,
          height: 1,
          decoration: BoxDecoration(
            border: Border(
              bottom: BorderSide(
                width: 1,
                color: i % 2 == 0
                    ? const Color.fromRGBO(214, 211, 211, 1)
                    : Colors.transparent,
              ),
            ),
          ),
        ),
    ],
  ),

Output:

Output:

Thank to marksimr answer, here is the code for both vertical and horizontal dash line.

Horizontal usage:

DashLineView(
  fillRate: 0.7,
),

Vertical usage:

DashLineView(
  fillRate: 0.7,
  direction: Axis.vertical,
),

Full code:

class DashLineView extends StatelessWidget {
  final double dashHeight;
  final double dashWith;
  final Color dashColor;
  final double fillRate; // [0, 1] totalDashSpace/totalSpace
  final Axis direction;

  DashLineView(
      {this.dashHeight = 1,
      this.dashWith = 8,
      this.dashColor = Colors.black,
      this.fillRate = 0.5,
      this.direction = Axis.horizontal});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        final boxSize = direction == Axis.horizontal
            ? constraints.constrainWidth()
            : constraints.constrainHeight();
        final dCount = (boxSize * fillRate / dashWith).floor();
        return Flex(
          children: List.generate(dCount, (_) {
            return SizedBox(
              width: direction == Axis.horizontal ? dashWith : dashHeight,
              height: direction == Axis.horizontal ? dashHeight : dashWith,
              child: DecoratedBox(
                decoration: BoxDecoration(color: dashColor),
              ),
            );
          }),
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          direction: direction,
        );
      },
    );
  }
}

Try this,

class DotDivider extends StatelessWidget {
  final double width;
  final double height;
  final double gap;
  final Color color;
  final double lineHeight;

  const DotDivider(
      {this.height = 1.0,
      this.color = Colors.black,
      this.width = 2.0,
      this.gap = 2.0,
      this.lineHeight = 10.0});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        final boxWidth = constraints.constrainWidth();
        final dashWidth = width;
        final dashHeight = height;
        final dashCount = (boxWidth / dashWidth).floor();
        return Container(
          height: (lineHeight * 2) + height,
          child: ListView.builder(
            physics: NeverScrollableScrollPhysics(),
            scrollDirection: Axis.horizontal,
            itemCount: dashCount,
            itemBuilder: (BuildContext context, int index) => Center(
              child: Container(
                width: dashWidth,
                height: dashHeight,
                margin:
                    EdgeInsets.symmetric(vertical: lineHeight, horizontal: gap),
                decoration: BoxDecoration(color: color),
              ),
            ),
          ),
        );
      },
    );
  }
}

You should prefer using CustomPainter because it's more performance and suitable for such issues.

class DashLine extends StatelessWidget {
  const DashLine({
    Key key,
    this.color,
    this.dashWidth,
    this.dashSpace,
    this.strokeWidth,
  }) : super(key: key);

  final Color color;
  final double dashWidth;
  final double dashSpace;
  final double strokeWidth;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: _DashLinePainter(
        color: color,
        dashWidth: dashWidth,
        dashSpace: dashSpace,
        strokeWidth: strokeWidth,
      ),
    );
  }
}

class _DashLinePainter extends CustomPainter {
  _DashLinePainter({
    Color color,
    double dashWidth,
    double dashSpace,
    double strokeWidth,
  })  : _color = color ?? Colors.red,
        _dashWidth = dashWidth ?? 5.0,
        _dashSpace = dashSpace ?? 5.0,
        _strokeWidth = strokeWidth ?? 1.0;

  final Color _color;
  final double _dashWidth;
  final double _dashSpace;
  final double _strokeWidth;

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = _color
      ..strokeWidth = _strokeWidth;

    var max = size.width;
    var startX = 0.0;
    while (max >= 0) {
      canvas.drawLine(Offset(startX, 0), Offset(startX + _dashWidth, 0), paint);
      final space = (_dashSpace + _dashWidth);
      startX += space;
      max -= space;
    }
  }

  @override
  bool shouldRepaint(_DashLinePainter oldDelegate) {
    return _color != oldDelegate._color ||
        _dashWidth != oldDelegate._dashWidth ||
        _dashSpace != oldDelegate._dashSpace ||
        _strokeWidth != oldDelegate._strokeWidth;
  }
}

Use dotted_line: ^3.0.0 lib which provides dashed lines and many more link

import 'package:dotted_line/dotted_line.dart';

DottedLine(
  direction: Axis.horizontal,
  lineLength: double.infinity,
  lineThickness: 1.0,
  dashLength: 4.0,
  dashColor: Colors.grey,
  dashRadius: 0.0,
  dashGapLength: 4.0,
  dashGapColor: Colors.transparent,
  dashGapRadius: 0.0,
)

Output:

enter image description here

Container(
                  color: Colors.white,
                  height: 40.0,
                  child: Center(
                    child: Text(
                      "---------------------------------------------------------------------------",
                      maxLines: 1,
                      style: typoNormalTextRegular.copyWith(
                          color: colorABGray),
                    ),
                  ),
                ),

Only use Text Widget, easy solution

When going for the CustomPainter approach, painting a dashed line in any direction can be achieved by a snippet like this:

  void _drawDashedLine(Canvas canvas, Offset start, Offset end, Paint paint) {
    const dashLength = 10.0;
    const stride = 2 * dashLength;

    var distance = (end - start).distance;
    while (distance > 0) {
      final remaining = end - start;
      final direction = remaining / remaining.distance;
      final next = start + (direction * dashLength);
      canvas.drawLine(start, next, paint);
      start = start + (direction * stride);
      distance -= stride;
    }
  }

This method should be embedded in your CustomPainter implementation of a CustomPaint widget, like mentioned in the other answers.

Related