253 lines
7.0 KiB
Dart
Executable File
253 lines
7.0 KiB
Dart
Executable File
import 'dart:math';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import 'package:uae_stat/domain/use_cases/language.dart';
|
|
import 'package:uae_stat/infrastructure/services/packages/list.dart';
|
|
import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart';
|
|
import 'package:uae_stat/presentation/components/space.dart';
|
|
|
|
class ThemedDistributionChart extends ConsumerWidget {
|
|
const ThemedDistributionChart(
|
|
this.data, {
|
|
required this.leftColor,
|
|
required this.middleText,
|
|
required this.rightColor,
|
|
this.doSort = true,
|
|
this.labelBuilder,
|
|
super.key,
|
|
});
|
|
|
|
final bool doSort;
|
|
final String middleText;
|
|
final Map<String, ({num left, num right})> data;
|
|
final Color leftColor;
|
|
final Color rightColor;
|
|
final String Function(bool isStart, num value)? labelBuilder;
|
|
|
|
Map<String, ({num left, num right})> calculateSortedData() {
|
|
final sortedEntries = data.entries.toList()
|
|
..sort(
|
|
(a, b) {
|
|
final aSum = a.value.left + a.value.right;
|
|
final bSum = b.value.left + b.value.right;
|
|
return bSum.compareTo(aSum);
|
|
},
|
|
);
|
|
return Map.fromEntries(sortedEntries);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final sortedDataIfAskedElseUnsorted = doSort ? calculateSortedData() : data;
|
|
final maxVal = data.values.fold<num>(
|
|
double.negativeInfinity,
|
|
(prev, vals) {
|
|
final localMax = max(vals.left, vals.right);
|
|
return localMax > prev ? localMax : prev;
|
|
},
|
|
);
|
|
const textSpace = 37.0;
|
|
final left = Column(
|
|
children: sortedDataIfAskedElseUnsorted.values.map<Widget>(
|
|
(e) {
|
|
final bar = FractionallySizedBox(
|
|
widthFactor: e.left / maxVal,
|
|
child: DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
color: leftColor,
|
|
borderRadius: const BorderRadiusDirectional.horizontal(
|
|
start: Radius.circular(100),
|
|
),
|
|
),
|
|
child: double.infinity.verticalSpace,
|
|
),
|
|
);
|
|
final text = Text(
|
|
labelBuilder == null
|
|
? e.left.toStringWithCommas()
|
|
: labelBuilder!(true, e.left),
|
|
textAlign: TextAlign.end,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
);
|
|
final segment = Expanded(
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
SizedBox(
|
|
width: textSpace,
|
|
child: text,
|
|
),
|
|
4.horizontalSpace,
|
|
Flexible(
|
|
child: bar,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return segment;
|
|
},
|
|
).toList()
|
|
..intersperseDivider(
|
|
8.verticalSpace,
|
|
)
|
|
..insert(
|
|
0,
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 4),
|
|
child: Text(
|
|
'Male',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
final right = Column(
|
|
children: sortedDataIfAskedElseUnsorted.values.map<Widget>(
|
|
(e) {
|
|
final bar = FractionallySizedBox(
|
|
widthFactor: e.right / maxVal,
|
|
child: DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
color: rightColor,
|
|
borderRadius: const BorderRadiusDirectional.horizontal(
|
|
end: Radius.circular(100),
|
|
),
|
|
),
|
|
child: double.infinity.verticalSpace,
|
|
),
|
|
);
|
|
final text = Text(
|
|
labelBuilder == null
|
|
? e.right.toStringWithCommas()
|
|
: labelBuilder!(true, e.right),
|
|
textAlign: TextAlign.start,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
);
|
|
final segment = Expanded(
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Flexible(
|
|
child: bar,
|
|
),
|
|
4.horizontalSpace,
|
|
SizedBox(
|
|
width: textSpace,
|
|
child: text,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return segment;
|
|
},
|
|
).toList()
|
|
..intersperseDivider(
|
|
8.verticalSpace,
|
|
)
|
|
..insert(
|
|
0,
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 4),
|
|
child: Text(
|
|
'Female',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
final legend = Column(
|
|
children: sortedDataIfAskedElseUnsorted.keys
|
|
.map<Widget>(
|
|
(e) => Expanded(
|
|
child: Center(
|
|
child: Text(
|
|
e,
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w900,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
.toList()
|
|
..intersperseDivider(
|
|
8.verticalSpace,
|
|
)
|
|
..insert(
|
|
0,
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 4),
|
|
child: Text(
|
|
middleText,
|
|
textAlign: TextAlign.center,
|
|
maxLines: 1,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: left,
|
|
),
|
|
ConstrainedBox(
|
|
constraints: const BoxConstraints(maxWidth: 60),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 6,
|
|
),
|
|
child: legend,
|
|
),
|
|
),
|
|
Expanded(
|
|
child: right,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|