Is there a way to click the section and get its data?
I cant figure it out to save my life.
I know i can get the index information when i hover over the section with touchedIndex but I cant figure out how to get the information when the user clicks on the data.
I tried adding GestureDetector to individual elements but that didnt work for some reason. not sure why but the following does not work::
return GestureDetector(
onTap: (){
print("tab");
},
child: DonutReportDecorator(
records: dummyRecords,
selected_sys_group_by: selectedSysGroupBy,
selected_operation: selectedOperation,
title: title,
),
);
I am using fl_chart: ^0.60.0.
all the code is here.
import 'dart:convert';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
// Define dummy data
final dummyRecords = [
{'category': 'A', 'value': 10},
{'category': 'B', 'value': 20},
{'category': 'C', 'value': 30},
{'category': 'A', 'value': 15},
{'category': 'B', 'value': 25},
{'category': 'C', 'value': 35},
];
// Define dummy parameters
final selectedSysGroupBy = 'category';
final selectedOperation = 'sum';
final title = 'Donut Chart Example';
return DonutReportDecorator(
records: dummyRecords,
selected_sys_group_by: selectedSysGroupBy,
selected_operation: selectedOperation,
title: title,
);
}
}
class DonutReportDecorator extends StatefulWidget {
final List<Map<String, dynamic>> records;
final String selected_sys_group_by;
final String selected_operation;
final String title;
DonutReportDecorator({
Key? key,
required this.records,
required this.selected_sys_group_by,
required this.selected_operation,
required this.title,
}) : super(key: key);
@override
_DonutReportState createState() => _DonutReportState();
}
class _DonutReportState extends State<DonutReportDecorator> {
int touchedIndex = -1;
@override
Widget build(BuildContext context) {
Map<String, double> groupCounts = ReportUtils.calculateGroupCounts(
widget.records,
widget.selected_sys_group_by,
widget.selected_operation,
);
double totalCount = groupCounts.values.reduce((a, b) => a + b);
return LayoutBuilder(
builder: (context, constraints) {
final maxWidth = constraints.maxWidth;
final maxHeight = constraints.maxHeight;
return Column(
children: [
Padding(
padding: EdgeInsets.only(top: maxHeight * 0.02, bottom: maxHeight * 0.01),
child: Text(
widget.title,
style: TextStyle(
fontSize: maxWidth * 0.04,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
Flexible(
flex: 3,
child: LayoutBuilder(
builder: (context, chartConstraints) {
final chartSize = chartConstraints.maxWidth < chartConstraints.maxHeight
? chartConstraints.maxWidth
: chartConstraints.maxHeight;
return Center(
child: SizedBox(
width: chartSize,
height: chartSize,
child: Stack(
children: [
PieChart(
PieChartData(
pieTouchData: PieTouchData(
touchCallback: (FlTouchEvent event, pieTouchResponse) {
setState(() {
if (!event.isInterestedForInteractions ||
pieTouchResponse == null ||
pieTouchResponse.touchedSection == null) {
touchedIndex = -1;
} else {
touchedIndex = pieTouchResponse.touchedSection!.touchedSectionIndex;
}
});
},
),
sections: generateSections(groupCounts, chartSize),
centerSpaceRadius: chartSize * 0.15,
sectionsSpace: 2,
borderData: FlBorderData(show: false),
),
),
if (touchedIndex != -1)
Positioned.fill(
child: Align(
alignment: Alignment.center,
child: Container(
padding: EdgeInsets.all(chartSize * 0.02),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7),
borderRadius: BorderRadius.circular(chartSize * 0.02),
),
child: Text(
'${groupCounts.keys.elementAt(touchedIndex)}n'
'Count: ${groupCounts.values.elementAt(touchedIndex)}n'
'Percentage: ${((groupCounts.values.elementAt(touchedIndex) / totalCount) * 100).toStringAsFixed(1)}%',
style: TextStyle(color: Colors.white, fontSize: chartSize * 0.04),
textAlign: TextAlign.center,
),
),
),
),
],
),
),
);
},
),
),
Flexible(
flex: 1,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: maxWidth * 0.02),
child: Wrap(
spacing: maxWidth * 0.02,
runSpacing: maxHeight * 0.01,
children: groupCounts.entries.map((entry) {
double percentage = (entry.value / totalCount) * 100;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: maxWidth * 0.03,
height: maxWidth * 0.03,
color: Colors.primaries[entry.key.hashCode % Colors.primaries.length],
),
SizedBox(width: maxWidth * 0.01),
Text(
'${entry.key} (${entry.value}/${totalCount} || ${percentage.toStringAsFixed(1)}%)',
style: TextStyle(fontSize: maxWidth * 0.02),
),
],
);
}).toList(),
),
),
),
],
);
},
);
}
List<PieChartSectionData> generateSections(Map<String, double> groupCounts, double chartSize) {
return groupCounts.entries.map((entry) {
final isTouched = groupCounts.keys.toList().indexOf(entry.key) == touchedIndex;
final double fontSize = isTouched ? chartSize * 0.05 : chartSize * 0.04;
final double radius = isTouched ? chartSize * 0.4 : chartSize * 0.35;
return PieChartSectionData(
color: Colors.primaries[entry.key.hashCode % Colors.primaries.length],
value: entry.value,
title: '',
radius: radius,
titleStyle: TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold, color: Colors.white),
badgeWidget: Text(''),
badgePositionPercentageOffset: .98,
);
}).toList();
}
}
class ReportUtils {
static Map<String, double> calculateGroupCounts(
List<Map<String, dynamic>> records,
String selectedGroupByColumn,
String selectedOperation) {
Map<String, double> groupCounts = {};
if (selectedOperation == 'count') {
for (var record in records) {
var groupValue = record[selectedGroupByColumn].toString();
groupCounts[groupValue] = (groupCounts[groupValue] ?? 0) + 1;
}
} else if (selectedOperation == 'unique_values') {
for (var record in records) {
var groupValue = record[selectedGroupByColumn].toString();
groupCounts[groupValue] = groupCounts.containsKey(groupValue) ? 1 : 1;
}
} else if (selectedOperation == 'sum') {
for (var record in records) {
var groupValue = record[selectedGroupByColumn].toString();
var value = record[selectedGroupByColumn];
if (value is num) {
groupCounts[groupValue] = (groupCounts[groupValue] ?? 0) + value.toDouble();
} else if (value is String) {
groupCounts[groupValue] = (groupCounts[groupValue] ?? 0) + value.length.toDouble();
}
}
}
return groupCounts;
}
static String get_table_id_from_filter(Map<String, dynamic> report) {
print("get_table_id_from_filter()n");
if (report == null || report.toString() == {}.toString()) {
return "";
}
Map<String, dynamic> json = jsonDecode(report['sys_filter']);
String table_id = json['table_id'];
return table_id;
}
}