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 colorstrokeWidth 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.