chart or gauge can be used to produce the charts attached
Hello,
What chart or gauge can be used to produce the charts below?
Thank and best regards
Sao
Hi Sao,
To achieve your requirement, you can use the SfRadialGauge. Here multiple radial gauges is rendered in a single row with multiple columns and rendered the text using the GaugeAnnotation and the range is given with the RangePointer as shown in the code snippet below.
|
class _MyHomePageState extends State<MyHomePage> { final TextStyle _textStyle = const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, );
@override Widget build(BuildContext context) { return Scaffold( body: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(width: 40), Expanded( child: Column( children: [ SfRadialGauge( axes: <RadialAxis>[ RadialAxis( startAngle: 270, endAngle: 270, showLabels: false, showTicks: false, pointers: const <GaugePointer>[ RangePointer( value: 10, color: Colors.red, ), ], annotations: <GaugeAnnotation>[ GaugeAnnotation( widget: Text( '10 %', style: _textStyle, ), ), ], ), ], ), ], ), ), const SizedBox(width: 40), Expanded( child: Column( children: [ SfRadialGauge( axes: <RadialAxis>[ RadialAxis( startAngle: 270, endAngle: 270, showLabels: false, showTicks: false, pointers: const <GaugePointer>[ RangePointer( value: 64, color: Colors.green, ), ], annotations: <GaugeAnnotation>[ GaugeAnnotation( widget: Text( '- 64 %', style: _textStyle, ), ), ], ), ], ), ], ), ), const SizedBox(width: 40), Expanded( child: Column( children: [ SfRadialGauge( axes: <RadialAxis>[ RadialAxis( startAngle: 270, endAngle: 270, showLabels: false, showTicks: false, pointers: const <GaugePointer>[ RangePointer( value: 49, color: Colors.red, ), ], annotations: <GaugeAnnotation>[ GaugeAnnotation( widget: Text( '49 %', style: _textStyle, ), ), ], ), ], ), ], ), ), const SizedBox(width: 40), Expanded( child: Column( children: [ SfRadialGauge( axes: <RadialAxis>[ RadialAxis( startAngle: 270, endAngle: 270, showLabels: false, showTicks: false, pointers: <GaugePointer>[ RangePointer( value: 270, color: Colors.red, ), ], annotations: <GaugeAnnotation>[ GaugeAnnotation( widget: Text( '357 %', style: _textStyle, ), ), ], ), ], ), ], ), ), const SizedBox(width: 40), Expanded( child: Column( children: [ SfRadialGauge( axes: <RadialAxis>[ RadialAxis( startAngle: 270, endAngle: 270, showLabels: false, showTicks: false, pointers: const <GaugePointer>[ RangePointer( value: 33, color: Colors.red, ), ], annotations: <GaugeAnnotation>[ GaugeAnnotation( widget: Text( '33 %', style: _textStyle, ), ), ], ), ], ), ], ), ), const SizedBox(width: 40), Expanded( child: Column( children: [ SfRadialGauge( axes: <RadialAxis>[ RadialAxis( startAngle: 270, endAngle: 270, showLabels: false, showTicks: false, pointers: const <GaugePointer>[ RangePointer( value: 40, color: Colors.red, ), ], annotations: <GaugeAnnotation>[ GaugeAnnotation( widget: Text( '40 %', style: _textStyle, ), ), ], ), ], ), ], ), ), const SizedBox(width: 40), ], ), ); } } |
Snapshot:
Also attached the sample below for your reference. If you have further queries, please get back to us.
Regards,
Hari Hara Sudhan. K.
Attachment: 183729_d63e4f7b.zip
Dear Hari Hara Sudhan. K.,
Thank you very much for this useful solution. How do I bind the chart to flutter data class?
Regards
Sao
Hi Sao,
You can achieve the mentioned requirement, using either an SfRadialGauge or SfCircularChart. However, you cannot bind the data source while using SfRadialGauge and therefore we have used the doughnut series in the SfCircularChart to achieve the mentioned requirement by binding the data source in the attached sample below. We also used CircularChartAnnotation to render the text at the center of the widget with the help of annotations property.
Kindly refer the code snippet below:
|
class _MyHomePageState extends State<MyHomePage> { final TextStyle _textStyle = const TextStyle( fontSize: 18, ); late List<ChartSampleData> _firstData; late List<ChartSampleData> _secondData; late List<ChartSampleData> _thirdData; late List<ChartSampleData> _fourthData; late List<ChartSampleData> _fifthData; late List<ChartSampleData> _sixthData; late List<Color> _colors; late List<Color> _additionalColors;
@override void initState() { _firstData = <ChartSampleData>[ ChartSampleData(x: '10', y: 05), ChartSampleData(x: '20', y: 35), ]; _secondData = <ChartSampleData>[ ChartSampleData(x: '10', y: 25), ChartSampleData(x: '20', y: 15), ]; _thirdData = <ChartSampleData>[ ChartSampleData(x: '10', y: 20), ChartSampleData(x: '20', y: 20), ]; _fourthData = <ChartSampleData>[ ChartSampleData(x: '10', y: 10), ]; _fifthData = <ChartSampleData>[ ChartSampleData(x: '10', y: 15), ChartSampleData(x: '20', y: 25), ]; _sixthData = <ChartSampleData>[ ChartSampleData(x: '10', y: 17), ChartSampleData(x: '20', y: 23), ]; _colors = <Color>[ Colors.red, const Color(0xFFC8C9CB), ]; _additionalColors = <Color>[ Colors.green, const Color(0xFFC8C9CB), ]; super.initState(); }
@override Widget build(BuildContext context) { return Scaffold( body: Row( children: [ Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _firstData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text('10%', style: _textStyle), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _secondData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _additionalColors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text('- 64%', style: _textStyle), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _thirdData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text('49%', style: _textStyle), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _fourthData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text('357%', style: _textStyle), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _fifthData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text('33%', style: _textStyle), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _sixthData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text('40%', style: _textStyle), ), ], ), ], ), ), ], ), ); }
@override void dispose() { _firstData.clear(); _secondData.clear(); _thirdData.clear(); _fourthData.clear(); _fifthData.clear(); _sixthData.clear(); _colors.clear(); _additionalColors.clear(); super.dispose(); } } |
Snapshot:
If you need to achieve the mentioned requirement using SfRadialGauge, you can refer to the code snippet that has already been attached in the previous response and modify the samples based on your requirement.
Also shared the User Guide documentation for binding the data source in the SfCircularChart below for your reference: UG: https://help.syncfusion.com/flutter/circular-charts/getting-started#bind-data-source.
If you have further queries, please feel free to reach out to us.
Regards,
Hari Hara Sudhan. K.
Attachment: _183729_a9fec16e.zip
Dear Hari Hara Sudhan. K. ,
Thank! You solution has helped me a lot.
Regards
Sao
Hi Sao,
Most Welcome. Kindly get back to us if you have further queries. We are always happy to assist you.
Regards,
Hari Hara Sudhan. K.
Dear
Hari Hara Sudhan,
It would be nice of you to show me how to replace the static text in the annotation with dynamically calculated value obtained from data class
annotations: [
CircularChartAnnotation(
widget: Text('40%', style: _textStyle),
),
],
Sao
Hi Sao,
You can be able to update the text of the annotations with dynamically calculated values obtained from the data class by accessing the data points from the data list and updating it. Here, we have calculated the difference between the data points in the individual data list respectively and displayed it using the Text widget in the annotations property as show in the code snippet below.
|
class _MyHomePageState extends State<MyHomePage> { final TextStyle _textStyle = const TextStyle( fontSize: 18, ); late List<ChartSampleData> _firstData; late List<ChartSampleData> _secondData; late List<ChartSampleData> _thirdData; late List<ChartSampleData> _fourthData; late List<ChartSampleData> _fifthData; late List<ChartSampleData> _sixthData; late List<Color> _colors; late List<Color> _additionalColors;
@override void initState() { _firstData = <ChartSampleData>[ ChartSampleData(x: '10', y: 05), ChartSampleData(x: '20', y: 35), ]; _secondData = <ChartSampleData>[ ChartSampleData(x: '10', y: 25), ChartSampleData(x: '20', y: 15), ]; _thirdData = <ChartSampleData>[ ChartSampleData(x: '10', y: 20), ChartSampleData(x: '20', y: 20), ]; _fourthData = <ChartSampleData>[ ChartSampleData(x: '10', y: 363), ]; _fifthData = <ChartSampleData>[ ChartSampleData(x: '10', y: 15), ChartSampleData(x: '20', y: 25), ]; _sixthData = <ChartSampleData>[ ChartSampleData(x: '10', y: 17), ChartSampleData(x: '20', y: 23), ]; _colors = <Color>[ Colors.red, const Color(0xFFC8C9CB), ]; _additionalColors = <Color>[ Colors.green, const Color(0xFFC8C9CB), ]; super.initState(); }
@override Widget build(BuildContext context) { return Scaffold( body: Row( children: [ Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _firstData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text( '${_firstData[0].y! - _firstData[1].y!}%', style: _textStyle, ), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _secondData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _additionalColors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text( '${_secondData[0].y! - _secondData[1].y!}%', style: _textStyle, ), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _thirdData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text( '${_thirdData[0].y! - _thirdData[1].y!}%', style: _textStyle, ), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _fourthData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text( '${_fourthData[0].y!}%', style: _textStyle, ), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _fifthData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text( '${_fifthData[0].y! - _fifthData[1].y!}%', style: _textStyle, ), ), ], ), ], ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SfCircularChart( series: <CircularSeries<ChartSampleData, String>>[ DoughnutSeries( radius: '60', innerRadius: '40', dataSource: _sixthData, xValueMapper: (ChartSampleData sales, _) => sales.x, yValueMapper: (ChartSampleData sales, _) => sales.y, pointColorMapper: (ChartSampleData sales, int index) => _colors[index], ), ], annotations: [ CircularChartAnnotation( widget: Text( '${_sixthData[0].y! - _sixthData[1].y!}%', style: _textStyle, ), ), ], ), ], ), ), ], ), ); }
@override void dispose() { _firstData.clear(); _secondData.clear(); _thirdData.clear(); _fourthData.clear(); _fifthData.clear(); _sixthData.clear(); _colors.clear(); _additionalColors.clear(); super.dispose(); } } |
Snapshot:
Also attached the sample below for your reference and you can modify the sample according to your needs. If you have further queries, please get back to us.
Regards,
Hari Hara Sudhan. K.
Attachment: 183729_d2097814.zip
Dear Hari Hara Sudhan. K.
I am writing to get your assistance again. Following your sample code, I am able to put the sfCircularChart to work. however, there is an error
"Exception has occurred.
RangeError (RangeError (index): Invalid value: Valid value range is empty: 1)"
causing by
widget: Text('${((_percChangeData[1].y! - _percChangeData[0].y!) / _percChangeData[0].y! * 100)}%',
style: circularChartAnnotationTextStyle01),
I have include the code below:
class ChartSampleData {
final String x;
final num y;
ChartSampleData({required this.x, required this.y});
}
List<ChartSampleData> getFirstCaseData(List<dynamic> rows) {
return rows.map((row) {
final x = row[0];
final y = num.tryParse(row[2]) ?? 0;
return ChartSampleData(x: x, y: y);
}).toList();
}
class MyDynamicDoughnutChart extends StatefulWidget {
const MyDynamicDoughnutChart({Key? key}) : super(key: key);
@override
State<MyDynamicDoughnutChart> createState() => _MyDynamicDoughnutChartState();
}
class _MyDynamicDoughnutChartState extends State<MyDynamicDoughnutChart> {
String get firstUrl =>
"http://...?dimension=dx:aNV9H1BXtfl&dimension=pe:2022;2023&displayProperty=NAME";
late List<ChartSampleData> _percChangeData;
@override
void initState() {
super.initState();
_percChangeData = [];
fetchData();
}
Future<void> fetchData() async {
String basicAuth = 'Basic ${base64Encode(
utf8.encode('$username:$password'),
)}';
http.Response firstUrlResponse = await http.get(
Uri.parse(firstUrl),
headers: <String, String>{'authorization': basicAuth},
);
try {
if (firstUrlResponse.statusCode == 200) {
final firstData = jsonDecode(firstUrlResponse.body);
setState(() {
_percChangeData = getFirstCaseData(firstData['rows']);
});
} else {
throw Exception('Failed to load data');
}
} catch (e) {}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dougnut Chart 2'),
centerTitle: true,
),
body: SingleChildScrollView(
child: Column(
children: [
const Center(
child: Text(
'% Change in pf case ',
style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
),
),
Row(
children: [
const SizedBox(
height: 10,
),
const SizedBox(
height: 20,
),
SizedBox(
child: SfCircularChart(
series: <CircularSeries<ChartSampleData, String>>[
DoughnutSeries(
radius: '30',
innerRadius: '20',
dataSource: _percChangeData,
xValueMapper: (ChartSampleData sales, _) => sales.x!,
yValueMapper: (ChartSampleData sales, _) => sales.y!,
dataLabelSettings: const DataLabelSettings(
color: Colors.white,
showZeroValue: false,
labelAlignment: ChartDataLabelAlignment.auto,
isVisible: false,
labelPosition: ChartDataLabelPosition.outside,
textStyle: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold)),
pointColorMapper: (ChartSampleData sales, int index) {
final percentage =
((_percChangeData[1].y! - _percChangeData[0].y!) /
_percChangeData[0].y! *
100);
if (percentage.isNegative) {
return Colors.green;
} else {
return Colors.red;
}
},
),
],
annotations: [
CircularChartAnnotation(
verticalAlignment: ChartAlignment.center,
horizontalAlignment: ChartAlignment.center,
widget: Text(
'${((_percChangeData[1].y! - _percChangeData[0].y!) / _percChangeData[0].y! * 100)}%',
style: circularChartAnnotationTextStyle01),
),
],
),
)
],
),
],
),
),
);
}
}
Regards
Hi Sao,
Based on the provided code snippet, we suspect that the mentioned range exception might be caused by the _percChangeData list being empty. We have attempted to replicate the mentioned range exception using the provided code snippet. Unfortunately, the code snippet is not in a runnable state, as it requires the username and password of the URL. However, a range exception may occur when trying to access elements of a list when the list is empty or when the index exceeds the length of the list.
In this case, the _percChangeData list is declared and initialized as an empty list, which is then assigned to the data source. This is the reason for the range exception. We recommend adding elements to the list according to your needs before attempting to access its elements. Additionally, you can check if the _percChangeData list is empty or not by using an if condition statement before accessing its elements. If you have any further questions, please feel free to reach out to us.
Regards,
Hari Hara Sudhan. K.