Flutter Circular Progress Using Custom Painter with Animation

There are multiple use cases which we will need to use the circle progress like show the fitness progress, weekly completed progress etc.

You will be able to implement the circle progress in Flutter within several minutes using CustomPaint widget and providing a custom implementation for CustomPainter class.


Before jump into the coding, we need to think about the structure. In here we need to show the following things

  • Show Progress in Text
  • Show full circle which is represent a background of the fill
  • Show Arc which shows the current Progress
  • Add animation when showing the progress



Inside the Scaffold body, you can add child widget inside the Center widget to show the CircleProgress in the centre of the screen.

Then you can provide CustomPaint as the child widget. In here you need to show the progress outside of the text widget. Therefore you need to pass CustomPainter widget as a foregroundPainter.

Then you need to create a CustomProgress. To Change the progress when the animation is happening we need to pass the progress to the widget.

As I mentioned above we have two circles to show the base of the progress and actual progress. For that, you need to create a two paint object to draw a Full Circle and a Draw an Arc. Check This Code.

class CircleProgress extends CustomPainter{

  double currentProgress;


  void paint(Canvas canvas, Size size) {

    //this is base circle
    Paint outerCircle = Paint()
        ..strokeWidth = 10
        ..color = Colors.black
        ..style = PaintingStyle.stroke;

    Paint completeArc = Paint()
      ..strokeWidth = 10
      ..color = Colors.redAccent
      ..style = PaintingStyle.stroke
      ..strokeCap  = StrokeCap.round;

    Offset center = Offset(size.width/2, size.height/2);
    double radius = min(size.width/2,size.height/2) - 10;

    canvas.drawCircle(center, radius, outerCircle); // this draws main outer circle

    double angle = 2 * pi * (currentProgress/100);

    canvas.drawArc(Rect.fromCircle(center: center,radius: radius), -pi/2, angle, false, completeArc);

  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;

Now the basic implementation is completed. Next we can add a animation to make this more cooool.


In here you can use tween animation to start from 0 and end in the percentage which you want to show on the progress. Then you can assign the current animation value to the text and you can pass that as a parameter to CircleProgress class.

class _CircleProgressState extends State with SingleTickerProviderStateMixin {

  AnimationController progressController;
  Animation animation;

  void initState() {
    // TODO: implement initState
    progressController = AnimationController(vsync: this,duration: Duration(milliseconds: 1000));
    animation = Tween(begin: 0,end: 80).animate(progressController)..addListener((){
      setState(() {


  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      body: Center(
        child: CustomPaint(

          foregroundPainter: CircleProgress(animation.value), // this will add custom painter after child
          child: Container(
            width: 200,
            height: 200,
            child: GestureDetector(
                onTap: (){
                  if(animation.value == 80){
                  }else {
                child: Center(child: Text("${animation.value.toInt()}%",style: TextStyle(
                  fontSize: 30,
                  fontWeight: FontWeight.bold


Check the Video

Leave a Reply