skip to Main Content

i have this code in flutter , iam using a custom paint to draw a "road like" and the path is done but when i draw circles inside it this happens :

enter image description here

and here is what i want :

enter image description here

and here is my code :

import 'dart:math' as math;

import 'package:flutter/material.dart';

//Add this CustomPaint widget to the Widget Tree

//Copy this CustomPainter code to the Bottom of the File

extension DrawOnPathExtension on Canvas {
  void drawOnPath(
    Path path, {
    required double spacing,
    required int steps,
    required Color defaultColor,
    required Color stepCompletedColor,
    double offsetFromPath = 10.0, // Adjust for desired offset from the path
  }) {
    final paint = Paint()..style = PaintingStyle.fill;
    final pathMetrics = path.computeMetrics();
    double distance = 0.0; // Start at the beginning of the path
    int shapeCounter = 0;

    for (final metric in pathMetrics) {
      while (distance < metric.length) {
        final tangent = metric.getTangentForOffset(distance);
        if (tangent == null) continue;

        // Calculate the normal (perpendicular) vector, consistently on one side
        final normal =
            Offset(-tangent.vector.dy, tangent.vector.dx).normalize();
        // Offset the position by the normal vector and specified offset
        final offsetPosition = tangent.position + normal * offsetFromPath;

        // Determine color based on steps completed
        paint.color =
            shapeCounter < steps * 5 ? defaultColor : stepCompletedColor;

        // Draw circle at the offset position
        drawCircle(this, offsetPosition, paint,
            radius: 5.0); // Adjust radius as needed

        distance += spacing; // Move to the next position along the path
        shapeCounter++;
      }
      distance = 0.0; // Reset distance for next path metric
    }
  }

  void drawCircle(Canvas canvas, Offset position, Paint paint,
      {double? radius}) {
    canvas.drawCircle(position, radius!, paint);
  }
}

extension on Offset {
  // Normalize the Offset to get a unit vector
  Offset normalize() {
    final length = math.sqrt(dx * dx + dy * dy);
    return Offset(dx / length, dy / length);
  }
}

class RPSCustomPainter1 extends CustomPainter {
  final int stepNumber;

  RPSCustomPainter1({required this.stepNumber});

  @override
  void paint(Canvas canvas, Size size) {
    Path path_0 = Path();
    path_0.moveTo(size.width * 0.7180468, size.height * 0.02709575);
    path_0.lineTo(size.width * 0.7585899, size.height * 0.04322462);
    path_0.lineTo(size.width * 0.5463654, size.height * 0.1534690);
    path_0.lineTo(size.width * 0.5066828, size.height * 0.1674390);
    path_0.lineTo(size.width * 0.4452413, size.height * 0.1719870);
    path_0.lineTo(size.width * 0.3967298, size.height * 0.1617205);
    path_0.lineTo(size.width * 0.2607467, size.height * 0.09137139);
    path_0.lineTo(size.width * 0.1801492, size.height * 0.04966274);
    path_0.lineTo(size.width * 0.1535241, size.height * 0.03829289);
    path_0.lineTo(size.width * 0.1110580, size.height * 0.03719908);
    path_0.lineTo(size.width * 0.07781390, size.height * 0.04915422);
    path_0.lineTo(size.width * 0.06558509, size.height * 0.06854533);
    path_0.lineTo(size.width * 0.08100125, size.height * 0.09115071);
    path_0.lineTo(size.width * 0.3775313, size.height * 0.2422786);
    path_0.lineTo(size.width * 0.5260088, size.height * 0.3199390);
    path_0.lineTo(size.width * 0.5832749, size.height * 0.3077632);
    path_0.lineTo(size.width * 0.6205457, size.height * 0.2884680);
    path_0.lineTo(size.width * 0.7098659, size.height * 0.2422786);
    path_0.lineTo(size.width * 0.7729755, size.height * 0.2284525);
    path_0.lineTo(size.width * 0.8304328, size.height * 0.2393905);
    path_0.lineTo(size.width * 0.8658337, size.height * 0.2554810);
    path_0.lineTo(size.width * 0.9350524, size.height * 0.2889861);
    path_0.lineTo(size.width * 0.9755636, size.height * 0.3107759);
    path_0.lineTo(size.width * 0.9982788, size.height * 0.3450582);
    path_0.lineTo(size.width * 0.9873037, size.height * 0.3732573);
    path_0.lineTo(size.width * 0.9598287, size.height * 0.3941836);
    path_0.lineTo(size.width * 0.9035826, size.height * 0.4244648);
    path_0.lineTo(size.width * 0.7605979, size.height * 0.4929238);
    path_0.lineTo(size.width * 0.6050551, size.height * 0.5751610);
    path_0.lineTo(size.width * 0.5392469, size.height * 0.6110743);
    path_0.lineTo(size.width * 0.4607637, size.height * 0.6302256);
    path_0.lineTo(size.width * 0.3966235, size.height * 0.6215039);
    path_0.lineTo(size.width * 0.3524150, size.height * 0.6004145);
    path_0.lineTo(size.width * 0.2721787, size.height * 0.5561536);
    path_0.lineTo(size.width * 0.2467648, size.height * 0.5403798);
    path_0.lineTo(size.width * 0.2365122, size.height * 0.5156923);
    path_0.lineTo(size.width * 0.2399545, size.height * 0.4979611);
    path_0.lineTo(size.width * 0.2555832, size.height * 0.4740029);
    path_0.lineTo(size.width * 0.2765772, size.height * 0.4647535);
    path_0.lineTo(size.width * 0.2883704, size.height * 0.4458325);
    path_0.lineTo(size.width * 0.2851406, size.height * 0.4353166);
    path_0.lineTo(size.width * 0.2691613, size.height * 0.4266237);
    path_0.lineTo(size.width * 0.1527698, size.height * 0.3681721);
    path_0.lineTo(size.width * 0.1179639, size.height * 0.3632883);
    path_0.lineTo(size.width * 0.09614118, size.height * 0.3655431);
    path_0.lineTo(size.width * 0.07278851, size.height * 0.3788031);
    path_0.lineTo(size.width * 0.06388517, size.height * 0.3943659);
    path_0.lineTo(size.width * 0.07820701, size.height * 0.4161270);
    path_0.lineTo(size.width * 0.1041414, size.height * 0.4326012);
    path_0.lineTo(size.width * 0.1138416, size.height * 0.4567322);
    path_0.lineTo(size.width * 0.1047683, size.height * 0.4849218);
    path_0.lineTo(size.width * 0.08567604, size.height * 0.4976637);
    path_0.lineTo(size.width * 0.06644568, size.height * 0.5117584);
    path_0.lineTo(size.width * 0.06388517, size.height * 0.5238287);
    path_0.lineTo(size.width * 0.07097172, size.height * 0.5405237);
    path_0.lineTo(size.width * 0.3899620, size.height * 0.7089222);
    path_0.lineTo(size.width * 0.5029536, size.height * 0.7667981);
    path_0.lineTo(size.width * 0.5419349, size.height * 0.7749921);
    path_0.lineTo(size.width * 0.5645120, size.height * 0.7719985);
    path_0.lineTo(size.width * 0.6117592, size.height * 0.7522140);
    path_0.lineTo(size.width * 0.6923780, size.height * 0.7080011);
    path_0.lineTo(size.width * 0.7431206, size.height * 0.6833137);
    path_0.lineTo(size.width * 0.8080151, size.height * 0.6485037);
    path_0.lineTo(size.width * 0.8541361, size.height * 0.6376711);
    path_0.lineTo(size.width * 0.8930962, size.height * 0.6366445);
    path_0.lineTo(size.width * 0.9610293, size.height * 0.6549898);
    path_0.lineTo(size.width * 0.9933278, size.height * 0.6862209);
    path_0.lineTo(size.width * 0.9846051, size.height * 0.7166077);
    path_0.lineTo(size.width * 0.9506279, size.height * 0.7450563);
    path_0.lineTo(size.width * 0.8039672, size.height * 0.8161442);
    path_0.lineTo(size.width * 0.7521408, size.height * 0.8452837);
    path_0.lineTo(size.width * 0.7301375, size.height * 0.8622089);
    path_0.lineTo(size.width * 0.7290750, size.height * 0.8811587);
    path_0.lineTo(size.width * 0.7547226, size.height * 0.8975274);
    path_0.lineTo(size.width * 0.7695332, size.height * 0.9127160);
    path_0.lineTo(size.width * 0.7793927, size.height * 0.9243449);
    path_0.lineTo(size.width * 0.7789465, size.height * 0.9451848);
    path_0.lineTo(size.width * 0.7551476, size.height * 0.9706687);
    path_0.lineTo(size.width * 0.7082404, size.height * 0.9895033);
    path_0.lineTo(size.width * 0.6569877, size.height * 0.9948572);
    path_0.lineTo(size.width * 0.6282378, size.height * 0.9920267);
    path_0.lineTo(size.width * 0.5725016, size.height * 0.9744298);
    path_0.lineTo(size.width * 0.5310873, size.height * 0.9512775);
    path_0.lineTo(size.width * 0.4816515, size.height * 0.9247095);
    path_0.lineTo(size.width * 0.4392491, size.height * 0.9106531);
    path_0.lineTo(size.width * 0.3966235, size.height * 0.9176190);
    path_0.lineTo(size.width * 0.3565054, size.height * 0.9344099);
    path_0.lineTo(size.width * 0.3246850, size.height * 0.9391881);
    path_0.lineTo(size.width * 0.2660696, size.height * 0.9344099);
    path_0.lineTo(size.width * 0.2456280, size.height * 0.9260816);
    path_0.lineTo(size.width * 0.1933873, size.height * 0.9024783);
    path_0.lineTo(size.width * 0.1445146, size.height * 0.8758240);
    path_0.lineTo(size.width * 0.1218737, size.height * 0.8622089);
    path_0.lineTo(size.width * 0.1673254, size.height * 0.8428274);
    path_0.lineTo(size.width * 0.2722318, size.height * 0.9009815);
    path_0.lineTo(size.width * 0.3126793, size.height * 0.9096649);
    path_0.lineTo(size.width * 0.3505557, size.height * 0.9011063);
    path_0.lineTo(size.width * 0.3743227, size.height * 0.8880286);
    path_0.lineTo(size.width * 0.4257134, size.height * 0.8826651);
    path_0.lineTo(size.width * 0.4815771, size.height * 0.8886042);
    path_0.lineTo(size.width * 0.5185823, size.height * 0.9014133);
    path_0.lineTo(size.width * 0.5595397, size.height * 0.9229153);
    path_0.lineTo(size.width * 0.6098468, size.height * 0.9499439);
    path_0.lineTo(size.width * 0.6647543, size.height * 0.9672241);
    path_0.lineTo(size.width * 0.7005801, size.height * 0.9561229);
    path_0.lineTo(size.width * 0.7179512, size.height * 0.9415292);
    path_0.lineTo(size.width * 0.7179512, size.height * 0.9280293);
    path_0.lineTo(size.width * 0.7068911, size.height * 0.9127160);
    path_0.lineTo(size.width * 0.6867045, size.height * 0.8975274);
    path_0.lineTo(size.width * 0.6740613, size.height * 0.8811587);
    path_0.lineTo(size.width * 0.6740613, size.height * 0.8651929);
    path_0.lineTo(size.width * 0.6790973, size.height * 0.8498892);
    path_0.lineTo(size.width * 0.6906037, size.height * 0.8335492);
    path_0.lineTo(size.width * 0.8937549, size.height * 0.7272099);
    path_0.lineTo(size.width * 0.9333312, size.height * 0.7059382);
    path_0.lineTo(size.width * 0.9358705, size.height * 0.6904426);
    path_0.lineTo(size.width * 0.9233973, size.height * 0.6779502);
    path_0.lineTo(size.width * 0.9100635, size.height * 0.6667722);
    path_0.lineTo(size.width * 0.8955292, size.height * 0.6680003);
    path_0.lineTo(size.width * 0.8606489, size.height * 0.6648341);
    path_0.lineTo(size.width * 0.8164722, size.height * 0.6822486);
    path_0.lineTo(size.width * 0.7452668, size.height * 0.7216737);
    path_0.lineTo(size.width * 0.6578271, size.height * 0.7673066);
    path_0.lineTo(size.width * 0.5956312, size.height * 0.7950644);
    path_0.lineTo(size.width * 0.5511995, size.height * 0.8046592);
    path_0.lineTo(size.width * 0.4933703, size.height * 0.7980100);
    path_0.lineTo(size.width * 0.4545909, size.height * 0.7847692);
    path_0.lineTo(size.width * 0.03892820, size.height * 0.5665064);
    path_0.lineTo(size.width * 0.008340239, size.height * 0.5365131);
    path_0.lineTo(size.width * 0.003527337, size.height * 0.5134759);
    path_0.lineTo(size.width * 0.01826353, size.height * 0.4914078);
    path_0.lineTo(size.width * 0.04395359, size.height * 0.4764591);
    path_0.lineTo(size.width * 0.05453560, size.height * 0.4598985);
    path_0.lineTo(size.width * 0.05588492, size.height * 0.4498047);
    path_0.lineTo(size.width * 0.03163979, size.height * 0.4302409);
    path_0.lineTo(size.width * 0.01340813, size.height * 0.4089884);
    path_0.lineTo(size.width * 0.008340239, size.height * 0.3902881);
    path_0.lineTo(size.width * 0.02125964, size.height * 0.3624440);
    path_0.lineTo(size.width * 0.04919147, size.height * 0.3438013);
    path_0.lineTo(size.width * 0.08431610, size.height * 0.3354250);
    path_0.lineTo(size.width * 0.1433352, size.height * 0.3320572);
    path_0.lineTo(size.width * 0.1968190, size.height * 0.3464974);
    path_0.lineTo(size.width * 0.2771828, size.height * 0.3903361);
    path_0.lineTo(size.width * 0.3188096, size.height * 0.4118381);
    path_0.lineTo(size.width * 0.3344808, size.height * 0.4280629);
    path_0.lineTo(size.width * 0.3424385, size.height * 0.4426662);
    path_0.lineTo(size.width * 0.3355751, size.height * 0.4748568);
    path_0.lineTo(size.width * 0.3045834, size.height * 0.4914078);
    path_0.lineTo(size.width * 0.2930027, size.height * 0.5031039);
    path_0.lineTo(size.width * 0.2930027, size.height * 0.5199236);
    path_0.lineTo(size.width * 0.3055609, size.height * 0.5349395);
    path_0.lineTo(size.width * 0.3848516, size.height * 0.5768400);
    path_0.lineTo(size.width * 0.4280402, size.height * 0.5960393);
    path_0.lineTo(size.width * 0.4721532, size.height * 0.6004145);
    path_0.lineTo(size.width * 0.4980876, size.height * 0.5913186);
    path_0.lineTo(size.width * 0.9008627, size.height * 0.3799353);
    path_0.lineTo(size.width * 0.9333100, size.height * 0.3623480);
    path_0.lineTo(size.width * 0.9405559, size.height * 0.3441083);
    path_0.lineTo(size.width * 0.9300695, size.height * 0.3320572);
    path_0.lineTo(size.width * 0.8011411, size.height * 0.2642315);
    path_0.lineTo(size.width * 0.7637322, size.height * 0.2617081);
    path_0.lineTo(size.width * 0.7242409, size.height * 0.2749969);
    path_0.lineTo(size.width * 0.6447908, size.height * 0.3163985);
    path_0.lineTo(size.width * 0.6063088, size.height * 0.3347917);
    path_0.lineTo(size.width * 0.5646183, size.height * 0.3454132);
    path_0.lineTo(size.width * 0.5051741, size.height * 0.3411435);
    path_0.lineTo(size.width * 0.4420539, size.height * 0.3163985);
    path_0.lineTo(size.width * 0.04462294, size.height * 0.1069150);
    path_0.lineTo(size.width * 0.02193961, size.height * 0.08686183);
    path_0.lineTo(size.width * 0.01177196, size.height * 0.06529269);
    path_0.lineTo(size.width * 0.02783621, size.height * 0.03075137);
    path_0.lineTo(size.width * 0.06909118, size.height * 0.01136985);
    path_0.lineTo(size.width * 0.1378318, size.height * 0.003444537);
    path_0.lineTo(size.width * 0.1783961, size.height * 0.01136985);
    path_0.lineTo(size.width * 0.3515544, size.height * 0.09681164);
    path_0.lineTo(size.width * 0.4009477, size.height * 0.1242528);
    path_0.lineTo(size.width * 0.4404709, size.height * 0.1401994);
    path_0.lineTo(size.width * 0.4821083, size.height * 0.1407367);
    path_0.lineTo(size.width * 0.5315229, size.height * 0.1189277);
    path_0.lineTo(size.width * 0.6837296, size.height * 0.03577905);
    path_0.lineTo(size.width * 0.7055524, size.height * 0.02407338);
    path_0.lineTo(size.width * 0.7180468, size.height * 0.02709575);
    path_0.close();

    Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
    paint_0_fill.color = Color(0xffffc629).withOpacity(1.0);
    canvas.drawPath(path_0, paint_0_fill);
    // Set your colors and spacing here
    final Color defaultColor = Colors.blue;
    final Color stepCompletedColor = Colors.blue.shade100;
    final double spacing = 20.0; // Adjust spacing based on your requirement

    // Use the drawOnPath method
    canvas.drawOnPath(
      path_0,
      spacing: spacing,
      steps: stepNumber,
      defaultColor: defaultColor,
      stepCompletedColor: stepCompletedColor,
    );
  }

  void drawCircle(Canvas canvas, Offset position, Paint paint) {
    canvas.drawCircle(position, 10.0, paint); // Circle radius of 10
  }

  // Simplified heart drawing function, you'll want to replace this with your actual heart drawing logic
  void drawHeart(Canvas canvas, Offset position, Paint paint) {
    Path heartPath = Path();
    // This is a placeholder, implement your heart drawing logic here
    heartPath.addOval(Rect.fromCircle(center: position, radius: 10));
    canvas.drawPath(heartPath, paint);
  }

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

my code depends on a steps that is coming from the database as this form :

 "steps": [
                {
                    "id": 1,
                    "scholarship_type_id": 1,
                    "sort": 1,
                    "name": "Start",
                    "name_ar": "البداية",
                    "amount": 0,
                    "created_at": "2023-12-18T15:35:29.000000Z",
                    "updated_at": "2023-12-18T15:45:56.000000Z",
                    "deleted_at": null
                },
                {
                    "id": 2,
                    "scholarship_type_id": 1,
                    "sort": 2,
                    "name": "Seat",
                    "name_ar": "مقعد الدراسة",
                    "amount": 100,
                    "created_at": "2023-12-18T15:35:56.000000Z",
                    "updated_at": "2023-12-18T15:45:56.000000Z",
                    "deleted_at": null
                }
            ]

what iam looking to is that :
every step has its own heart shape and i want this heart to be in another color if he didnot reach the step yet , how should i do this ?

and also a simple question if i have too many paths how to handle it , and can i use a normal svg and place my widgets dynamically on it ?

2

Answers


  1. Let’s call the circles as the steps and the heart-shaped positions as the milestone.

    To find out what the problem is, and if you are doing some parts on purpose or not, let’s walk through the code.

    The first problem is the loop. To view what is the problem, we can remove the position normalizing and use tangent.position instead. The result is:

    Without normalizing the position

    I think the solution is obvious, change the while loop:

    double maxDistance = metric.length / 2;
    while (distance < maxDistance){
      // Your codes here
    }
    

    And the result is:

    One way Path

    Now we have fixed the distance, lets put back the normalizing and see how it works:

    enter image description here

    The next, in the while loop, you are normalizing the position, I think there are some minor problem in here: The road width, and overlapping steps (caused by positions and vectors).

    • Do you know the road width? Are the width all the same in the road?
    • How to normalize the path on the curves?

    I mentioned that because I guess that causes a problem:

    • Some steps are near the road edges instead of being exactly in the middle.
    • Some steps are on the same spot as the previous one (like the first few steps).

    But how to fix it? Can I cheat?

    Instead of creating the full path, can I create one side of the road? Or is the list should be on this exact positions?

    Let us cheat a little bit. Remove the closing part of the path, so in your road path list, comment out the lines where creating the right edge:

    /*
    path_0.lineTo(size.width * 0.1673254, size.height * 0.8428274);
    path_0.lineTo(size.width * 0.2722318, size.height * 0.9009815);
    .
    .
    .
    path_0.lineTo(size.width * 0.7180468, size.height * 0.02709575);
    path_0.close();
    */
    

    Now, instead of using this path as the edge of the road, lets use it as the middle of the road. This part of the code should look like this:

    // Paint paint0Fill = Paint()..style = PaintingStyle.fill; //Removed this line
    Paint paint0Fill = Paint(); // added this line
    paint0Fill.strokeWidth = 20; // and added this line
    paint0Fill.color = const Color(0xffffc629).withOpacity(1.0);
    canvas.drawPath(path_0, paint0Fill);
    

    Since you removed the other side of the road in path, now you can use the full distance:

    double maxDistance = metric.length;
    while (distance < maxDistance){
      // Your codes here
    }
    

    Using strokeWidth

    It’s better, I think. But I don’t like the first step. I prefer to remove it (but it is your choice).

    Removed the first step

    I removed the first moveTo and started the moveTo from the second element.

    // path_0.moveTo(size.width * 0.7180468, size.height * 0.02709575);
    // path_0.lineTo(size.width * 0.7585899, size.height * 0.04322462);
    path_0.moveTo(size.width * 0.7585899, size.height * 0.04322462);
    

    Let’s go back to the while loop. Your code uses:

    paint.color = shapeCounter < steps * 5 ? defaultColor : stepCompletedColor;
    

    But why are you multiplying the steps? I think it is more calculatable if you don’t do that and use it like:

    paint.color = shapeCounter < steps ? defaultColor : stepCompletedColor;
    

    enter image description here

    You have to take more steps and of course it is your choice.

    So far, the drawOnPath looks like this:

    void drawOnPath(
      Path path, {
      required double spacing,
      required int steps,
      required Color defaultColor,
      required Color stepCompletedColor,
      double offsetFromPath =
          10.0,
    }) {
      final paint = Paint()..style = PaintingStyle.fill;
      final pathMetrics = path.computeMetrics();
      double distance = 0.0;
      int shapeCounter = 0; 
      for (final metric in pathMetrics) {
        double maxDistance = metric.length;
        while (distance < maxDistance) {
          final tangent = metric.getTangentForOffset(distance);
          if (tangent == null) continue;
          paint.color = shapeCounter < steps ? defaultColor : stepCompletedColor;
          drawCircle(this, tangent.position, paint, radius: 5);
          distance += spacing;
          shapeCounter++;
        }
        distance = 0.0;
      }
    }
    

    And the paint is:

      @override
      void paint(Canvas canvas, Size size) {
        Path path_0 = Path();
        path_0.moveTo(size.width * 0.7585899, size.height * 0.04322462);
        path_0.lineTo(size.width * 0.5463654, size.height * 0.1534690);
        path_0.lineTo(size.width * 0.5066828, size.height * 0.1674390);
        path_0.lineTo(size.width * 0.4452413, size.height * 0.1719870);
        path_0.lineTo(size.width * 0.3967298, size.height * 0.1617205);
        path_0.lineTo(size.width * 0.2607467, size.height * 0.09137139);
        path_0.lineTo(size.width * 0.1801492, size.height * 0.04966274);
        path_0.lineTo(size.width * 0.1535241, size.height * 0.03829289);
        path_0.lineTo(size.width * 0.1110580, size.height * 0.03719908);
        path_0.lineTo(size.width * 0.07781390, size.height * 0.04915422);
        path_0.lineTo(size.width * 0.06558509, size.height * 0.06854533);
        path_0.lineTo(size.width * 0.08100125, size.height * 0.09115071);
        path_0.lineTo(size.width * 0.3775313, size.height * 0.2422786);
        path_0.lineTo(size.width * 0.5260088, size.height * 0.3199390);
        path_0.lineTo(size.width * 0.5832749, size.height * 0.3077632);
        path_0.lineTo(size.width * 0.6205457, size.height * 0.2884680);
        path_0.lineTo(size.width * 0.7098659, size.height * 0.2422786);
        path_0.lineTo(size.width * 0.7729755, size.height * 0.2284525);
        path_0.lineTo(size.width * 0.8304328, size.height * 0.2393905);
        path_0.lineTo(size.width * 0.8658337, size.height * 0.2554810);
        path_0.lineTo(size.width * 0.9350524, size.height * 0.2889861);
        path_0.lineTo(size.width * 0.9755636, size.height * 0.3107759);
        path_0.lineTo(size.width * 0.9982788, size.height * 0.3450582);
        path_0.lineTo(size.width * 0.9873037, size.height * 0.3732573);
        path_0.lineTo(size.width * 0.9598287, size.height * 0.3941836);
        path_0.lineTo(size.width * 0.9035826, size.height * 0.4244648);
        path_0.lineTo(size.width * 0.7605979, size.height * 0.4929238);
        path_0.lineTo(size.width * 0.6050551, size.height * 0.5751610);
        path_0.lineTo(size.width * 0.5392469, size.height * 0.6110743);
        path_0.lineTo(size.width * 0.4607637, size.height * 0.6302256);
        path_0.lineTo(size.width * 0.3966235, size.height * 0.6215039);
        path_0.lineTo(size.width * 0.3524150, size.height * 0.6004145);
        path_0.lineTo(size.width * 0.2721787, size.height * 0.5561536);
        path_0.lineTo(size.width * 0.2467648, size.height * 0.5403798);
        path_0.lineTo(size.width * 0.2365122, size.height * 0.5156923);
        path_0.lineTo(size.width * 0.2399545, size.height * 0.4979611);
        path_0.lineTo(size.width * 0.2555832, size.height * 0.4740029);
        path_0.lineTo(size.width * 0.2765772, size.height * 0.4647535);
        path_0.lineTo(size.width * 0.2883704, size.height * 0.4458325);
        path_0.lineTo(size.width * 0.2851406, size.height * 0.4353166);
        path_0.lineTo(size.width * 0.2691613, size.height * 0.4266237);
        path_0.lineTo(size.width * 0.1527698, size.height * 0.3681721);
        path_0.lineTo(size.width * 0.1179639, size.height * 0.3632883);
        path_0.lineTo(size.width * 0.09614118, size.height * 0.3655431);
        path_0.lineTo(size.width * 0.07278851, size.height * 0.3788031);
        path_0.lineTo(size.width * 0.06388517, size.height * 0.3943659);
        path_0.lineTo(size.width * 0.07820701, size.height * 0.4161270);
        path_0.lineTo(size.width * 0.1041414, size.height * 0.4326012);
        path_0.lineTo(size.width * 0.1138416, size.height * 0.4567322);
        path_0.lineTo(size.width * 0.1047683, size.height * 0.4849218);
        path_0.lineTo(size.width * 0.08567604, size.height * 0.4976637);
        path_0.lineTo(size.width * 0.06644568, size.height * 0.5117584);
        path_0.lineTo(size.width * 0.06388517, size.height * 0.5238287);
        path_0.lineTo(size.width * 0.07097172, size.height * 0.5405237);
        path_0.lineTo(size.width * 0.3899620, size.height * 0.7089222);
        path_0.lineTo(size.width * 0.5029536, size.height * 0.7667981);
        path_0.lineTo(size.width * 0.5419349, size.height * 0.7749921);
        path_0.lineTo(size.width * 0.5645120, size.height * 0.7719985);
        path_0.lineTo(size.width * 0.6117592, size.height * 0.7522140);
        path_0.lineTo(size.width * 0.6923780, size.height * 0.7080011);
        path_0.lineTo(size.width * 0.7431206, size.height * 0.6833137);
        path_0.lineTo(size.width * 0.8080151, size.height * 0.6485037);
        path_0.lineTo(size.width * 0.8541361, size.height * 0.6376711);
        path_0.lineTo(size.width * 0.8930962, size.height * 0.6366445);
        path_0.lineTo(size.width * 0.9610293, size.height * 0.6549898);
        path_0.lineTo(size.width * 0.9933278, size.height * 0.6862209);
        path_0.lineTo(size.width * 0.9846051, size.height * 0.7166077);
        path_0.lineTo(size.width * 0.9506279, size.height * 0.7450563);
        path_0.lineTo(size.width * 0.8039672, size.height * 0.8161442);
        path_0.lineTo(size.width * 0.7521408, size.height * 0.8452837);
        path_0.lineTo(size.width * 0.7301375, size.height * 0.8622089);
        path_0.lineTo(size.width * 0.7290750, size.height * 0.8811587);
        path_0.lineTo(size.width * 0.7547226, size.height * 0.8975274);
        path_0.lineTo(size.width * 0.7695332, size.height * 0.9127160);
        path_0.lineTo(size.width * 0.7793927, size.height * 0.9243449);
        path_0.lineTo(size.width * 0.7789465, size.height * 0.9451848);
        path_0.lineTo(size.width * 0.7551476, size.height * 0.9706687);
        path_0.lineTo(size.width * 0.7082404, size.height * 0.9895033);
        path_0.lineTo(size.width * 0.6569877, size.height * 0.9948572);
        path_0.lineTo(size.width * 0.6282378, size.height * 0.9920267);
        path_0.lineTo(size.width * 0.5725016, size.height * 0.9744298);
        path_0.lineTo(size.width * 0.5310873, size.height * 0.9512775);
        path_0.lineTo(size.width * 0.4816515, size.height * 0.9247095);
        path_0.lineTo(size.width * 0.4392491, size.height * 0.9106531);
        path_0.lineTo(size.width * 0.3966235, size.height * 0.9176190);
        path_0.lineTo(size.width * 0.3565054, size.height * 0.9344099);
        path_0.lineTo(size.width * 0.3246850, size.height * 0.9391881);
        path_0.lineTo(size.width * 0.2660696, size.height * 0.9344099);
        path_0.lineTo(size.width * 0.2456280, size.height * 0.9260816);
        path_0.lineTo(size.width * 0.1933873, size.height * 0.9024783);
        path_0.lineTo(size.width * 0.1445146, size.height * 0.8758240);
        path_0.lineTo(size.width * 0.1218737, size.height * 0.8622089);
    
        Paint paint0Fill = Paint();
        paint0Fill.strokeWidth = 20;
        paint0Fill.color = const Color(0xffffc629).withOpacity(1.0);
        canvas.drawPath(path_0, paint0Fill);
    
        const Color defaultColor = Colors.blue;
        final Color stepCompletedColor = Colors.blue.shade100;
        const double spacing = 20.0;
    
        canvas.drawOnPath(
          path_0,
          spacing: spacing,
          steps: stepNumber,
          defaultColor: defaultColor,
          stepCompletedColor: stepCompletedColor,
        );
      }
    

    Last part is drawing milestones. You have too many options, but the problem is you didn’t mentioned where you want to put those!

    Suppose you want to add milestones every 10 steps. Now, you can add a condition to check if it is the place you want to put the heart shape or not.

    Define a list List milestones = []; before your for loop and then, in the while loop add:

    if (shapeCounter % 10 == 0) {
      milestones.add({
        'position': tangent.position,
        'color': paint.color,
        'paint': paint
      });
    }
    

    After the while loop finished:

    for (var milestone in milestones) {
      drawHeart(this, milestone['position'], milestone['color'], milestone['paint']);
    }
    

    Drawing the heart-shape is like:

    void drawHeart(Canvas canvas, Offset position, Color color, Paint paint) {
      Paint paint = Paint();
      paint.color = color;
      double width = 30;
      double height = 30;
      position -= Offset(0.7 * width, 0.7 * height);
    
      Path path = Path();
      path.moveTo(position.dx + 0.5 * width, position.dy + height * 0.35);
      path.cubicTo(
          position.dx + 0.2 * width,
          position.dy + height * 0.1,
          position.dx + -0.25 * width,
          position.dy + height * 0.6,
          position.dx + 0.5 * width,
          position.dy + height);
      path.moveTo(position.dx + 0.5 * width, position.dy + height * 0.35);
      path.cubicTo(
          position.dx + 0.8 * width,
          position.dy + height * 0.1,
          position.dx + 1.25 * width,
          position.dy + height * 0.6,
          position.dx + 0.5 * width,
          position.dy + height);
    
      canvas.drawPath(path, paint);
    }
    

    The final code looks like this:

    void drawOnPath(
      Path path, {
      required double spacing,
      required int steps,
      required Color defaultColor,
      required Color stepCompletedColor,
      double offsetFromPath = 10.0,
    }) {
      final paint = Paint()..style = PaintingStyle.fill;
      final pathMetrics = path.computeMetrics();
      double distance = 0.0;
      int shapeCounter = 0;
    
      List milestones = [];
      for (final metric in pathMetrics) {
        double maxDistance = metric.length;
        while (distance < maxDistance) {
          final tangent = metric.getTangentForOffset(distance);
          if (tangent == null) continue;
          paint.color = shapeCounter < steps ? defaultColor : stepCompletedColor;
          drawCircle(this, tangent.position, paint, radius: 5);
          distance += spacing;
          shapeCounter++;
          if (shapeCounter % 10 == 0) {
            milestones.add({
              'position': tangent.position,
              'color': paint.color,
              'paint': paint
            });
          }
        }
        distance = 0.0;
        for (var milestone in milestones) {
          drawHeart(
              this, milestone['position'], milestone['color'], milestone['paint']);
        }
      }
    }
    

    And the result is:

    The final result.

    The last point doesn’t match the (shapeCounter % 10 == 0) condition but if you want, you can add it by adding the last position to the list. 🙂

    The final part, if you want to have different steps number in each milestone, you must change the condition where you put the heart-shapes.

    Suppose you have a list like List<int> stepsInMilestones = [5, 12, 4, 14, 9, 4, 5, 12, 7, 14, 9, 4]; which defines the counts of steps in milestones.

    First change the class:

    class RPSCustomPainter1 extends CustomPainter {
      final int stepNumber;
      final List<int> stepsInMilestones; // Added this and update the class
    
      RPSCustomPainter1(
          {required this.stepNumber, required this.stepsInMilestones});
    
      // Your old codes here
    
        canvas.drawOnPath(path_0,
            spacing: spacing,
            steps: stepNumber,
            defaultColor: defaultColor,
            stepCompletedColor: stepCompletedColor,
            stepsInMilestones: stepsInMilestones, // Added this line
        );
    }
    

    Pass this to RPSCustomPainter1 like:

    RPSCustomPainter1(stepNumber: 30, stepsInMilestones: stepsInMilestones)
    

    And update the drawOnPath:

     void drawOnPath(Path path,
          {required double spacing,
          required int steps,
          required Color defaultColor,
          required Color stepCompletedColor,
          double offsetFromPath = 10.0,
          required List<int> stepsInMilestones, // Added this line
    }) {
     // Your code here
    }
    

    Now, You must change the condition, and check if it reached your desired step for that particular milestone instead of checking the 10th steps for every milestones.

    First, add these two variables before the for loop:

    int milestoneCounter = 0;
    int totalSteps = stepsInMilestones[milestoneCounter];
    

    Remove the previous condition from the loop:

    // if (shapeCounter % 10 == 0) {
    //   milestones.add({
    //     'position': tangent.position,
    //     'color': paint.color,
    //     'paint': paint
    //   });
    // }
    

    And add the new one:

    if (shapeCounter == totalSteps) {
      // Add this milestone position
      milestones.add({
        'position': tangent.position,
        'color': paint.color,
        'paint': paint
      });
      milestoneCounter += 1; // Move to the next milestone
      if (milestoneCounter >= stepsInMilestones.length - 1) {
        // Added this condition to prevent index out of range error.
        // I added steps randomly and there is a chance that
        // I needed to put more values in stepsInMilestones.
        milestoneCounter = stepsInMilestones.length - 1;
      }
      totalSteps += stepsInMilestones[
          milestoneCounter]; // This is the next milestone step
    }
    

    The final drawOnPath is:

    void drawOnPath(Path path,
        {required double spacing,
        required int steps,
        required Color defaultColor,
        required Color stepCompletedColor,
        double offsetFromPath = 10.0,
        required List<int> stepsInMilestones}) {
      final paint = Paint()..style = PaintingStyle.fill;
      final pathMetrics = path.computeMetrics();
      double distance = 0.0;
      int shapeCounter = 0;
    
      List milestones = [];
      int milestoneCounter = 0;
      int totalSteps = stepsInMilestones[milestoneCounter];
      for (final metric in pathMetrics) {
        double maxDistance = metric.length;
        while (distance < maxDistance) {
          final tangent = metric.getTangentForOffset(distance);
          if (tangent == null) continue;
          paint.color = shapeCounter < steps ? defaultColor : stepCompletedColor;
          drawCircle(this, tangent.position, paint, radius: 5);
          distance += spacing;
          shapeCounter++;
          if (shapeCounter == totalSteps) {
            milestones.add({
              'position': tangent.position,
              'color': paint.color,
              'paint': paint
            });
            milestoneCounter += 1; // Move to the next milestone
            if (milestoneCounter >= stepsInMilestones.length - 1) {
              // Added this condition to prevent index out of range error.
              // I added steps randomly and there is a chance that
              // I needed to put more values in stepsInMilestones.
              milestoneCounter = stepsInMilestones.length - 1;
            }
            totalSteps += stepsInMilestones[
                milestoneCounter]; // This is the next milestone step
          }
        }
        distance = 0.0;
        for (var milestone in milestones) {
          drawHeart(
              this, milestone['position'], milestone['color'], milestone['paint']);
        }
      }
    }
    

    And the result is:

    The road with different steps in milestones

    Login or Signup to reply.
  2. There is somewhat different approach which does not use CustomPainter.

    This is the result
    enter image description here

    Here you break your svg into layers. And with help of xml parser, you draw the road path. Please make relevant adjustment.

    // main.dart
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    import 'package:flutter_svg/svg.dart';
    import 'package:xml/xml.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      runApp(const MainApp());
    }
    
    class MainApp extends StatelessWidget {
      const MainApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          home: LayeredSvg(),
        );
      }
    }
    
    class LayeredSvg extends StatefulWidget {
      const LayeredSvg({super.key});
    
      @override
      State<LayeredSvg> createState() => _LayeredSvgState();
    }
    
    class _LayeredSvgState extends State<LayeredSvg> {
      List<String> dots = [];
      List<String> favorites = [];
    
      List<int> favoritesSteps = [5, 22, 33, 52, 64];
    
      int currentLevel = 0;
    
      Future<List<String>> getElements(String path) async {
        List<String> result = [];
        await rootBundle.loadString(path).then((value) {
          final document = XmlDocument.parse(value);
          final circles = List.of(document.children.first.children);
          for (var element in circles) {
            document.children.first.children.clear();
            if (element.nodeType == XmlNodeType.ELEMENT) {
              document.children.first.children.add(element);
              result.add(document.toXmlString());
            }
          }
        });
        return result;
      }
    
      @override
      void initState() {
        super.initState();
        getElements('assets/svgs/path_dot.svg')
            .then((value) => dots = value)
            .then((value) => setState(() {}));
        getElements('assets/svgs/path_favorite.svg')
            .then((value) => favorites = value)
            .then((value) => setState(() {}));
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Stack(
              children: [
                SvgPicture.asset('assets/svgs/path.svg'),
                for (int i = 0; i < dots.length; i++)
                  SvgPicture.string(
                    dots[i],
                    colorFilter: ColorFilter.mode(
                        i < currentLevel
                            ? Colors.blue.shade700
                            : Colors.yellow.shade200,
                        BlendMode.srcIn),
                  ),
                for (int i = 0; i < favorites.length; i++)
                  SvgPicture.string(
                    favorites[i],
                    colorFilter: ColorFilter.mode(
                        favoritesSteps[i] < currentLevel
                            ? Colors.blue.shade700
                            : Colors.blue.shade200,
                        BlendMode.srcIn),
                  )
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              setState(() {
                currentLevel++;
              });
            },
            child: const Icon(Icons.add),
          ),
        );
      }
    }
    
    
    
    #pubspec.yaml
    name: flutter_temp
    description: "A new Flutter project."
    publish_to: "none"
    version: 0.1.0
    
    environment:
      sdk: ">=3.3.2 <4.0.0"
    
    dependencies:
      flutter:
        sdk: flutter
      flutter_svg: ^2.0.10+1
      xml: ^6.5.0
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      flutter_lints: ^3.0.0
    
    flutter:
      uses-material-design: true
    
      assets:
        - assets/svgs/
    
    

    The assets I used
    path_dot.svg

    <svg width="358" height="723" viewBox="0 0 358 723" fill="none" xmlns="http://www.w3.org/2000/svg">
    <circle cx="156.96" cy="110.654" r="7.5" transform="rotate(-90 156.96 110.654)" fill="#2B83EB"/>
    <circle cx="137.72" cy="94.0565" r="7.5" transform="rotate(-90 137.72 94.0565)" fill="#2B83EB"/>
    <circle cx="118.194" cy="77.796" r="7.5" transform="rotate(-90 118.194 77.796)" fill="#2B83EB"/>
    <circle cx="98.2325" cy="62.0709" r="7.5" transform="rotate(-90 98.2325 62.0709)" fill="#2B83EB"/>
    <circle cx="77.4432" cy="47.3801" r="7.5" transform="rotate(-90 77.4432 47.3801)" fill="#2B83EB"/>
    <circle cx="55.7192" cy="61.0934" r="7.5" transform="rotate(-90 55.7192 61.0934)" fill="#2B83EB"/>
    <circle cx="67.7007" cy="83.4941" r="7.5" transform="rotate(-90 67.7007 83.4941)" fill="#2B83EB"/>
    <circle cx="81.023" cy="105.131" r="7.5" transform="rotate(-90 81.023 105.131)" fill="#2B83EB"/>
    <circle cx="94.9861" cy="126.361" r="7.5" transform="rotate(-90 94.9861 126.361)" fill="#2B83EB"/>
    <circle cx="109.342" cy="147.326" r="7.5" transform="rotate(-90 109.342 147.326)" fill="#2B83EB"/>
    <circle cx="123.971" cy="168.103" r="7.5" transform="rotate(-90 123.971 168.103)" fill="#2B83EB"/>
    <circle cx="138.803" cy="188.736" r="7.5" transform="rotate(-90 138.803 188.736)" fill="#2B83EB"/>
    <circle cx="153.792" cy="209.253" r="7.5" transform="rotate(-90 153.792 209.253)" fill="#2B83EB"/>
    <circle cx="166.5" cy="225.5" r="7.5" transform="rotate(-90 166.5 225.5)" fill="#2B83EB"/>
    <circle cx="180.5" cy="245.5" r="7.5" transform="rotate(-90 180.5 245.5)" fill="#2B83EB"/>
    <circle cx="192.674" cy="260.109" r="7.5" transform="rotate(-90 192.674 260.109)" fill="#2B83EB"/>
    <circle cx="208.306" cy="280.142" r="7.5" transform="rotate(-90 208.306 280.142)" fill="#2B83EB"/>
    <circle cx="223.767" cy="300.307" r="7.5" transform="rotate(-90 223.767 300.307)" fill="#2B83EB"/>
    <circle cx="239.008" cy="320.639" r="7.5" transform="rotate(-90 239.008 320.639)" fill="#2B83EB"/>
    <circle cx="253.952" cy="341.19" r="7.5" transform="rotate(-90 253.952 341.19)" fill="#2B83EB"/>
    <circle cx="268.46" cy="362.051" r="7.5" transform="rotate(-90 268.46 362.051)" fill="#2B83EB"/>
    <circle cx="282.238" cy="383.4" r="7.5" transform="rotate(-90 282.238 383.4)" fill="#2B83EB"/>
    <circle cx="259.171" cy="411.241" r="7.5" transform="rotate(-90 259.171 411.241)" fill="#2B83EB"/>
    <circle cx="238.135" cy="396.989" r="7.5" transform="rotate(-90 238.135 396.989)" fill="#2B83EB"/>
    <circle cx="217.655" cy="381.947" r="7.5" transform="rotate(-90 217.655 381.947)" fill="#2B83EB"/>
    <circle cx="197.52" cy="366.448" r="7.5" transform="rotate(-90 197.52 366.448)" fill="#2B83EB"/>
    <circle cx="180.5" cy="352.5" r="7.5" transform="rotate(-90 180.5 352.5)" fill="#2B83EB"/>
    <circle cx="163.5" cy="339.5" r="7.5" transform="rotate(-90 163.5 339.5)" fill="#2B83EB"/>
    <circle cx="145.259" cy="327.251" r="7.5" transform="rotate(-90 145.259 327.251)" fill="#2B83EB"/>
    <circle cx="124.381" cy="312.768" r="7.5" transform="rotate(-90 124.381 312.768)" fill="#2B83EB"/>
    <circle cx="103.282" cy="298.608" r="7.5" transform="rotate(-90 103.282 298.608)" fill="#2B83EB"/>
    <circle cx="81.8645" cy="284.934" r="7.5" transform="rotate(-90 81.8645 284.934)" fill="#2B83EB"/>
    <circle cx="59.9338" cy="272.104" r="7.5" transform="rotate(-90 59.9338 272.104)" fill="#2B83EB"/>
    <circle cx="40.1586" cy="301.844" r="7.5" transform="rotate(-90 40.1586 301.844)" fill="#2B83EB"/>
    <circle cx="55.3503" cy="322.212" r="7.5" transform="rotate(-90 55.3503 322.212)" fill="#2B83EB"/>
    <circle cx="71.228" cy="342.05" r="7.5" transform="rotate(-90 71.228 342.05)" fill="#2B83EB"/>
    <circle cx="87.5153" cy="361.553" r="7.5" transform="rotate(-90 87.5153 361.553)" fill="#2B83EB"/>
    <circle cx="104.082" cy="380.821" r="7.5" transform="rotate(-90 104.082 380.821)" fill="#2B83EB"/>
    <circle cx="120.853" cy="399.909" r="7.5" transform="rotate(-90 120.853 399.909)" fill="#2B83EB"/>
    <circle cx="137.785" cy="418.857" r="7.5" transform="rotate(-90 137.785 418.857)" fill="#2B83EB"/>
    <circle cx="154.844" cy="437.689" r="7.5" transform="rotate(-90 154.844 437.689)" fill="#2B83EB"/>
    <circle cx="172.009" cy="456.424" r="7.5" transform="rotate(-90 172.009 456.424)" fill="#2B83EB"/>
    <circle cx="185.5" cy="475.5" r="7.5" transform="rotate(-90 185.5 475.5)" fill="#2B83EB"/>
    <circle cx="197.5" cy="490.5" r="7.5" transform="rotate(-90 197.5 490.5)" fill="#2B83EB"/>
    <circle cx="210.342" cy="509.398" r="7.5" transform="rotate(-90 210.342 509.398)" fill="#2B83EB"/>
    <circle cx="224.955" cy="530.186" r="7.5" transform="rotate(-90 224.955 530.186)" fill="#2B83EB"/>
    <circle cx="239.461" cy="551.049" r="7.5" transform="rotate(-90 239.461 551.049)" fill="#2B83EB"/>
    <circle cx="253.834" cy="572.003" r="7.5" transform="rotate(-90 253.834 572.003)" fill="#2B83EB"/>
    <circle cx="268.039" cy="593.071" r="7.5" transform="rotate(-90 268.039 593.071)" fill="#2B83EB"/>
    <circle cx="282.02" cy="614.29" r="7.5" transform="rotate(-90 282.02 614.29)" fill="#2B83EB"/>
    <circle cx="295.678" cy="635.717" r="7.5" transform="rotate(-90 295.678 635.717)" fill="#2B83EB"/>
    <circle cx="308.82" cy="657.464" r="7.5" transform="rotate(-90 308.82 657.464)" fill="#2B83EB"/>
    <circle cx="295.874" cy="686.752" r="7.5" transform="rotate(-90 295.874 686.752)" fill="#2B83EB"/>
    <circle cx="276.609" cy="670.185" r="7.5" transform="rotate(-90 276.609 670.185)" fill="#2B83EB"/>
    <circle cx="257.917" cy="652.974" r="7.5" transform="rotate(-90 257.917 652.974)" fill="#2B83EB"/>
    <circle cx="239.56" cy="635.404" r="7.5" transform="rotate(-90 239.56 635.404)" fill="#2B83EB"/>
    <circle cx="221.43" cy="617.6" r="7.5" transform="rotate(-90 221.43 617.6)" fill="#2B83EB"/>
    <circle cx="203.468" cy="599.627" r="7.5" transform="rotate(-90 203.468 599.627)" fill="#2B83EB"/>
    <circle cx="187.5" cy="585.5" r="7.5" transform="rotate(-90 187.5 585.5)" fill="#2B83EB"/>
    <circle cx="174.431" cy="570.041" r="7.5" transform="rotate(-90 174.431 570.041)" fill="#2B83EB"/>
    <circle cx="161.5" cy="551.5" r="7.5" transform="rotate(-90 161.5 551.5)" fill="#2B83EB"/>
    <circle cx="149.5" cy="536.5" r="7.5" transform="rotate(-90 149.5 536.5)" fill="#2B83EB"/>
    <circle cx="134.5" cy="519.5" r="7.5" transform="rotate(-90 134.5 519.5)" fill="#2B83EB"/>
    <circle cx="117.5" cy="502.5" r="7.5" transform="rotate(-90 117.5 502.5)" fill="#2B83EB"/>
    <circle cx="100.026" cy="519.539" r="7.5" transform="rotate(-90 100.026 519.539)" fill="#2B83EB"/>
    <circle cx="109.437" cy="543.141" r="7.5" transform="rotate(-90 109.437 543.141)" fill="#2B83EB"/>
    <circle cx="119.564" cy="566.446" r="7.5" transform="rotate(-90 119.564 566.446)" fill="#2B83EB"/>
    <circle cx="130.104" cy="589.567" r="7.5" transform="rotate(-90 130.104 589.567)" fill="#2B83EB"/>
    <circle cx="140.923" cy="612.558" r="7.5" transform="rotate(-90 140.923 612.558)" fill="#2B83EB"/>
    <circle cx="151.946" cy="635.453" r="7.5" transform="rotate(-90 151.946 635.453)" fill="#2B83EB"/>
    <circle cx="163.126" cy="659.271" r="7.5" transform="rotate(-90 163.126 659.271)" fill="#2B83EB"/>
    <circle cx="174.431" cy="681.028" r="7.5" transform="rotate(-90 174.431 681.028)" fill="#2B83EB"/>
    </svg>
    

    path_favorite.svg

    <svg width="358" height="723" viewBox="0 0 358 723" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M66.6114 12.8575C63.5521 11.3624 59.9791 11.414 56.8841 12.9135C56.1655 9.55028 54.0109 6.69958 50.9516 5.2045C45.3609 2.47232 38.4838 5.01454 35.6214 10.8717C33.38 15.458 33.3979 22.0386 35.6746 30.4306C37.4267 36.8889 40.0257 42.4963 40.7804 44.061L41.2183 44.9689L42.2036 44.7566C43.902 44.3907 49.9232 42.9958 56.0949 40.4102C64.1151 37.0503 69.318 33.021 71.5594 28.4346C74.4218 22.5775 72.2021 15.5896 66.6114 12.8575Z" fill="#1D42C4"/>
    <path d="M305.905 407.536C302.59 408.316 299.857 410.618 298.411 413.738C295.725 411.589 292.253 410.747 288.938 411.526C282.881 412.951 279.167 419.272 280.659 425.618C281.828 430.588 286.008 435.67 293.083 440.725C298.528 444.615 304.09 447.31 305.664 448.043L306.578 448.469L307.206 447.681C308.29 446.322 312.067 441.431 315.208 435.522C319.289 427.844 320.766 421.431 319.597 416.461C318.105 410.115 311.962 406.112 305.905 407.536Z" fill="#1D42C4"/>
    <path d="M28.7149 240C25.3098 240 22.1223 241.615 20 244.321C17.8777 241.615 14.6902 240 11.2851 240C5.06251 240 0 245.304 0 251.823C0 256.927 2.90541 262.832 8.6356 269.372C13.0455 274.405 17.8425 278.302 19.2076 279.377L19.9997 280L20.7917 279.377C22.157 278.302 26.9543 274.405 31.364 269.372C37.0945 262.832 40 256.928 40 251.823C40 245.304 34.9375 240 28.7149 240Z" fill="#1D42C4"/>
    <path d="M325.604 673.427C322.805 675.366 321.104 678.509 320.901 681.942C317.615 680.926 314.075 681.414 311.276 683.353C306.161 686.897 305.02 694.139 308.733 699.498C311.64 703.694 317.391 706.893 325.826 709.006C332.317 710.631 338.479 711.103 340.213 711.208L341.219 711.27L341.515 710.306C342.026 708.646 343.75 702.71 344.508 696.062C345.494 687.423 344.52 680.914 341.613 676.718C337.9 671.36 330.718 669.883 325.604 673.427Z" fill="#1D42C4"/>
    <path d="M94.7149 458C91.3098 458 88.1223 459.615 86 462.321C83.8777 459.615 80.6902 458 77.2851 458C71.0625 458 66 463.304 66 469.823C66 474.927 68.9054 480.832 74.6356 487.372C79.0455 492.405 83.8425 496.302 85.2076 497.377L85.9997 498L86.7917 497.377C88.157 496.302 92.9543 492.405 97.364 487.372C103.095 480.832 106 474.928 106 469.823C106 463.304 100.937 458 94.7149 458Z" fill="#1D42C4"/>
    </svg>
    
    

    path.svg

    <svg width="358" height="723" viewBox="0 0 358 723" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M172.5 678.726C61.5491 456.751 61.5491 419.756 172.5 567.738C377.167 778.219 377.167 741.223 172.5 456.751C-32.1667 234.777 -32.1667 197.781 172.5 345.764C338.388 479.728 338.388 442.732 172.5 234.777C6.61228 12.8026 6.61228 -24.1931 172.5 123.79" stroke="#E8E869" stroke-width="27" stroke-linecap="round"/>
    </svg>
    
    

    I had to create a sample design in Figma as your svg was not available.
    Inside the favoritesSteps array, you need to provide the steps between each favorites.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search