MVP #141

Merged
flixcoo merged 705 commits from development into main 2026-01-09 12:55:50 +00:00
Showing only changes of commit e60961730f - Show all commits

View File

@@ -16,9 +16,9 @@ class StatisticsView extends StatefulWidget {
class _StatisticsViewState extends State<StatisticsView> { class _StatisticsViewState extends State<StatisticsView> {
late Future<List<Game>> _gamesFuture; late Future<List<Game>> _gamesFuture;
late Future<List<Player>> _playersFuture; late Future<List<Player>> _playersFuture;
List<(String, int)> winCounts = List.filled(6, ('Skeleton Player', 5)); List<(String, int)> winCounts = List.filled(6, ('Skeleton Player', 1));
List<(String, int)> gameCounts = List.filled(6, ('Skeleton Player', 5)); List<(String, int)> gameCounts = List.filled(6, ('Skeleton Player', 1));
List<(String, double)> winRates = List.filled(6, ('Skeleton Player', 1));
bool isLoading = true; bool isLoading = true;
@override @override
@@ -29,11 +29,12 @@ class _StatisticsViewState extends State<StatisticsView> {
_playersFuture = db.playerDao.getAllPlayers(); _playersFuture = db.playerDao.getAllPlayers();
Future.wait([_gamesFuture, _playersFuture]).then((results) async { Future.wait([_gamesFuture, _playersFuture]).then((results) async {
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(const Duration(milliseconds: 200));
final games = results[0] as List<Game>; final games = results[0] as List<Game>;
final players = results[1] as List<Player>; final players = results[1] as List<Player>;
winCounts = _calculateWinsForAllPlayers(games, players); winCounts = _calculateWinsForAllPlayers(games, players);
gameCounts = _calculateGameAmountsForAllPlayers(games, players); gameCounts = _calculateGameAmountsForAllPlayers(games, players);
winRates = computeWinRatePercent(wins: winCounts, games: gameCounts);
if (mounted) { if (mounted) {
setState(() { setState(() {
isLoading = false; isLoading = false;
@@ -46,7 +47,8 @@ class _StatisticsViewState extends State<StatisticsView> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder( return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) { builder: (BuildContext context, BoxConstraints constraints) {
return Skeletonizer( return SingleChildScrollView(
child: Skeletonizer(
effect: PulseEffect( effect: PulseEffect(
from: Colors.grey[800]!, from: Colors.grey[800]!,
to: Colors.grey[600]!, to: Colors.grey[600]!,
@@ -54,14 +56,22 @@ class _StatisticsViewState extends State<StatisticsView> {
), ),
enabled: isLoading, enabled: isLoading,
enableSwitchAnimation: true, enableSwitchAnimation: true,
switchAnimationConfig: const SwitchAnimationConfig( switchAnimationConfig: SwitchAnimationConfig(
duration: Duration(milliseconds: 200), duration: const Duration(milliseconds: 1000),
switchInCurve: Curves.linear, switchInCurve: Curves.linear,
switchOutCurve: Curves.linear, switchOutCurve: Curves.linear,
transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder, transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder,
layoutBuilder: AnimatedSwitcher.defaultLayoutBuilder, layoutBuilder:
(Widget? currentChild, List<Widget> previousChildren) {
return Stack(
alignment: Alignment.topCenter,
children: [
...previousChildren,
if (currentChild != null) currentChild,
],
);
},
), ),
child: SingleChildScrollView(
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints(minWidth: constraints.maxWidth), constraints: BoxConstraints(minWidth: constraints.maxWidth),
child: Column( child: Column(
@@ -78,6 +88,15 @@ class _StatisticsViewState extends State<StatisticsView> {
barColor: Colors.blue, barColor: Colors.blue,
), ),
SizedBox(height: constraints.maxHeight * 0.02), SizedBox(height: constraints.maxHeight * 0.02),
StatisticsTile(
icon: Icons.casino,
title: 'Winrate per Player',
width: constraints.maxWidth * 0.95,
values: winRates,
itemCount: 6,
barColor: Colors.orange[700]!,
),
SizedBox(height: constraints.maxHeight * 0.02),
StatisticsTile( StatisticsTile(
icon: Icons.casino, icon: Icons.casino,
title: 'Games per Player', title: 'Games per Player',
@@ -86,6 +105,7 @@ class _StatisticsViewState extends State<StatisticsView> {
itemCount: 6, itemCount: 6,
barColor: Colors.green, barColor: Colors.green,
), ),
SizedBox(height: MediaQuery.paddingOf(context).bottom), SizedBox(height: MediaQuery.paddingOf(context).bottom),
], ],
), ),
@@ -200,4 +220,36 @@ class _StatisticsViewState extends State<StatisticsView> {
return gameCounts; return gameCounts;
} }
// dart
List<(String, double)> computeWinRatePercent({
required List<(String, int)> wins, // [(name, wins)]
required List<(String, int)> games, // [(name, games)]
}) {
final Map<String, int> winsMap = {for (var e in wins) e.$1: e.$2};
final Map<String, int> gamesMap = {for (var e in games) e.$1: e.$2};
final names = {...winsMap.keys, ...gamesMap.keys};
final result = names.map((name) {
final int w = winsMap[name] ?? 0;
final int g = gamesMap[name] ?? 0;
final double percent = (g > 0)
? double.parse(((w / g)).toStringAsFixed(2))
: 0;
return (name, percent);
}).toList();
// Sort the result: first by winrate descending,
// then by wins descending in case of a tie
result.sort((a, b) {
final cmp = b.$2.compareTo(a.$2);
if (cmp != 0) return cmp;
final wa = winsMap[a.$1] ?? 0;
final wb = winsMap[b.$1] ?? 0;
return wb.compareTo(wa);
});
return result;
}
} }