Files
sendico/frontend/pweb/lib/pages/report/charts/distribution.dart
2025-11-25 08:20:09 +03:00

104 lines
3.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:pshared/models/payment/operation.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
class PayoutDistributionChart extends StatelessWidget {
final List<OperationItem> operations;
const PayoutDistributionChart({super.key, required this.operations});
@override
Widget build(BuildContext context) {
// 1) Aggregate sums
final sums = <String, double>{};
for (var op in operations) {
final name = op.name;
sums[name] = (sums[name] ?? 0) + op.amount;
}
if (sums.isEmpty) {
return Center(child: Text(AppLocalizations.of(context)!.noPayouts));
}
// 2) Build chart data
final data = sums.entries
.map((e) => _ChartData(e.key, e.value))
.toList();
// 3) Build a simple horizontal legend
final palette = [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary,
Theme.of(context).colorScheme.tertiary,
Theme.of(context).colorScheme.primaryContainer,
Theme.of(context).colorScheme.secondaryContainer,
];
final legendItems = List<Widget>.generate(data.length, (i) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.circle, size: 10, color: palette[i % palette.length]),
const SizedBox(width: 4),
Text(data[i].label, style: Theme.of(context).textTheme.bodySmall),
if (i < data.length - 1) const SizedBox(width: 12),
],
);
});
return Card(
margin: const EdgeInsets.all(16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
// Pie takes 2/3 of the width
Expanded(
flex: 2,
child: SfCircularChart(
legend: Legend(isVisible: false),
tooltipBehavior: TooltipBehavior(enable: true),
series: <PieSeries<_ChartData, String>>[
PieSeries<_ChartData, String>(
dataSource: data,
xValueMapper: (d, _) => d.label,
yValueMapper: (d, _) => d.value,
dataLabelMapper: (d, _) =>
'${(d.value / sums.values.fold(0, (a, b) => a + b) * 100).toStringAsFixed(1)}%',
dataLabelSettings: const DataLabelSettings(
isVisible: true,
labelPosition: ChartDataLabelPosition.inside,
),
radius: '100%',
)
],
),
),
const SizedBox(width: 16),
// Legend takes 1/3
Expanded(
flex: 1,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(spacing: 4.0, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: legendItems),
),
),
],
),
),
);
}
}
class _ChartData {
final String label;
final double value;
_ChartData(this.label, this.value);
}