How to implement the CustomPaint widget in Flutter

In this guide, we will discuss how to implement CustomPaint in Flutter. For this, we should know some basic information about what a Custom Painter in flutter is. A widget that provides a canvas on which to draw during the painting phase. To paint in Flutter we can use the CustomPaint widget which basically takes the size of its parent if not given to the child. The CustomPainter subclass overrides two methods: paint() and shouldRepaint(). Custom Paint basically ensures the UI designing of those components that cannot be derived from the regular shapes provided by Flutter. This widget shows the customizability of flutter from its peers. CustomPaint first asks its painter to paint on the current canvas, then it paints its child, and then, after painting its child, it asks its foreground painter to paint. The Percentage increase is shown by the onPressed method at the Raised Button. Let’s understand what foregroundPainter implies. This also illustrates that choosing a painter instead of the foregroundPainter will do the exact opposite.

Some of the important properties of the CustomPaint Widget are:

  • painter: The painter that paints before the child.
  • foregroundPainter: The painter that paints after the child.
  • child: By default, the canvas will take the size of the child, if it is defined.
  • size: If the child is not defined, then the size of the canvas should be specified.

Let us take an example of how we can implement the flutter application.

import 'dart:math';

import 'package:flutter/material.dart';

class CustomePainterScreen extends StatefulWidget {
  const CustomePainterScreen({Key? key, required this.progress})
      : super(key: key);

  final double progress;
  @override
  State<CustomePainterScreen> createState() => _CustomePainterScreenState();
}

class _CustomePainterScreenState extends State<CustomePainterScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.red,
      appBar: AppBar(
        title: const Text('Custom Painter'),
        centerTitle: true,
      ),
      body: Column(
        children: [
          AspectRatio(
            aspectRatio: 1,
            child: CustomPaint(
              painter: RingPainter(
                progress: widget.progress,
                taskNotCompletedColor: Colors.black,
                taskCompletedColor: Colors.green,
              ),
            ),
          ),
          const Text(
            'Custom Painter in Flutter',
            style: TextStyle(
              color: Colors.white,
              fontSize: 25,
            ),
          )
        ],
      ),
    );
  }
}

class RingPainter extends CustomPainter {
  RingPainter({
    required this.progress,
    required this.taskNotCompletedColor,
    required this.taskCompletedColor,
  });
  // a value between 0 and 1
  final double progress;
  // background color to use when the task is not completed
  final Color taskNotCompletedColor;
  // foreground color to use when the task is completed
  final Color taskCompletedColor;
  @override
  void paint(Canvas canvas, Size size) {
    final strokeWidth = size.width / 15.0;
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (size.width - strokeWidth) / 3;
    final backgroundPaint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = strokeWidth
      ..color = taskNotCompletedColor
      ..style = PaintingStyle.stroke;
    canvas.drawCircle(center, radius, backgroundPaint);
    final foregroundPaint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = strokeWidth
      ..color = taskCompletedColor
      ..style = PaintingStyle.stroke;
    canvas.drawArc(
      Rect.fromCircle(center: center, radius: radius),
      -pi / 2,
      2 * pi * progress,
      false,
      foregroundPaint,
    );
  }

  @override
  bool shouldRepaint(RingPainter oldDelegate) {
    return oldDelegate.progress != progress;
  }
}

As the RingPainter class extends from CustomPainter, which is an abstract class, two methods must be implemented within its paint: This method is called whenever the object needs to be repainted. shouldRepaint: This method is called when a new instance of the class is provided. The drawCircle method takes the coordinates for the center of the circle, its radius, and the paint object as the arguments for drawing a circle.

Meanwhile, when we run this application in the emulator or device, we should get the UI similar to the screenshot below:

Thank you for reading the Guide on How to implement the CustomPaint in Flutter.

Also Read: Flutter Widgets: Understanding A Suite Of Powerful Basic Widgets