Reputation: 622
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 :
and here is what i want :
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 ?
Upvotes: 4
Views: 507
Reputation: 1220
Let's call the circles as the steps
and the heart-shaped positions as the milestones
.
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:
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:
Now we have fixed the distance
, lets put back the normalizing and see how it works:
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).
I mentioned that because I guess that causes a problem:
steps
are near the road edges instead of being exactly in the middle.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
}
It's better, I think. But I don't like the first step. I prefer to remove it (but it is your choice).
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;
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']);
}
Because when you draw next element, it shows on top previous elements, and it leads to having parts of the circles on top of the hearts. We don't want that, right? So, a quick solution is drawing all the circles and after that, drawing all the hearts.
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 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:
Upvotes: 7
Reputation: 4341
There is somewhat different approach which does not use CustomPainter.
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.
Upvotes: 0