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
If there is any solution then please let me know or i have to manually write code for all this circle and progress
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: