diff --git a/lib/core/custom_theme.dart b/lib/core/custom_theme.dart index a00340b..fa78cb6 100644 --- a/lib/core/custom_theme.dart +++ b/lib/core/custom_theme.dart @@ -13,6 +13,10 @@ class CustomTheme { static const Color graphColor4 = Color(0xFF9C27B0); static final Color graphColor5 = primaryColor; + // Colors for PointsView + static Color pointLossColor = primaryColor; + static const Color pointGainColor = Color(0xFFF44336); + static TextStyle modeTitle = TextStyle( color: primaryColor, fontSize: 20, diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 93215ed..399aefd 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -92,7 +92,6 @@ } }, - "end_game": "Spiel beenden", "delete_game": "Spiel löschen", "new_game_same_settings": "Neues Spiel mit gleichen Einstellungen", @@ -102,7 +101,9 @@ "end_game_title": "Spiel beenden?", "end_game_message": "Möchtest du das Spiel beenden? Das Spiel wird als beendet markiert und kann nicht fortgeführt werden.", - "game_process": "Spielverlauf", + "statistics": "Statistiken", + "point_overview": "Punkteübersicht", + "scoring_history": "Spielverlauf", "empty_graph_text": "Du musst mindestens eine Runde spielen, damit der Graph des Spielverlaufes angezeigt werden kann.", "settings": "Einstellungen", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 8a327a5..3b6150a 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -92,7 +92,6 @@ } }, - "end_game": "End Game", "delete_game": "Delete Game", "new_game_same_settings": "New Game with same Settings", @@ -102,7 +101,9 @@ "end_game_title": "End the game?", "end_game_message": "Do you want to end the game? The game gets marked as finished and cannot be continued.", - "game_process": "Scoring History", + "statistics": "Statistics", + "point_overview": "Point Overview", + "scoring_history": "Scoring History", "empty_graph_text": "You must play at least one round for the game progress graph to be displayed.", "settings": "Settings", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 0a902f6..7ce3c52 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -477,11 +477,23 @@ abstract class AppLocalizations { /// **'Möchtest du das Spiel beenden? Das Spiel wird als beendet markiert und kann nicht fortgeführt werden.'** String get end_game_message; - /// No description provided for @game_process. + /// No description provided for @statistics. + /// + /// In de, this message translates to: + /// **'Statistiken'** + String get statistics; + + /// No description provided for @point_overview. + /// + /// In de, this message translates to: + /// **'Punkteübersicht'** + String get point_overview; + + /// No description provided for @scoring_history. /// /// In de, this message translates to: /// **'Spielverlauf'** - String get game_process; + String get scoring_history; /// No description provided for @empty_graph_text. /// diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 7a71d00..6539c20 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -222,7 +222,13 @@ class AppLocalizationsDe extends AppLocalizations { 'Möchtest du das Spiel beenden? Das Spiel wird als beendet markiert und kann nicht fortgeführt werden.'; @override - String get game_process => 'Spielverlauf'; + String get statistics => 'Statistiken'; + + @override + String get point_overview => 'Punkteübersicht'; + + @override + String get scoring_history => 'Spielverlauf'; @override String get empty_graph_text => diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 440d9cd..7e026f6 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -220,7 +220,13 @@ class AppLocalizationsEn extends AppLocalizations { 'Do you want to end the game? The game gets marked as finished and cannot be continued.'; @override - String get game_process => 'Scoring History'; + String get statistics => 'Statistics'; + + @override + String get point_overview => 'Point Overview'; + + @override + String get scoring_history => 'Scoring History'; @override String get empty_graph_text => diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index ab07804..defe5fc 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -4,6 +4,7 @@ import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/create_game_view.dart'; import 'package:cabo_counter/presentation/views/graph_view.dart'; +import 'package:cabo_counter/presentation/views/points_view.dart'; import 'package:cabo_counter/presentation/views/round_view.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:flutter/cupertino.dart'; @@ -117,7 +118,7 @@ class _ActiveGameViewState extends State { Padding( padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), child: Text( - AppLocalizations.of(context).game, + AppLocalizations.of(context).statistics, style: CustomTheme.rowTitle, ), ), @@ -125,7 +126,7 @@ class _ActiveGameViewState extends State { children: [ CupertinoListTile( title: Text( - AppLocalizations.of(context).game_process, + AppLocalizations.of(context).scoring_history, ), backgroundColorActivated: CustomTheme.backgroundColor, @@ -135,6 +136,29 @@ class _ActiveGameViewState extends State { builder: (_) => GraphView( gameSession: gameSession, )))), + CupertinoListTile( + title: Text( + AppLocalizations.of(context).point_overview, + ), + backgroundColorActivated: + CustomTheme.backgroundColor, + onTap: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (_) => PointsView( + gameSession: gameSession, + )))), + ], + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).game, + style: CustomTheme.rowTitle, + ), + ), + Column( + children: [ Visibility( visible: !gameSession.isPointsLimitEnabled, child: CupertinoListTile( diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index d322bd0..23137cd 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -27,24 +27,36 @@ class _GraphViewState extends State { Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( - middle: Text(AppLocalizations.of(context).game_process), + middle: Text(AppLocalizations.of(context).scoring_history), previousPageTitle: AppLocalizations.of(context).back, ), child: widget.gameSession.roundNumber > 1 ? Padding( padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), child: SfCartesianChart( + enableAxisAnimation: true, legend: const Legend( overflowMode: LegendItemOverflowMode.wrap, isVisible: true, position: LegendPosition.bottom), primaryXAxis: const NumericAxis( + labelStyle: TextStyle(fontWeight: FontWeight.bold), interval: 1, decimalPlaces: 0, ), - primaryYAxis: const NumericAxis( + primaryYAxis: NumericAxis( + labelStyle: const TextStyle(fontWeight: FontWeight.bold), + labelAlignment: LabelAlignment.center, + labelPosition: ChartDataLabelPosition.inside, interval: 1, decimalPlaces: 0, + axisLabelFormatter: (AxisLabelRenderDetails details) { + if (details.value == 0) { + return ChartAxisLabel('', const TextStyle()); + } + return ChartAxisLabel( + '${details.value.toInt()}', const TextStyle()); + }, ), series: getCumulativeScores(), ), diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 1a818b6..a80664d 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -118,8 +118,15 @@ class _MainMenuViewState extends State { ), ], ) - : ListView.builder( + : ListView.separated( itemCount: gameManager.gameList.length, + separatorBuilder: (context, index) => Divider( + height: 1, + thickness: 0.5, + color: CustomTheme.white.withAlpha(50), + indent: 50, + endIndent: 50, + ), itemBuilder: (context, index) { final session = gameManager.gameList[index]; return ListenableBuilder( diff --git a/lib/presentation/views/points_view.dart b/lib/presentation/views/points_view.dart new file mode 100644 index 0000000..1379785 --- /dev/null +++ b/lib/presentation/views/points_view.dart @@ -0,0 +1,141 @@ +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/data/game_session.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class PointsView extends StatefulWidget { + final GameSession gameSession; + + const PointsView({super.key, required this.gameSession}); + + @override + State createState() => _PointsViewState(); +} + +class _PointsViewState extends State { + @override + Widget build(BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text(AppLocalizations.of(context).point_overview), + previousPageTitle: AppLocalizations.of(context).back, + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: DataTable( + dataRowMinHeight: 60, + dataRowMaxHeight: 60, + dividerThickness: 0.5, + columnSpacing: 20, + columns: [ + const DataColumn( + numeric: true, + headingRowAlignment: MainAxisAlignment.center, + label: Text( + '#', + style: TextStyle(fontWeight: FontWeight.bold), + ), + columnWidth: IntrinsicColumnWidth(flex: 0.5)), + ...widget.gameSession.players.map( + (player) => DataColumn( + label: FittedBox( + fit: BoxFit.fill, + child: Text( + player, + style: const TextStyle(fontWeight: FontWeight.bold), + )), + headingRowAlignment: MainAxisAlignment.center, + columnWidth: const IntrinsicColumnWidth(flex: 1)), + ), + ], + rows: [ + ...List.generate( + widget.gameSession.roundList.length, + (roundIndex) { + final round = widget.gameSession.roundList[roundIndex]; + return DataRow( + cells: [ + DataCell(Align( + alignment: Alignment.center, + child: Text( + '${roundIndex + 1}', + style: const TextStyle(fontSize: 20), + ), + )), + ...List.generate(widget.gameSession.players.length, + (playerIndex) { + final int score = round.scores[playerIndex]; + final int update = round.scoreUpdates[playerIndex]; + final bool saidCabo = + round.caboPlayerIndex == playerIndex; + return DataCell( + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: update <= 0 + ? CustomTheme.pointLossColor + : CustomTheme.pointGainColor, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + '${update >= 0 ? '+' : ''}$update', + style: const TextStyle( + color: CupertinoColors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(height: 4), + Text('$score', + style: TextStyle( + fontWeight: saidCabo + ? FontWeight.bold + : FontWeight.normal, + )), + ], + ), + ), + ); + }), + ], + ); + }, + ), + DataRow( + cells: [ + const DataCell(Align( + alignment: Alignment.center, + child: Text( + 'Σ', + style: + TextStyle(fontSize: 25, fontWeight: FontWeight.bold), + ), + )), + ...widget.gameSession.playerScores.map( + (score) => DataCell( + Center( + child: Text( + '$score', + style: const TextStyle( + fontSize: 20, fontWeight: FontWeight.bold), + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index a29cba1..7f8483c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.7+512 +version: 0.4.8+525 environment: sdk: ^3.5.4