androidiosflutterdartprogress-bar

How to create this circle progress with diff colors for diff progresses


I want a circle or semi circle progress diagram or indication type in my flutter app i have researched many but is there is way for this so it can we easy and flexible

see i want like this or it can be a whole circle enter image description here

If there is any solution then please let me know or i have to manually write code for all this circle and progress


Solution

  • Here's a way to do it without using a CustomPainter, using only existing Flutter widgets:

    class TripleLoadingWidget extends StatelessWidget {
      final double outerMinimum;
      final double outerMaximum;
      final double outerValue;
      final Color outerColor;
      
      final double middleMinimum;
      final double middleMaximum;
      final double middleValue;
      final Color middleColor;
      
      final double innerMinimum;
      final double innerMaximum;
      final double innerValue;
      final Color innerColor;
      
      final double strokePadding;
      final double strokeWidth;
      final StrokeCap strokeCap;
    
      const TripleLoadingWidget({
        this.outerColor = Colors.cyan,
        this.outerMinimum = 0,
        this.outerMaximum = 100,
        required this.outerValue,
        this.middleColor = Colors.indigo,
        this.middleMinimum = 0,
        this.middleMaximum = 100,
        required this.middleValue,
        this.innerColor = Colors.orange,
        this.innerMinimum = 0,
        this.innerMaximum = 100,
        required this.innerValue,
        this.strokePadding = 8,
        this.strokeWidth = 2,
        this.strokeCap = StrokeCap.butt,
      });
    
      double normalize(double min, double max, double v) {
        return (v - min) / (max - min);
      }
    
      Widget drawGauge({
        required Rect rect,
        required Color color,
        required double minimum,
        required double maximum,
        required double value,
      }) {
        return Positioned.fromRect(
          rect: rect,
          // CircularProgressIndicator starts at the top, so it needs to be rotated to
          // start at the left
          child: Transform.rotate(
            angle: -pi / 2,
            child: CircularProgressIndicator(
              // Halving the value makes a half-circle
              value: normalize(minimum, maximum, value) / 2, 
              color: color,
              strokeWidth: strokeWidth,
              strokeCap: strokeCap,
            ),
          ),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return LayoutBuilder(
          builder: (cxt, constraints) {
            // Create an internal square bounding rect
            final widgetBounds =
                Offset.zero &
                Size(
                  constraints.biggest.longestSide,
                  constraints.biggest.longestSide,
                );
    
            // Calculate each arc based on the size and position of the last arc
            // Constrict the outer arc based on the thickness of the stroke
            final outerRect = widgetBounds
                .deflate(strokeWidth)
                .translate(0, strokeCap == StrokeCap.butt ? 0 : -strokeWidth / 2);
            final middleRect = outerRect.deflate(strokeWidth + strokePadding);
            final innerRect = middleRect.deflate(strokeWidth + strokePadding);
    
            // Size the stack to half the square to only draw half-circles
            return SizedBox(
              width: widgetBounds.width,
              height: widgetBounds.width / 2,
              child: Stack(
                children: [
                  drawGauge(
                    rect: outerRect,
                    color: outerColor,
                    minimum: outerMinimum,
                    maximum: outerMaximum,
                    value: outerValue,
                  ),
                  drawGauge(
                    rect: middleRect,
                    color: middleColor,
                    minimum: middleMinimum,
                    maximum: middleMaximum,
                    value: middleValue,
                  ),
                  drawGauge(
                    rect: innerRect,
                    color: innerColor,
                    minimum: innerMinimum,
                    maximum: innerMaximum,
                    value: innerValue,
                  ),
                ],
              ),
            );
          },
        );
      }
    }
    

    Example usage:

    class MyHomePage extends StatefulWidget {
      final String title;
    
      const MyHomePage({super.key, required this.title});
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      double _outerValue = 75;
      double _middleValue = 15;
      double _innerValue = 30;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text(widget.title)),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                SizedBox(
                  width: 300,
                  height: 150,
                  child: TripleLoadingWidget(
                    outerValue: _outerValue,
                    middleValue: _middleValue,
                    innerValue: _innerValue,
                    strokeWidth: 16,
                    strokePadding: 4,
                    strokeCap: StrokeCap.round,
                  ),
                ),
                const SizedBox(height: 12),
                const Text('Outer Value:'),
                Slider(
                  min: 1,
                  max: 100,
                  value: _outerValue,
                  onChanged: (d) {
                    setState(() => _outerValue = d);
                  },
                ),
                const SizedBox(height: 6),
                const Text('Middle Value:'),
                Slider(
                  min: 1,
                  max: 100,
                  value: _middleValue,
                  onChanged: (d) {
                    setState(() => _middleValue = d);
                  },
                ),
                const SizedBox(height: 6),
                const Text('Inner Value:'),
                Slider(
                  min: 1,
                  max: 100,
                  value: _innerValue,
                  onChanged: (d) {
                    setState(() => _innerValue = d);
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    

    Screenshot: