return SfRadialGauge(
title: GaugeTitle(
text: 'Speedometer',
textStyle:
const TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)),
axes: <RadialAxis>[
RadialAxis(
minimum: 0,
maximum: 320,
startAngle: 180,
endAngle: 0,
// showLabels: false,
maximumLabels: 320,
onLabelCreated: _handleLabelCreated,
canRotateLabels: true,
labelsPosition: ElementsPosition.outside,
ticksPosition: ElementsPosition.outside,
showTicks: false,
radiusFactor: 1.1,
axisLineStyle: AxisLineStyle(
thickness: 5,
dashArray: <double>[(320+100) / 5, 3],
// color: Color(0xFF66BB6A)
gradient: SweepGradient(colors: <Color>[
Color(0xFFE7627D),
Color(0xFF231557),
Color(0xFF44107A),
Color(0xFFFF1361),
Color(0xFFFFF800),
], stops: <double>[
0,
100,
190,
240,
320
])
),
/* ranges: <GaugeRange>[
GaugeRange(
startValue: 0,
endValue: 100,
color: Colors.green,
),
GaugeRange(
startValue: 100,
endValue: 210,
color: Colors.orange,
),
GaugeRange(
startValue: 210,
endValue: 320,
color: Colors.red,
)
],*/
annotations: <GaugeAnnotation>[
GaugeAnnotation(
angle: 180,
horizontalAlignment: GaugeAlignment.near,
positionFactor: 0.78,
verticalAlignment: GaugeAlignment.near,
widget: Text(
"0",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 14,
),
)),
GaugeAnnotation(
angle: 0,
horizontalAlignment: GaugeAlignment.far,
positionFactor: 0.85,
verticalAlignment: GaugeAlignment.near,
widget: Text(
"320",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 14,
),
)),
GaugeAnnotation(
axisValue: 100,
positionFactor: 0.5,
angle: 90,
widget: Column(children: [
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: '223\n',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 30,
),
children: [
TextSpan(
text: 'Points earned this month',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.normal,
fontSize: 12,
),
)
],
)),
)
]))
],
pointers: <GaugePointer>[
MarkerPointer(
value: 223,
markerHeight: 15,
markerWidth: 15,
enableDragging: true,
overlayRadius: 12,
borderColor: Colors.green,
borderWidth: 2,
color: Colors.white,
markerType: MarkerType.circle)
],
)
]);
void _handleLabelCreated(AxisLabelCreatedArgs args) {
if (args.text == '30') {
args.text = '£0 - £4';
} else if (args.text == '100') {
args.text = '£4 - £8';
} else if (args.text == '165') {
args.text = '£8 - £12';
} else if (args.text == '235') {
args.text = '£12 - £16';
} else if (args.text == '290') {
args.text = '£16 - £20';
} else {
args.text = '';
}
I have to develop this type of UI using plugin.but there is some point remaining like color we are not achieve after pointer and how equally segment divided and how responsive for all device that need to check.
I have done below part from my side and remaining thing need to add that i mention .So suggest me how can i achieve .
Your requirement can be achieved with help of the ranges property, MarkerPointer onValueChanging callback in the SfRadialGauge. Added _buildRanges method to find the segment length based on the max value and return the list of the GaugeRange, and added _buildGaugeRange method to create GaugeRange based on the given start, end, and _pointerValue value. The minimum value to the marker range is considered an active range, and the maximum value to the marker range is considered an inactive range in the _buildGaugeRange method. The GaugeRange is colored based on the active and inactive range.
We have prepared and shared a code snippet below for your reference.
class _MyHomePageState extends State<MyHomePage> {
double _pointerValue = 256;
int _segmentsCount = 5;
late List<LabelDetails> _labels;
@override
void initState() {
_labels = [];
_calculateLabelsPosition();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SfRadialGauge(
title: const GaugeTitle(
text: 'Speedometer',
textStyle:
TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)),
axes: <RadialAxis>[
RadialAxis(
minimum: 0,
maximum: 320,
startAngle: 180,
endAngle: 0,
maximumLabels: 320,
canScaleToFit: true,
interval: 1,
onLabelCreated: _handleLabelCreated,
canRotateLabels: true,
labelsPosition: ElementsPosition.outside,
showTicks: false,
radiusFactor: 1.1,
pointers: <GaugePointer>[
MarkerPointer(
onValueChanging: (dynamic args) {
setState(() {
_pointerValue = args.value;
});
},
value: _pointerValue,
enableDragging: true,
color: Colors.white,
borderColor: Colors.green,
borderWidth: 3,
markerType: MarkerType.circle,
markerHeight: 15,
markerWidth: 15,
overlayRadius: 0,
)
],
ranges: _buildRanges(),
annotations: <GaugeAnnotation>[
const GaugeAnnotation(
angle: 180,
horizontalAlignment: GaugeAlignment.far,
positionFactor: 0.75,
verticalAlignment: GaugeAlignment.near,
widget: Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
"0",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
)),
const GaugeAnnotation(
angle: 0,
horizontalAlignment: GaugeAlignment.far,
positionFactor: 0.85,
verticalAlignment: GaugeAlignment.near,
widget: Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
"320",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
)),
GaugeAnnotation(
widget:
Column(mainAxisSize: MainAxisSize.min, children: [
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: '${_pointerValue.round()}\n',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 30,
),
children: const [
TextSpan(
text: 'Points earned this month',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.normal,
fontSize: 12,
),
)
],
)),
)
]))
],
),
]),
ElevatedButton(
onPressed: () {
setState(() {
_segmentsCount = 7;
_calculateLabelsPosition();
});
},
child: const Text('Update Segment count'))
],
),
);
}
void _calculateLabelsPosition() {
_labels.clear();
// Length of the each segment.
double segmentLength = 320 / _segmentsCount;
double start = segmentLength / 2;
for (int i = 0; i < _segmentsCount; i++) {
_labels.add(LabelDetails(start.toInt(), '£${i * 4} - £${(i + 1) * 4}'));
start += segmentLength;
}
}
void _handleLabelCreated(AxisLabelCreatedArgs args) {
for (int i = 0; i < _segmentsCount; i++) {
LabelDetails details = _labels[i];
if (details.labelPoint == int.parse(args.text)) {
args.text = details.customizedLabel;
return;
}
}
args.text = '';
}
// Return the list of gauge range
List<GaugeRange> _buildRanges() {
List<GaugeRange> ranges = [];
// Gap value between two range
int gap = 2;
// Length of the each segment without gap.
double segmentLength =
(320 - ((_segmentsCount - 1) * gap)) / _segmentsCount;
double start = 0;
for (int i = 0; i < _segmentsCount; i++) {
_buildGaugeRange(start, start + segmentLength, ranges);
start += segmentLength + gap;
}
return ranges;
}
// Method to create a GaugeRange based on start, end and pointerValue and assigned
// color based on active and inactive range.
void _buildGaugeRange(double start, double end, List<GaugeRange> ranges) {
if (_pointerValue >= start && _pointerValue <= end) {
ranges.add(GaugeRange(
startValue: start, endValue: _pointerValue, color: Colors.green));
ranges.add(GaugeRange(
startValue: _pointerValue,
endValue: end,
color: const Color.fromARGB(255, 82, 86, 97),
));
} else if (_pointerValue >= end) {
ranges.add(GaugeRange(
startValue: start,
endValue: end,
color: Colors.green,
));
} else {
ranges.add(GaugeRange(
startValue: start,
endValue: end,
color: const Color.fromARGB(255, 82, 86, 97),
));
}
}
}
class LabelDetails {
LabelDetails(this.labelPoint, this.customizedLabel);
int labelPoint;
String customizedLabel;
}
Screenshot: