Merge pull request #65 from flixcoo/feature/61-implement-graph-for-player-scores
Implement graph for player scores
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
"player": "Spieler:in",
|
||||
"players": "Spieler:innen",
|
||||
"name": "Name",
|
||||
"back": "Zurück",
|
||||
|
||||
"home": "Home",
|
||||
"about": "Über",
|
||||
@@ -63,6 +64,13 @@
|
||||
"done": "Fertig",
|
||||
"next_round": "Nächste Runde",
|
||||
|
||||
"statistics": "Statistiken",
|
||||
"delete_game": "Spiel löschen",
|
||||
"new_game_same_settings": "Neues Spiel mit gleichen Einstellungen",
|
||||
"export_game": "Spiel exportieren",
|
||||
|
||||
"game_process": "Spielverlauf",
|
||||
|
||||
"settings": "Einstellungen",
|
||||
"cabo_penalty": "Cabo-Strafe",
|
||||
"cabo_penalty_subtitle": "... für falsches Cabo sagen",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"player": "Player",
|
||||
"players": "Players",
|
||||
"name": "Name",
|
||||
"back": "Back",
|
||||
|
||||
"home": "Home",
|
||||
"about": "About",
|
||||
@@ -63,6 +64,13 @@
|
||||
"done": "Done",
|
||||
"next_round": "Next Round",
|
||||
|
||||
"statistics": "Statistics",
|
||||
"delete_game": "Delete Game",
|
||||
"new_game_same_settings": "New Game with same Settings",
|
||||
"export_game": "Export Game",
|
||||
|
||||
"game_process": "Spielverlauf",
|
||||
|
||||
"settings": "Settings",
|
||||
"cabo_penalty": "Cabo Penalty",
|
||||
"cabo_penalty_subtitle": "... for falsely calling Cabo.",
|
||||
@@ -80,5 +88,6 @@
|
||||
"app_version": "App Version",
|
||||
"load_version": "Loading version...",
|
||||
"build": "Build",
|
||||
|
||||
"about_text": "Hey :) Thanks for being one of the first users of my app! I’ve put a lot of work into this project, and even though I tried to think of everything, it might not work perfectly just yet. So if you discover any bugs or have feedback on the design or usability, please let me know via the TestFlight app or by sending me a message or email. Thank you very much!"
|
||||
}
|
||||
|
||||
@@ -176,6 +176,12 @@ abstract class AppLocalizations {
|
||||
/// **'Name'**
|
||||
String get name;
|
||||
|
||||
/// No description provided for @back.
|
||||
///
|
||||
/// In de, this message translates to:
|
||||
/// **'Zurück'**
|
||||
String get back;
|
||||
|
||||
/// No description provided for @home.
|
||||
///
|
||||
/// In de, this message translates to:
|
||||
@@ -356,6 +362,36 @@ abstract class AppLocalizations {
|
||||
/// **'Nächste Runde'**
|
||||
String get next_round;
|
||||
|
||||
/// No description provided for @statistics.
|
||||
///
|
||||
/// In de, this message translates to:
|
||||
/// **'Statistiken'**
|
||||
String get statistics;
|
||||
|
||||
/// No description provided for @delete_game.
|
||||
///
|
||||
/// In de, this message translates to:
|
||||
/// **'Spiel löschen'**
|
||||
String get delete_game;
|
||||
|
||||
/// No description provided for @new_game_same_settings.
|
||||
///
|
||||
/// In de, this message translates to:
|
||||
/// **'Neues Spiel mit gleichen Einstellungen'**
|
||||
String get new_game_same_settings;
|
||||
|
||||
/// No description provided for @export_game.
|
||||
///
|
||||
/// In de, this message translates to:
|
||||
/// **'Spiel exportieren'**
|
||||
String get export_game;
|
||||
|
||||
/// No description provided for @game_process.
|
||||
///
|
||||
/// In de, this message translates to:
|
||||
/// **'Spielverlauf'**
|
||||
String get game_process;
|
||||
|
||||
/// No description provided for @settings.
|
||||
///
|
||||
/// In de, this message translates to:
|
||||
|
||||
@@ -47,6 +47,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get name => 'Name';
|
||||
|
||||
@override
|
||||
String get back => 'Zurück';
|
||||
|
||||
@override
|
||||
String get home => 'Home';
|
||||
|
||||
@@ -146,6 +149,21 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get next_round => 'Nächste Runde';
|
||||
|
||||
@override
|
||||
String get statistics => 'Statistiken';
|
||||
|
||||
@override
|
||||
String get delete_game => 'Spiel löschen';
|
||||
|
||||
@override
|
||||
String get new_game_same_settings => 'Neues Spiel mit gleichen Einstellungen';
|
||||
|
||||
@override
|
||||
String get export_game => 'Spiel exportieren';
|
||||
|
||||
@override
|
||||
String get game_process => 'Spielverlauf';
|
||||
|
||||
@override
|
||||
String get settings => 'Einstellungen';
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get name => 'Name';
|
||||
|
||||
@override
|
||||
String get back => 'Back';
|
||||
|
||||
@override
|
||||
String get home => 'Home';
|
||||
|
||||
@@ -58,7 +61,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get empty_text_2 =>
|
||||
'Add a new round using the button in the top right corner';
|
||||
'Add a new round using the button in the top right corner.';
|
||||
|
||||
@override
|
||||
String get delete_game_title => 'Delete game?';
|
||||
@@ -143,6 +146,21 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get next_round => 'Next Round';
|
||||
|
||||
@override
|
||||
String get statistics => 'Statistics';
|
||||
|
||||
@override
|
||||
String get delete_game => 'Delete Game';
|
||||
|
||||
@override
|
||||
String get new_game_same_settings => 'New Game with same Settings';
|
||||
|
||||
@override
|
||||
String get export_game => 'Export Game';
|
||||
|
||||
@override
|
||||
String get game_process => 'Spielverlauf';
|
||||
|
||||
@override
|
||||
String get settings => 'Settings';
|
||||
|
||||
@@ -150,13 +168,13 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get cabo_penalty => 'Cabo Penalty';
|
||||
|
||||
@override
|
||||
String get cabo_penalty_subtitle => '... for falsely calling Cabo';
|
||||
String get cabo_penalty_subtitle => '... for falsely calling Cabo.';
|
||||
|
||||
@override
|
||||
String get point_limit => 'Point Limit';
|
||||
|
||||
@override
|
||||
String get point_limit_subtitle => '... the game ends here';
|
||||
String get point_limit_subtitle => '... the game ends here.';
|
||||
|
||||
@override
|
||||
String get reset_to_default => 'Reset to Default';
|
||||
@@ -196,5 +214,5 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get about_text =>
|
||||
'Hey :) Thanks for being one of the first users of my first app! I’ve put a lot of work into this project, and even though I (hopefully) thought of a lot, not everything will work 100% yet. So if you discover any bugs or have feedback on the design or usability, please let me know via the Testflight app or a message / email. Thank you very much!';
|
||||
'Hey :) Thanks for being one of the first users of my app! I’ve put a lot of work into this project, and even though I tried to think of everything, it might not work perfectly just yet. So if you discover any bugs or have feedback on the design or usability, please let me know via the TestFlight app or by sending me a message or email. Thank you very much!';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:cabo_counter/data/game_session.dart';
|
||||
import 'package:cabo_counter/l10n/app_localizations.dart';
|
||||
import 'package:cabo_counter/utility/custom_theme.dart';
|
||||
import 'package:cabo_counter/views/graph_view.dart';
|
||||
import 'package:cabo_counter/views/round_view.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ActiveGameView extends StatefulWidget {
|
||||
final GameSession gameSession;
|
||||
@@ -80,6 +82,8 @@ class _ActiveGameViewState extends State<ActiveGameView> {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(1),
|
||||
child: CupertinoListTile(
|
||||
backgroundColorActivated:
|
||||
CustomTheme.backgroundColor,
|
||||
title: Text(
|
||||
'${AppLocalizations.of(context).round} ${index + 1}',
|
||||
),
|
||||
@@ -107,6 +111,51 @@ class _ActiveGameViewState extends State<ActiveGameView> {
|
||||
));
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
|
||||
child: Text(
|
||||
AppLocalizations.of(context).game,
|
||||
style: CustomTheme.rowTitle,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
CupertinoListTile(
|
||||
backgroundColorActivated:
|
||||
CustomTheme.backgroundColor,
|
||||
title: Text(
|
||||
AppLocalizations.of(context).statistics,
|
||||
),
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => GraphView(
|
||||
gameSession: widget.gameSession,
|
||||
)))),
|
||||
CupertinoListTile(
|
||||
title:
|
||||
Text(AppLocalizations.of(context).delete_game,
|
||||
style: const TextStyle(
|
||||
color: Colors.white30,
|
||||
)),
|
||||
onTap: () {},
|
||||
),
|
||||
CupertinoListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)
|
||||
.new_game_same_settings,
|
||||
style: const TextStyle(
|
||||
color: Colors.white30,
|
||||
))),
|
||||
CupertinoListTile(
|
||||
title:
|
||||
Text(AppLocalizations.of(context).export_game,
|
||||
style: const TextStyle(
|
||||
color: Colors.white30,
|
||||
)),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
84
lib/views/graph_view.dart
Normal file
84
lib/views/graph_view.dart
Normal file
@@ -0,0 +1,84 @@
|
||||
import 'package:cabo_counter/data/game_session.dart';
|
||||
import 'package:cabo_counter/l10n/app_localizations.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncfusion_flutter_charts/charts.dart';
|
||||
|
||||
class GraphView extends StatefulWidget {
|
||||
final GameSession gameSession;
|
||||
|
||||
const GraphView({super.key, required this.gameSession});
|
||||
|
||||
@override
|
||||
State<GraphView> createState() => _GraphViewState();
|
||||
}
|
||||
|
||||
class _GraphViewState extends State<GraphView> {
|
||||
/// List of colors for the graph lines.
|
||||
List<Color> lineColors = [
|
||||
Colors.red,
|
||||
Colors.blue,
|
||||
Colors.orange.shade400,
|
||||
Colors.purple,
|
||||
Colors.green,
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text(AppLocalizations.of(context).game_process),
|
||||
previousPageTitle: AppLocalizations.of(context).back,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 100, 0, 0),
|
||||
child: SfCartesianChart(
|
||||
legend:
|
||||
const Legend(isVisible: true, position: LegendPosition.bottom),
|
||||
primaryXAxis: const NumericAxis(),
|
||||
primaryYAxis: const NumericAxis(),
|
||||
series: getCumulativeScores(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns a list of LineSeries representing the cumulative scores of each player.
|
||||
/// Each series contains data points for each round, showing the cumulative score up to that round.
|
||||
/// The x-axis represents the round number, and the y-axis represents the cumulative score.
|
||||
List<LineSeries<(int, int), int>> getCumulativeScores() {
|
||||
final rounds = widget.gameSession.roundList;
|
||||
final playerCount = widget.gameSession.players.length;
|
||||
final playerNames = widget.gameSession.players;
|
||||
|
||||
List<List<int>> cumulativeScores = List.generate(playerCount, (_) => []);
|
||||
List<int> runningTotals = List.filled(playerCount, 0);
|
||||
|
||||
for (var round in rounds) {
|
||||
for (int i = 0; i < playerCount; i++) {
|
||||
runningTotals[i] += round.scores[i];
|
||||
cumulativeScores[i].add(runningTotals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a list of LineSeries for each player
|
||||
/// Each series contains data points for each round
|
||||
return List.generate(playerCount, (i) {
|
||||
final data = List.generate(
|
||||
cumulativeScores[i].length,
|
||||
(j) => (j + 1, cumulativeScores[i][j]), // (round, score)
|
||||
);
|
||||
|
||||
/// Create a LineSeries for the player
|
||||
/// The xValueMapper maps the round number, and the yValueMapper maps the cumulative score.
|
||||
return LineSeries<(int, int), int>(
|
||||
name: playerNames[i],
|
||||
dataSource: data,
|
||||
xValueMapper: (record, _) => record.$1, // Runde
|
||||
yValueMapper: (record, _) => record.$2, // Punktestand
|
||||
markerSettings: const MarkerSettings(isVisible: true),
|
||||
color: lineColors[i],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ name: cabo_counter
|
||||
description: "Mobile app for the card game Cabo"
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.3.1+237
|
||||
version: 0.3.2+244
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.4
|
||||
@@ -25,6 +25,7 @@ dependencies:
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: any
|
||||
syncfusion_flutter_charts: ^30.1.37
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user