Search code examples
flutterdartflutter-layout

Filled circle progress bar


Does anyone know how to do filled circle progress bar like on a picture?

Picture:


Solution

  • You can use CustomPainter

    class CirclePaint extends CustomPainter {
      final double value;
    
      CirclePaint(this.value);
    
      @override
      void paint(Canvas canvas, Size size) {
        final area = Rect.fromCircle(
            center: size.center(Offset.zero), radius: size.width / 2);
    
        canvas.drawArc(
            area, -pi / 2, 2 * pi * value, true, Paint()..color = Colors.amber);
      }
    
      @override
      bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
    }
    

    And play with this widget and change the color you like to have.

      double value = .4;
      final size = 200.0;
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              children: [
                Slider(
                  value: value,
                  onChanged: (v) {
                    setState(() {
                      value = v;
                    });
                  },
                ),
                Container(
                  width: size,
                  height: size,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    border: Border.all(
                      color: Colors.blue,
                      width: 4,
                    ),
                  ),
                  child: CustomPaint(
                    painter: CirclePaint(value),
                  ),
                ),
              ],
            ),
          ),
        );
    }
    

    enter image description here

    Find more about CustomPaint

    With Listenable repaint as pskink mentioned on comment

    
    class Psf extends StatelessWidget {
      const Psf({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        ValueNotifier<double> value = ValueNotifier(.3);
    
        const size = 200.0;
        return Scaffold(
          body: Center(
            child: Column(
              children: [
                Slider(
                  value: value.value,
                  onChanged: (v) {
                    value.value = v;
                  },
                ),
                Container(
                  width: size,
                  height: size,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    border: Border.all(
                      color: Colors.blue,
                      width: 4,
                    ),
                  ),
                  child: CustomPaint(
                    painter: CirclePaint(value),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class CirclePaint extends CustomPainter {
      final ValueNotifier<double> value;
    
      CirclePaint(this.value) : super(repaint: value);
    
      @override
      void paint(Canvas canvas, Size size) {
        final area = Rect.fromCircle(
            center: size.center(Offset.zero), radius: size.width / 2);
    
        canvas.drawArc(area, -pi / 2, 2 * pi * value.value, true,
            Paint()..color = Colors.amber);
      }
    
      @override
      bool shouldRepaint(covariant CirclePaint oldDelegate) =>
          oldDelegate.value != value;
    }
    

    With custom decoration as @pskink included, you need to use some custom snippet for this, check them here

    Container(
      width: size,
      height: size,
      decoration: AnimatedDecoration(
        listenable: value,
        onPaintFrame: (canvas, bounds, double v) {
          canvas.drawArc(bounds, -pi / 2, 2 * pi * v, true,
              Paint()..color = Colors.amber);
        },
      ),
    ),