import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/infrastructure/services/packages/material_color.dart'; import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart'; import 'package:uae_stat/presentation/components/space.dart'; /// individual pie charts per category class ThemedPieChartPlural extends ConsumerWidget { /// must provide either [materialColor] or [perSegmentColor] const ThemedPieChartPlural({ super.key, required this.data, this.materialColor, this.perSegmentColor, this.badgePositionPercentageOffset = 2, }); final Map data; /// key should match data final Map? perSegmentColor; final double badgePositionPercentageOffset; /// generates segment colors from [MaterialColor] /// do not provide if [perSegmentColor] is also provided final MaterialColor? materialColor; @override Widget build(BuildContext context, ref) { assert(!(perSegmentColor != null && materialColor != null)); final totalVal = data.values.fold( 0.0, (prev, e) => prev + e, ); final pieCharts = List.generate( data.length, (index) { final e = data.entries.elementAt(index); final fraction = e.value / totalVal; final pieChartSectionData = [ PieChartSectionData( radius: 11, value: 1 - fraction, color: Colors.grey.shade200, showTitle: false, ), PieChartSectionData( radius: 11, value: fraction, color: _segmentColor(index), showTitle: false, ), ]; return Column( children: [ Expanded( child: FittedBox( fit: BoxFit.scaleDown, child: Text( e.key, textAlign: TextAlign.center, style: const TextStyle( fontSize: 11, ), ), ), ), 4.verticalSpace, Expanded( flex: 4, child: Stack( alignment: Alignment.center, children: [ Text( '${(fraction * 100).round()}%', ), PieChart( PieChartData( sections: pieChartSectionData, ), ), ], ), ), 4.verticalSpace, Expanded( child: Text( e.value.toStringWithCommas( abbreviateIfAbove10powN: 5, ), textAlign: TextAlign.center, style: const TextStyle( fontSize: 11, ), ), ), ], ); }, ); // final pieChart = PieChart( // PieChartData( // sections: List.generate( // data.length, // (index) { // final e = data[index]; // return PieChartSectionData( // radius: 22, // badgeWidget: ConstrainedBox( // constraints: const BoxConstraints(maxWidth: 100), // child: Text( // '${e.key}\n${e.value.toStringWithCommas()}', // textAlign: TextAlign.center, // style: TextStyle( // fontFamily: context.translate( // 'Roboto', // 'NotoKufi', // ), // fontSize: 10, // fontWeight: FontWeight.w600, // ), // ), // ), // badgePositionPercentageOffset: badgePositionPercentageOffset, // title: '${((e.value / totalVal) * 100).toStringAsFixed(1)}%', // titlePositionPercentageOffset: -0.6, // titleStyle: TextStyle( // fontFamily: context.translate( // 'Roboto', // 'NotoKufi', // ), // fontSize: 10, // fontWeight: FontWeight.w600, // ), // showTitle: true, // color: _segmentColor(index), // value: e.value, // ); // }, // ), // ), // ); return Wrap( spacing: 10, runSpacing: 10, alignment: WrapAlignment.center, children: pieCharts .map( (e) => SizedBox.square( dimension: 130, child: e, ), ) .toList(), ); } Color _segmentColor(int index) { if (materialColor == null) { final key = data.keys.elementAt(index); return perSegmentColor![key]!; } final colorIndex = index % materialColor!.colorListButFewer.length; return materialColor!.colorListButFewer[colorIndex]; } } class ThemedPieChartSingular extends ConsumerWidget { /// must provide either [materialColor] or [perSegmentColor] const ThemedPieChartSingular({ super.key, required this.data, this.materialColor, this.perSegmentColor, this.badgePositionPercentageOffset = 2, }); final Map data; /// key should match data final Map? perSegmentColor; final double badgePositionPercentageOffset; /// generates segment colors from [MaterialColor] /// do not provide if [perSegmentColor] is also provided final MaterialColor? materialColor; @override Widget build(BuildContext context, ref) { assert(!(perSegmentColor != null && materialColor != null)); final totalVal = data.values.fold( 0.0, (prev, e) => prev + e, ); final dataWithoutLessThan1PctSectors = data.entries .where( (emirateConsumption) => emirateConsumption.value.asPctOf(totalVal) > 1, ) .toList(); dataWithoutLessThan1PctSectors.insert( 1, dataWithoutLessThan1PctSectors .removeAt(dataWithoutLessThan1PctSectors.length - 1), ); final pieChart = PieChart( PieChartData( sections: List.generate( dataWithoutLessThan1PctSectors.length, (index) { final e = dataWithoutLessThan1PctSectors[index]; return PieChartSectionData( radius: 22, badgeWidget: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 100), child: Text( '${e.key}\n${e.value.toStringWithCommas( abbreviateIfAbove10powN: 5, )}', textAlign: TextAlign.center, style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontSize: 10, fontWeight: FontWeight.w600, ), ), ), badgePositionPercentageOffset: badgePositionPercentageOffset, title: '${((e.value / totalVal) * 100).toStringAsFixed(1)}%', titlePositionPercentageOffset: -0.6, titleStyle: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontSize: 10, fontWeight: FontWeight.w600, ), showTitle: true, color: _segmentColor(index), value: e.value, ); }, ), ), ); return Padding( padding: const EdgeInsets.all(20), child: pieChart, ); } Color _segmentColor(int index) { if (materialColor == null) { final key = data.keys.elementAt(index); return perSegmentColor![key]!; } final colorIndex = index % materialColor!.colorListButFewer.length; return materialColor!.colorListButFewer[colorIndex]; } }