In my most recent project, I was asked to create scrollable path between GridView children widgets. The Flutter CustomPaint Widget allows us to make a highly personalized user interface with interesting animations and this widget also offers you access to low-level graphics. So, in this article, I’ll walk you through the steps I took to achieve this.

Create a class LevelsScreen extends StatefulWidget .In the Build method return the scaffold and add a GridView.builder() inside the body. Then create a List which we use to create Gridview using GridView.builder()
List<String> levels = [
'Start',
'Level-1',
'Level-2',
'Level-3',
'Level-4',
'Level-5'
];
Give the itemCount as the length of the list we just created. itemBuilder will return the circular containers on which we display the text from the list.
GridView.builder(
itemCount: levels.length,
reverse: true,
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1, childAspectRatio: 2),
itemBuilder: (BuildContext context, int index) {
return Row(
mainAxisAlignment: (index % 2 == 0)
? MainAxisAlignment.start
: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Container(
height: 125,
width: 125,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(62.5),
),
child: Center(
child: Container(
height: 110,
width: 110,
decoration: BoxDecoration(
color: (index == 0)
? Colors.green
: Colors.amberAccent,
borderRadius: BorderRadius.circular(55),
),
child: Center(
child: Text(levels[index],
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600)
),
),
),
),
),
),
],
);
},
),
Step 2: Create a class “DashLinePainter” that extends “CustomPainter”
Then we create a new class DashLinePainter that extends CustomPainter ,in which we define the Paint with the required color, strokeWidth and style .Later inside the paint method define the Path.
class DashLinePainter extends CustomPainter {
final Paint _paint = Paint()
..color = Colors.black
..strokeWidth = 4.0
..style = PaintingStyle.stroke
..strokeJoin = StrokeJoin.round;
@override
void paint(Canvas canvas, Size size) {
var path = Path()
..moveTo(size.width / 4.5, size.height / 1.09)
..lineTo(size.width / 4.5, size.height / 1.28)
..arcToPoint(Offset(size.width / 3.2, size.height / 1.33),
radius: const Radius.circular(50), clockwise: true)
..lineTo(size.width / 1.3, size.height / 1.33)
..lineTo(size.width / 1.3, size.height / 1.62)
..arcToPoint(Offset(size.width / 1.5, size.height / 1.72),
radius: const Radius.circular(50), clockwise: false)
..lineTo(size.width / 4.5, size.height / 1.72)
..lineTo(size.width / 4.5, size.height / 2.2)
..arcToPoint(Offset(size.width / 3, size.height / 2.4),
radius: const Radius.circular(50), clockwise: true)
..lineTo(size.width / 1.3, size.height / 2.4)
..lineTo(size.width / 1.3, size.height / 3.5)
..arcToPoint(Offset(size.width / 1.45, size.height / 4),
radius: const Radius.circular(50), clockwise: false)
..lineTo(size.width / 4.5, size.height / 4)
..lineTo(size.width / 4.5, size.height / 8.5)
..arcToPoint(Offset(size.width / 3.2, size.height / 11),
radius: const Radius.circular(50), clockwise: true)
..lineTo(size.width / 1.3, size.height / 11);
Path dashPath = Path();
double dashWidth = 10.0;
double dashSpace = 5.0;
double distance = 0.0;
for (PathMetric pathMetric in path.computeMetrics()) {
while (distance < pathMetric.length) {
dashPath.addPath(
pathMetric.extractPath(distance, distance + dashWidth),
Offset.zero,
);
distance += dashWidth;
distance += dashSpace;
}
}
canvas.drawPath(dashPath, _paint);
}
@override
bool shouldRepaint(DashLinePainter oldDelegate) => true;
}
Step 3: Wrap “GridView.builder()” with “CustomPaint”
To show the path between gridView children, we wrap the GridView.builder with CustomPaint and pass the painter as DashLinePainter.
CustomPaint(
painter: DashLinePainter(),
child: GridView.builder(<Code>),
)
Step 4: Make the path scrollable
Now to make the path scrollable along with the gridView children we need to wrap the customPaint with SingleChildScrollView and give any scroll physics .
SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: CustomPaint(
painter: DashLinePainter(),
child: GridView.builder(<code>),
),
),
Output
You can get the complete code on GitHub, and the GIF below demonstrates how scrollable path using CustomPaint works.

Conclusion:
I’ve discussed how to create scrollable path using CustomPaint in this article, and you can adapt this code to match your requirements. This was just a quick introduction to drawing scrollable paths using CustomPaint from my perspective, and how it works with Flutter. I hope that this blog has given you enough information to try out scrollable paths in Flutter in your own projects.