From 76ce3af6437690ae004fae3c909f77539859a380 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sun, 11 Jan 2026 15:26:10 +0100 Subject: [PATCH 01/15] implement custom alert dialog --- .../views/main_menu/settings_view.dart | 11 +++---- .../widgets/custom_alert_dialog.dart | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 lib/presentation/widgets/custom_alert_dialog.dart diff --git a/lib/presentation/views/main_menu/settings_view.dart b/lib/presentation/views/main_menu/settings_view.dart index 0fa7085..d416deb 100644 --- a/lib/presentation/views/main_menu/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view.dart @@ -5,6 +5,7 @@ import 'package:game_tracker/l10n/generated/app_localizations.dart'; import 'package:game_tracker/presentation/widgets/tiles/settings_list_tile.dart'; import 'package:game_tracker/services/data_transfer_service.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:game_tracker/presentation/widgets/custom_alert_dialog.dart'; class SettingsView extends StatefulWidget { const SettingsView({super.key}); @@ -92,17 +93,17 @@ class _SettingsViewState extends State { onPressed: () { showDialog( context: context, - builder: (context) => AlertDialog( - title: Text('${loc.delete_all_data}?'), - content: Text(loc.this_cannot_be_undone), + builder: (context) => CustomAlertDialog( + title: '${loc.delete_all_data}?', + content: loc.this_cannot_be_undone, actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), - child: Text(loc.cancel), + child: Text(loc.cancel, style: TextStyle(color: CustomTheme.textColor),), ), TextButton( onPressed: () => Navigator.of(context).pop(true), - child: Text(loc.delete), + child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor),), ), ], ), diff --git a/lib/presentation/widgets/custom_alert_dialog.dart b/lib/presentation/widgets/custom_alert_dialog.dart new file mode 100644 index 0000000..6a6019a --- /dev/null +++ b/lib/presentation/widgets/custom_alert_dialog.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:game_tracker/core/custom_theme.dart'; + +class CustomAlertDialog extends StatelessWidget { + final String title; + final String content; + final List actions; + + const CustomAlertDialog({ + super.key, + required this.title, + required this.content, + required this.actions, + }); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(title, style: const TextStyle(color: CustomTheme.textColor,),), + content: Text(content, style: const TextStyle(color: CustomTheme.textColor),), + actions: actions, + backgroundColor: CustomTheme.boxColor, + shape: RoundedRectangleBorder( + borderRadius: CustomTheme.standardBorderRadiusAll, + side: BorderSide(color: CustomTheme.boxBorder), + ), + ); + } +} From 3ceae8341b56461d6d376d54f7fa57afa481cb79 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sun, 11 Jan 2026 15:26:50 +0100 Subject: [PATCH 02/15] add const --- lib/presentation/views/main_menu/settings_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/main_menu/settings_view.dart b/lib/presentation/views/main_menu/settings_view.dart index d416deb..500a550 100644 --- a/lib/presentation/views/main_menu/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view.dart @@ -99,7 +99,7 @@ class _SettingsViewState extends State { actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), - child: Text(loc.cancel, style: TextStyle(color: CustomTheme.textColor),), + child: Text(loc.cancel, style: const TextStyle(color: CustomTheme.textColor),), ), TextButton( onPressed: () => Navigator.of(context).pop(true), From 22ce742d4333a80d9b759de160591245b98ddbfa Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sun, 11 Jan 2026 17:14:28 +0100 Subject: [PATCH 03/15] change button alignment & remove InkWell Animation --- lib/presentation/views/main_menu/settings_view.dart | 9 +++++++++ lib/presentation/widgets/custom_alert_dialog.dart | 1 + 2 files changed, 10 insertions(+) diff --git a/lib/presentation/views/main_menu/settings_view.dart b/lib/presentation/views/main_menu/settings_view.dart index 500a550..5814537 100644 --- a/lib/presentation/views/main_menu/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view.dart @@ -98,13 +98,22 @@ class _SettingsViewState extends State { content: loc.this_cannot_be_undone, actions: [ TextButton( + style: TextButton.styleFrom( + splashFactory: NoSplash.splashFactory, + overlayColor: Colors.transparent, + ), onPressed: () => Navigator.of(context).pop(false), child: Text(loc.cancel, style: const TextStyle(color: CustomTheme.textColor),), ), TextButton( + style: TextButton.styleFrom( + splashFactory: NoSplash.splashFactory, + overlayColor: Colors.transparent, + ), onPressed: () => Navigator.of(context).pop(true), child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor),), ), + ], ), ).then((confirmed) { diff --git a/lib/presentation/widgets/custom_alert_dialog.dart b/lib/presentation/widgets/custom_alert_dialog.dart index 6a6019a..2456b56 100644 --- a/lib/presentation/widgets/custom_alert_dialog.dart +++ b/lib/presentation/widgets/custom_alert_dialog.dart @@ -20,6 +20,7 @@ class CustomAlertDialog extends StatelessWidget { content: Text(content, style: const TextStyle(color: CustomTheme.textColor),), actions: actions, backgroundColor: CustomTheme.boxColor, + actionsAlignment: MainAxisAlignment.spaceAround, shape: RoundedRectangleBorder( borderRadius: CustomTheme.standardBorderRadiusAll, side: BorderSide(color: CustomTheme.boxBorder), From 1ebcfc9e57e1685fe75a0c2438ab297c8de2f0c9 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sun, 11 Jan 2026 17:21:05 +0100 Subject: [PATCH 04/15] implement animation --- .../views/main_menu/settings_view.dart | 73 +++++++++++++++---- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view.dart b/lib/presentation/views/main_menu/settings_view.dart index 5814537..cab26ef 100644 --- a/lib/presentation/views/main_menu/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view.dart @@ -97,23 +97,14 @@ class _SettingsViewState extends State { title: '${loc.delete_all_data}?', content: loc.this_cannot_be_undone, actions: [ - TextButton( - style: TextButton.styleFrom( - splashFactory: NoSplash.splashFactory, - overlayColor: Colors.transparent, - ), + _PressableButton( onPressed: () => Navigator.of(context).pop(false), - child: Text(loc.cancel, style: const TextStyle(color: CustomTheme.textColor),), + child: Text(loc.cancel, style: const TextStyle(color: CustomTheme.textColor)), ), - TextButton( - style: TextButton.styleFrom( - splashFactory: NoSplash.splashFactory, - overlayColor: Colors.transparent, - ), + _PressableButton( onPressed: () => Navigator.of(context).pop(true), - child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor),), + child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor)), ), - ], ), ).then((confirmed) { @@ -227,3 +218,59 @@ class _SettingsViewState extends State { }); } } + +class _PressableButton extends StatefulWidget { + final VoidCallback onPressed; + final Widget child; + + const _PressableButton({required this.onPressed, required this.child}); + + @override + State<_PressableButton> createState() => _PressableButtonState(); +} + +class _PressableButtonState extends State<_PressableButton> { + bool _isPressed = false; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTapDown: (_) => setState(() => _isPressed = true), + onTapUp: (_) => setState(() => _isPressed = false), + onTapCancel: () => setState(() => _isPressed = false), + onTap: widget.onPressed, + child: AnimatedScale( + scale: _isPressed ? 0.95 : 1.0, + duration: const Duration(milliseconds: 100), + child: AnimatedOpacity( + opacity: _isPressed ? 0.6 : 1.0, + duration: const Duration(milliseconds: 100), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: widget.child, + ), + ), + ), + ); + } +} + + +/* + TextButton( + style: TextButton.styleFrom( + splashFactory: NoSplash.splashFactory, + overlayColor: Colors.transparent, + ), + onPressed: () => Navigator.of(context).pop(false), + child: Text(loc.cancel, style: const TextStyle(color: CustomTheme.textColor),), + ), + TextButton( + style: TextButton.styleFrom( + splashFactory: NoSplash.splashFactory, + overlayColor: Colors.transparent, + ), + onPressed: () => Navigator.of(context).pop(true), + child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor),), + ), + */ From d7f4b1c227f10c83b956e9db6724b3fdd180ec6c Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Tue, 13 Jan 2026 20:41:03 +0100 Subject: [PATCH 05/15] seperate button into widget & change to agreed design --- .../views/main_menu/settings_view.dart | 61 +------------------ .../buttons/animated_dialog_button.dart | 48 +++++++++++++++ 2 files changed, 51 insertions(+), 58 deletions(-) create mode 100644 lib/presentation/widgets/buttons/animated_dialog_button.dart diff --git a/lib/presentation/views/main_menu/settings_view.dart b/lib/presentation/views/main_menu/settings_view.dart index cab26ef..21b9d64 100644 --- a/lib/presentation/views/main_menu/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/enums.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart'; +import 'package:game_tracker/presentation/widgets/buttons/animated_dialog_button.dart'; import 'package:game_tracker/presentation/widgets/tiles/settings_list_tile.dart'; import 'package:game_tracker/services/data_transfer_service.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -97,11 +98,11 @@ class _SettingsViewState extends State { title: '${loc.delete_all_data}?', content: loc.this_cannot_be_undone, actions: [ - _PressableButton( + AnimatedDialogButton( onPressed: () => Navigator.of(context).pop(false), child: Text(loc.cancel, style: const TextStyle(color: CustomTheme.textColor)), ), - _PressableButton( + AnimatedDialogButton( onPressed: () => Navigator.of(context).pop(true), child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor)), ), @@ -218,59 +219,3 @@ class _SettingsViewState extends State { }); } } - -class _PressableButton extends StatefulWidget { - final VoidCallback onPressed; - final Widget child; - - const _PressableButton({required this.onPressed, required this.child}); - - @override - State<_PressableButton> createState() => _PressableButtonState(); -} - -class _PressableButtonState extends State<_PressableButton> { - bool _isPressed = false; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTapDown: (_) => setState(() => _isPressed = true), - onTapUp: (_) => setState(() => _isPressed = false), - onTapCancel: () => setState(() => _isPressed = false), - onTap: widget.onPressed, - child: AnimatedScale( - scale: _isPressed ? 0.95 : 1.0, - duration: const Duration(milliseconds: 100), - child: AnimatedOpacity( - opacity: _isPressed ? 0.6 : 1.0, - duration: const Duration(milliseconds: 100), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: widget.child, - ), - ), - ), - ); - } -} - - -/* - TextButton( - style: TextButton.styleFrom( - splashFactory: NoSplash.splashFactory, - overlayColor: Colors.transparent, - ), - onPressed: () => Navigator.of(context).pop(false), - child: Text(loc.cancel, style: const TextStyle(color: CustomTheme.textColor),), - ), - TextButton( - style: TextButton.styleFrom( - splashFactory: NoSplash.splashFactory, - overlayColor: Colors.transparent, - ), - onPressed: () => Navigator.of(context).pop(true), - child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor),), - ), - */ diff --git a/lib/presentation/widgets/buttons/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart new file mode 100644 index 0000000..cf49c19 --- /dev/null +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:game_tracker/core/custom_theme.dart'; + +/// A custom animated button widget that provides a scaling and opacity effect +/// when pressed. This widget is designed to be used in dialogs or other UI +/// components where a visually appealing button is required. +class AnimatedDialogButton extends StatefulWidget { + /// Callback function that is triggered when the button is pressed. + final VoidCallback onPressed; + + /// The child widget to be displayed inside the button, typically a text or icon. + final Widget child; + + /// Creates an instance of `AnimatedDialogButton`. + /// + /// The [onPressed] and [child] parameters are required. + const AnimatedDialogButton({required this.onPressed, required this.child}); + + @override + State createState() => _AnimatedDialogButtonState(); +} + +class _AnimatedDialogButtonState extends State { + bool _isPressed = false; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTapDown: (_) => setState(() => _isPressed = true), + onTapUp: (_) => setState(() => _isPressed = false), + onTapCancel: () => setState(() => _isPressed = false), + onTap: widget.onPressed, + child: AnimatedScale( + scale: _isPressed ? 0.95 : 1.0, + duration: const Duration(milliseconds: 100), + child: AnimatedOpacity( + opacity: _isPressed ? 0.6 : 1.0, + duration: const Duration(milliseconds: 100), + child: Container( + decoration: CustomTheme.standardBoxDecoration, + padding: EdgeInsets.symmetric(horizontal: 26, vertical: 6), + child: widget.child, + ), + ), + ), + ); + } +} From d662680a340371d9d901315394af533f1c23648d Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Tue, 13 Jan 2026 20:46:42 +0100 Subject: [PATCH 06/15] fix dart analysis errors --- lib/presentation/widgets/buttons/animated_dialog_button.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/widgets/buttons/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart index cf49c19..42104b2 100644 --- a/lib/presentation/widgets/buttons/animated_dialog_button.dart +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -14,7 +14,7 @@ class AnimatedDialogButton extends StatefulWidget { /// Creates an instance of `AnimatedDialogButton`. /// /// The [onPressed] and [child] parameters are required. - const AnimatedDialogButton({required this.onPressed, required this.child}); + const AnimatedDialogButton({super.key, required this.onPressed, required this.child}); @override State createState() => _AnimatedDialogButtonState(); @@ -38,7 +38,7 @@ class _AnimatedDialogButtonState extends State { duration: const Duration(milliseconds: 100), child: Container( decoration: CustomTheme.standardBoxDecoration, - padding: EdgeInsets.symmetric(horizontal: 26, vertical: 6), + padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 6), child: widget.child, ), ), From 4161e1e88bcede3643de9ed7ae8ef5a8c5f92dbb Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Tue, 13 Jan 2026 20:50:54 +0100 Subject: [PATCH 07/15] add docs to custom_alert_dialog.dart --- lib/presentation/widgets/custom_alert_dialog.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/presentation/widgets/custom_alert_dialog.dart b/lib/presentation/widgets/custom_alert_dialog.dart index 2456b56..5dc8b9a 100644 --- a/lib/presentation/widgets/custom_alert_dialog.dart +++ b/lib/presentation/widgets/custom_alert_dialog.dart @@ -1,9 +1,19 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; +/// A custom alert dialog widget that follows the application's design theme. +/// +/// This widget provides a styled alternative to the default Flutter AlertDialog, +/// with consistent colors, borders, and layout that match the app's custom theme. class CustomAlertDialog extends StatelessWidget { + /// The title text displayed at the top of the dialog. final String title; + + /// The main content text displayed in the body of the dialog. final String content; + + /// A list of action widgets (typically buttons) displayed at the bottom of the dialog. + /// These actions are horizontally spaced around the dialog's width. final List actions; const CustomAlertDialog({ From 82ad2b74f829e857a949cb607d77349d1b1bea6c Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Tue, 13 Jan 2026 21:16:59 +0100 Subject: [PATCH 08/15] refactor: enhance documentation and fix punctuation in localization strings --- lib/l10n/arb/app_de.arb | 2 +- lib/l10n/arb/app_en.arb | 2 +- lib/l10n/generated/app_localizations.dart | 2 +- lib/l10n/generated/app_localizations_de.dart | 2 +- lib/l10n/generated/app_localizations_en.dart | 2 +- .../buttons/animated_dialog_button.dart | 12 +++++------ .../widgets/custom_alert_dialog.dart | 20 +++++++++---------- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 6aee6ee..7091586 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -79,7 +79,7 @@ "stats": "Statistiken", "successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt", "there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht", - "this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden", + "this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden.", "today_at": "Heute um", "undo": "Rückgängig", "unknown_exception": "Unbekannter Fehler (siehe Konsole)", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index c311050..6eb7613 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -356,7 +356,7 @@ "stats": "Stats", "successfully_added_player": "Successfully added player {playerName}", "there_is_no_group_matching_your_search": "There is no group matching your search", - "this_cannot_be_undone": "This can't be undone", + "this_cannot_be_undone": "This can't be undone.", "today_at": "Today at", "undo": "Undo", "unknown_exception": "Unknown Exception (see console)", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 399dc85..e78d69b 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -575,7 +575,7 @@ abstract class AppLocalizations { /// Warning message for irreversible actions /// /// In en, this message translates to: - /// **'This can\'t be undone'** + /// **'This can\'t be undone.'** String get this_cannot_be_undone; /// Date format for today diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index f4d0f8c..8ecc0a7 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -262,7 +262,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get this_cannot_be_undone => - 'Dies kann nicht rückgängig gemacht werden'; + 'Dies kann nicht rückgängig gemacht werden.'; @override String get today_at => 'Heute um'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 6c4ac74..b911676 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -261,7 +261,7 @@ class AppLocalizationsEn extends AppLocalizations { 'There is no group matching your search'; @override - String get this_cannot_be_undone => 'This can\'t be undone'; + String get this_cannot_be_undone => 'This can\'t be undone.'; @override String get today_at => 'Today at'; diff --git a/lib/presentation/widgets/buttons/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart index 42104b2..c0ce560 100644 --- a/lib/presentation/widgets/buttons/animated_dialog_button.dart +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -4,18 +4,18 @@ import 'package:game_tracker/core/custom_theme.dart'; /// A custom animated button widget that provides a scaling and opacity effect /// when pressed. This widget is designed to be used in dialogs or other UI /// components where a visually appealing button is required. +/// +/// Parameters: +/// - [onPressed]: Callback function that is triggered when the button is pressed. +/// - [child]: The child widget to be displayed inside the button, typically a text or icon. class AnimatedDialogButton extends StatefulWidget { + const AnimatedDialogButton({super.key, required this.onPressed, required this.child}); + /// Callback function that is triggered when the button is pressed. final VoidCallback onPressed; - /// The child widget to be displayed inside the button, typically a text or icon. final Widget child; - /// Creates an instance of `AnimatedDialogButton`. - /// - /// The [onPressed] and [child] parameters are required. - const AnimatedDialogButton({super.key, required this.onPressed, required this.child}); - @override State createState() => _AnimatedDialogButtonState(); } diff --git a/lib/presentation/widgets/custom_alert_dialog.dart b/lib/presentation/widgets/custom_alert_dialog.dart index 5dc8b9a..832369a 100644 --- a/lib/presentation/widgets/custom_alert_dialog.dart +++ b/lib/presentation/widgets/custom_alert_dialog.dart @@ -5,17 +5,13 @@ import 'package:game_tracker/core/custom_theme.dart'; /// /// This widget provides a styled alternative to the default Flutter AlertDialog, /// with consistent colors, borders, and layout that match the app's custom theme. +/// +/// Parameters: +/// - [title]: The title text displayed at the top of the dialog. +/// - [content]: The main content text displayed in the body of the dialog. +/// - [actions]: A list of action widgets (typically buttons) displayed at the bottom +/// of the dialog. These actions are horizontally spaced around the dialog's width. class CustomAlertDialog extends StatelessWidget { - /// The title text displayed at the top of the dialog. - final String title; - - /// The main content text displayed in the body of the dialog. - final String content; - - /// A list of action widgets (typically buttons) displayed at the bottom of the dialog. - /// These actions are horizontally spaced around the dialog's width. - final List actions; - const CustomAlertDialog({ super.key, required this.title, @@ -23,6 +19,10 @@ class CustomAlertDialog extends StatelessWidget { required this.actions, }); + final String title; + final String content; + final List actions; + @override Widget build(BuildContext context) { return AlertDialog( From efdb5e036137a42f53b0be072615bf46192ee836 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 13 Jan 2026 21:44:38 +0100 Subject: [PATCH 09/15] Fixed snackbar --- .../settings_view/settings_view.dart | 394 ++++++++++-------- 1 file changed, 212 insertions(+), 182 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/settings_view.dart b/lib/presentation/views/main_menu/settings_view/settings_view.dart index 1843c90..e32696d 100644 --- a/lib/presentation/views/main_menu/settings_view/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view/settings_view.dart @@ -37,194 +37,223 @@ class _SettingsViewState extends State { Widget build(BuildContext context) { final loc = AppLocalizations.of(context); return ScaffoldMessenger( - child: Scaffold( - appBar: AppBar(backgroundColor: CustomTheme.backgroundColor), - backgroundColor: CustomTheme.backgroundColor, - body: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(left: 16, bottom: 10), - child: Text( - textAlign: TextAlign.start, - loc.settings, - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - ), - ), - ), - Padding( - padding: const EdgeInsets.only(left: 16, top: 10, bottom: 10), - child: Text( - textAlign: TextAlign.start, - loc.data, - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - ), - ), - ), - SettingsListTile( - title: loc.export_data, - icon: Icons.upload, - suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), - onPressed: () async { - final String json = - await DataTransferService.getAppDataAsJson(context); - final result = await DataTransferService.exportData( - json, - 'game_tracker-data', - ); - if (!context.mounted) return; - showExportSnackBar(context: context, result: result); - }, - ), - SettingsListTile( - title: loc.import_data, - icon: Icons.download, - suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), - onPressed: () async { - final result = await DataTransferService.importData(context); - if (!context.mounted) return; - showImportSnackBar(context: context, result: result); - }, - ), - SettingsListTile( - title: loc.delete_all_data, - icon: Icons.delete, - suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('${loc.delete_all_data}?'), - content: Text(loc.this_cannot_be_undone), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(false), - child: Text(loc.cancel), - ), - TextButton( - onPressed: () => Navigator.of(context).pop(true), - child: Text(loc.delete), - ), - ], + child: Builder( + builder: (scaffoldMessengerContext) { + return Scaffold( + appBar: AppBar(backgroundColor: CustomTheme.backgroundColor), + backgroundColor: CustomTheme.backgroundColor, + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, bottom: 10), + child: Text( + textAlign: TextAlign.start, + loc.settings, + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + ), ), - ).then((confirmed) { - if (confirmed == true && context.mounted) { - DataTransferService.deleteAllData(context); - showSnackbar( - context: context, - message: AppLocalizations.of( - context, - ).data_successfully_deleted, + ), + Padding( + padding: const EdgeInsets.only( + left: 16, + top: 10, + bottom: 10, + ), + child: Text( + textAlign: TextAlign.start, + loc.data, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ), + SettingsListTile( + title: loc.export_data, + icon: Icons.upload, + suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), + onPressed: () async { + final String json = + await DataTransferService.getAppDataAsJson( + scaffoldMessengerContext, + ); + final result = await DataTransferService.exportData( + json, + 'game_tracker-data', ); - } - }); - }, - ), - Padding( - padding: const EdgeInsets.only(left: 16, top: 10, bottom: 10), - child: Text( - textAlign: TextAlign.start, - loc.legal, - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, + if (!scaffoldMessengerContext.mounted) return; + showExportSnackBar( + context: scaffoldMessengerContext, + result: result, + ); + }, ), - ), - ), - SettingsListTile( - title: loc.licenses, - icon: Icons.insert_drive_file, - suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const LicensesView(), - ), - ); - }, - ), - SettingsListTile( - title: loc.legal_notice, - icon: Icons.account_balance_sharp, - suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), - onPressed: null, - ), - SettingsListTile( - title: loc.privacy_policy, - icon: Icons.gpp_good_rounded, - suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), - onPressed: null, - ), - Padding( - padding: const EdgeInsets.only(top: 30, bottom: 20), - child: Center( - child: Column( - spacing: 4, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 40, - children: [ - GestureDetector( - child: const Icon(Icons.language), - onTap: () => { - launchUrl(Uri.parse('https://liquid-dev.de')), - }, + SettingsListTile( + title: loc.import_data, + icon: Icons.download, + suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), + onPressed: () async { + final result = await DataTransferService.importData( + scaffoldMessengerContext, + ); + if (!scaffoldMessengerContext.mounted) return; + showImportSnackBar( + context: scaffoldMessengerContext, + result: result, + ); + }, + ), + SettingsListTile( + title: loc.delete_all_data, + icon: Icons.delete, + suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), + onPressed: () { + showDialog( + context: scaffoldMessengerContext, + builder: (dialogContext) => AlertDialog( + title: Text('${loc.delete_all_data}?'), + content: Text(loc.this_cannot_be_undone), + actions: [ + TextButton( + onPressed: () => + Navigator.of(dialogContext).pop(false), + child: Text(loc.cancel), ), - GestureDetector( - child: const FaIcon(FontAwesomeIcons.github), - onTap: () => { - launchUrl( - Uri.parse( - 'https://github.com/liquiddevelopmentde', - ), - ), - }, - ), - GestureDetector( - child: Icon( - Platform.isIOS - ? CupertinoIcons.mail_solid - : Icons.email, - ), - onTap: () => launchUrl( - Uri.parse('mailto:hi@liquid-dev.de'), - ), + TextButton( + onPressed: () => + Navigator.of(dialogContext).pop(true), + child: Text(loc.delete), ), ], ), - ), - Text( - '© ${DateFormat('yyyy').format(DateTime.now())} Liquid Development', - style: TextStyle( - color: Colors.grey.shade600, - fontSize: 14, - fontWeight: FontWeight.w600, - ), - ), - Text( - 'Version ${_packageInfo.version} (${_packageInfo.buildNumber})', - style: TextStyle( - color: Colors.grey.shade600, - fontSize: 14, - fontWeight: FontWeight.w600, - ), - ), - ], + ).then((confirmed) { + if (confirmed == true && + scaffoldMessengerContext.mounted) { + DataTransferService.deleteAllData( + scaffoldMessengerContext, + ); + showSnackbar( + context: scaffoldMessengerContext, + message: AppLocalizations.of( + scaffoldMessengerContext, + ).data_successfully_deleted, + ); + } + }); + }, ), - ), + Padding( + padding: const EdgeInsets.only( + left: 16, + top: 10, + bottom: 10, + ), + child: Text( + textAlign: TextAlign.start, + loc.legal, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + ), + SettingsListTile( + title: loc.licenses, + icon: Icons.insert_drive_file, + suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const LicensesView(), + ), + ); + }, + ), + SettingsListTile( + title: loc.legal_notice, + icon: Icons.account_balance_sharp, + suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), + onPressed: null, + ), + SettingsListTile( + title: loc.privacy_policy, + icon: Icons.gpp_good_rounded, + suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), + onPressed: null, + ), + Padding( + padding: const EdgeInsets.only(top: 30, bottom: 20), + child: Center( + child: Column( + spacing: 4, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 40, + children: [ + GestureDetector( + child: const Icon(Icons.language), + onTap: () => { + launchUrl( + Uri.parse('https://liquid-dev.de'), + ), + }, + ), + GestureDetector( + child: const FaIcon(FontAwesomeIcons.github), + onTap: () => { + launchUrl( + Uri.parse( + 'https://github.com/liquiddevelopmentde', + ), + ), + }, + ), + GestureDetector( + child: Icon( + Platform.isIOS + ? CupertinoIcons.mail_solid + : Icons.email, + ), + onTap: () => launchUrl( + Uri.parse('mailto:hi@liquid-dev.de'), + ), + ), + ], + ), + ), + Text( + '© ${DateFormat('yyyy').format(DateTime.now())} Liquid Development', + style: TextStyle( + color: Colors.grey.shade600, + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + Text( + 'Version ${_packageInfo.version} (${_packageInfo.buildNumber})', + style: TextStyle( + color: Colors.grey.shade600, + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + ], ), - ], - ), - ), + ), + ); + }, ), ); } @@ -285,10 +314,11 @@ class _SettingsViewState extends State { Duration duration = const Duration(seconds: 3), VoidCallback? action, }) { + if (!context.mounted) return; + final loc = AppLocalizations.of(context); - final messenger = ScaffoldMessenger.of(context); - messenger.hideCurrentSnackBar(); - messenger.showSnackBar( + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message, style: const TextStyle(color: Colors.white)), backgroundColor: CustomTheme.onBoxColor, From 7be0b96491c66f33750aeae1b6744afd1352b119 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 13 Jan 2026 21:45:22 +0100 Subject: [PATCH 10/15] Added hiding to prevent stacking snackbars --- lib/presentation/widgets/player_selection.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index c5bb5de..2b9bb50 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -295,6 +295,7 @@ class _PlayerSelectionState extends State { /// [message] - The message to display in the snackbar. void showSnackBarMessage(String message) { if (!context.mounted) return; + ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( backgroundColor: CustomTheme.boxColor, From 4019ed083ff75e6abecffcf6e9a2247f64a69401 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Tue, 13 Jan 2026 21:53:45 +0100 Subject: [PATCH 11/15] add background color option to AnimatedDialogButton --- .../main_menu/settings_view/settings_view.dart | 3 ++- .../widgets/buttons/animated_dialog_button.dart | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/settings_view.dart b/lib/presentation/views/main_menu/settings_view/settings_view.dart index 1304f4a..34381c4 100644 --- a/lib/presentation/views/main_menu/settings_view/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view/settings_view.dart @@ -111,7 +111,8 @@ class _SettingsViewState extends State { ), AnimatedDialogButton( onPressed: () => Navigator.of(context).pop(true), - child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor)), + child: Text(loc.delete, style: TextStyle(color: CustomTheme.textColor)), + backgroundColor: CustomTheme.secondaryColor, ), ], ), diff --git a/lib/presentation/widgets/buttons/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart index c0ce560..a93f7cf 100644 --- a/lib/presentation/widgets/buttons/animated_dialog_button.dart +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -8,13 +8,18 @@ import 'package:game_tracker/core/custom_theme.dart'; /// Parameters: /// - [onPressed]: Callback function that is triggered when the button is pressed. /// - [child]: The child widget to be displayed inside the button, typically a text or icon. +/// - [backgroundColor]: Optional background color for the button container. If null, uses the standard box color from CustomTheme. class AnimatedDialogButton extends StatefulWidget { - const AnimatedDialogButton({super.key, required this.onPressed, required this.child}); + const AnimatedDialogButton({ + super.key, + required this.onPressed, + required this.child, + this.backgroundColor, + }); - /// Callback function that is triggered when the button is pressed. final VoidCallback onPressed; - /// The child widget to be displayed inside the button, typically a text or icon. final Widget child; + final Color? backgroundColor; @override State createState() => _AnimatedDialogButtonState(); @@ -37,7 +42,11 @@ class _AnimatedDialogButtonState extends State { opacity: _isPressed ? 0.6 : 1.0, duration: const Duration(milliseconds: 100), child: Container( - decoration: CustomTheme.standardBoxDecoration, + decoration: widget.backgroundColor != null + ? CustomTheme.standardBoxDecoration.copyWith( + color: widget.backgroundColor, + ) + : CustomTheme.standardBoxDecoration, padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 6), child: widget.child, ), From db51990695d60144175ea136cb62dfafd17002f6 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Tue, 13 Jan 2026 22:17:38 +0100 Subject: [PATCH 12/15] Revert "add background color option to AnimatedDialogButton" This reverts commit 4019ed083ff75e6abecffcf6e9a2247f64a69401. --- .../main_menu/settings_view/settings_view.dart | 3 +-- .../widgets/buttons/animated_dialog_button.dart | 17 ++++------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/settings_view.dart b/lib/presentation/views/main_menu/settings_view/settings_view.dart index 34381c4..1304f4a 100644 --- a/lib/presentation/views/main_menu/settings_view/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view/settings_view.dart @@ -111,8 +111,7 @@ class _SettingsViewState extends State { ), AnimatedDialogButton( onPressed: () => Navigator.of(context).pop(true), - child: Text(loc.delete, style: TextStyle(color: CustomTheme.textColor)), - backgroundColor: CustomTheme.secondaryColor, + child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor)), ), ], ), diff --git a/lib/presentation/widgets/buttons/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart index a93f7cf..c0ce560 100644 --- a/lib/presentation/widgets/buttons/animated_dialog_button.dart +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -8,18 +8,13 @@ import 'package:game_tracker/core/custom_theme.dart'; /// Parameters: /// - [onPressed]: Callback function that is triggered when the button is pressed. /// - [child]: The child widget to be displayed inside the button, typically a text or icon. -/// - [backgroundColor]: Optional background color for the button container. If null, uses the standard box color from CustomTheme. class AnimatedDialogButton extends StatefulWidget { - const AnimatedDialogButton({ - super.key, - required this.onPressed, - required this.child, - this.backgroundColor, - }); + const AnimatedDialogButton({super.key, required this.onPressed, required this.child}); + /// Callback function that is triggered when the button is pressed. final VoidCallback onPressed; + /// The child widget to be displayed inside the button, typically a text or icon. final Widget child; - final Color? backgroundColor; @override State createState() => _AnimatedDialogButtonState(); @@ -42,11 +37,7 @@ class _AnimatedDialogButtonState extends State { opacity: _isPressed ? 0.6 : 1.0, duration: const Duration(milliseconds: 100), child: Container( - decoration: widget.backgroundColor != null - ? CustomTheme.standardBoxDecoration.copyWith( - color: widget.backgroundColor, - ) - : CustomTheme.standardBoxDecoration, + decoration: CustomTheme.standardBoxDecoration, padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 6), child: widget.child, ), From bb79eecdfdf21d2a5f9abddb48f395d91312873e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 13 Jan 2026 22:24:31 +0100 Subject: [PATCH 13/15] Updated and added comments --- lib/core/adaptive_page_route.dart | 4 + lib/core/constants.dart | 1 + lib/core/custom_theme.dart | 1 + .../main_menu/custom_navigation_bar.dart | 2 + .../group_view/create_group_view.dart | 1 + .../main_menu/group_view/groups_view.dart | 1 + .../views/main_menu/home_view.dart | 2 + .../create_match/choose_game_view.dart | 12 ++- .../create_match/choose_group_view.dart | 19 ++-- .../create_match/choose_ruleset_view.dart | 12 ++- .../create_match/create_match_view.dart | 9 +- .../match_view/match_result_view.dart | 8 +- .../main_menu/match_view/match_view.dart | 1 + .../licenses/license_detail_view.dart | 7 +- .../settings_view/licenses_view.dart | 1 + .../settings_view/settings_view.dart | 2 + .../views/main_menu/statistics_view.dart | 1 + lib/presentation/widgets/app_skeleton.dart | 8 +- .../widgets/buttons/custom_width_button.dart | 12 +-- .../widgets/buttons/main_menu_button.dart | 8 +- .../widgets/buttons/quick_create_button.dart | 6 +- lib/presentation/widgets/navbar_item.dart | 12 +-- .../widgets/player_selection.dart | 14 +-- .../widgets/text_input/custom_search_bar.dart | 18 ++-- .../widgets/text_input/text_input_field.dart | 8 +- .../widgets/tiles/choose_tile.dart | 8 +- .../widgets/tiles/custom_radio_list_tile.dart | 8 +- .../widgets/tiles/group_tile.dart | 6 +- lib/presentation/widgets/tiles/info_tile.dart | 14 +-- .../widgets/tiles/license_tile.dart | 7 +- .../widgets/tiles/match_summary_tile.dart | 95 ------------------- .../widgets/tiles/match_tile.dart | 12 +-- .../widgets/tiles/quick_info_tile.dart | 14 +-- .../widgets/tiles/settings_list_tile.dart | 10 +- .../widgets/tiles/statistics_tile.dart | 14 +-- .../widgets/tiles/text_icon_list_tile.dart | 8 +- .../widgets/tiles/text_icon_tile.dart | 8 +- .../tiles/title_description_list_tile.dart | 14 +-- .../widgets/top_centered_message.dart | 8 +- 39 files changed, 177 insertions(+), 219 deletions(-) delete mode 100644 lib/presentation/widgets/tiles/match_summary_tile.dart diff --git a/lib/core/adaptive_page_route.dart b/lib/core/adaptive_page_route.dart index ba68557..4b0f9b4 100644 --- a/lib/core/adaptive_page_route.dart +++ b/lib/core/adaptive_page_route.dart @@ -1,7 +1,11 @@ import 'dart:io'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +/// Returns a platform-adaptive page route based on the current platform. +/// - On iOS, it returns a [CupertinoPageRoute]. +/// - On other platforms, it returns a [MaterialPageRoute]. Route adaptivePageRoute({ required Widget Function(BuildContext) builder, bool fullscreenDialog = false, diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 8d3c8cc..4ed41c0 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -1,3 +1,4 @@ +/// Application-wide constants class Constants { Constants._(); // Private constructor to prevent instantiation diff --git a/lib/core/custom_theme.dart b/lib/core/custom_theme.dart index cfe9b5a..12ac4a8 100644 --- a/lib/core/custom_theme.dart +++ b/lib/core/custom_theme.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +/// Theme class that defines colors, border radius, padding, and decorations class CustomTheme { CustomTheme._(); // Private constructor to prevent instantiation diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index 3d1fa96..a110419 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -12,6 +12,8 @@ import 'package:game_tracker/presentation/views/main_menu/statistics_view.dart'; import 'package:game_tracker/presentation/widgets/navbar_item.dart'; class CustomNavigationBar extends StatefulWidget { + /// A custom navigation bar widget that provides tabbed navigation + /// between different views: Home, Matches, Groups, and Statistics. const CustomNavigationBar({super.key}); @override diff --git a/lib/presentation/views/main_menu/group_view/create_group_view.dart b/lib/presentation/views/main_menu/group_view/create_group_view.dart index 8192c6b..719b47d 100644 --- a/lib/presentation/views/main_menu/group_view/create_group_view.dart +++ b/lib/presentation/views/main_menu/group_view/create_group_view.dart @@ -11,6 +11,7 @@ import 'package:game_tracker/presentation/widgets/text_input/text_input_field.da import 'package:provider/provider.dart'; class CreateGroupView extends StatefulWidget { + /// A view that allows the user to create a new group const CreateGroupView({super.key}); @override diff --git a/lib/presentation/views/main_menu/group_view/groups_view.dart b/lib/presentation/views/main_menu/group_view/groups_view.dart index ec5fc44..10ae148 100644 --- a/lib/presentation/views/main_menu/group_view/groups_view.dart +++ b/lib/presentation/views/main_menu/group_view/groups_view.dart @@ -14,6 +14,7 @@ import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:provider/provider.dart'; class GroupsView extends StatefulWidget { + /// A view that displays a list of groups const GroupsView({super.key}); @override diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index affbe92..2f25d5a 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -15,6 +15,8 @@ import 'package:game_tracker/presentation/widgets/tiles/quick_info_tile.dart'; import 'package:provider/provider.dart'; class HomeView extends StatefulWidget { + /// The main home view of the application, displaying quick info, + /// recent matches, and quick create options. const HomeView({super.key}); @override diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart index 5976f72..32868e4 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart @@ -6,15 +6,21 @@ import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.d import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart'; class ChooseGameView extends StatefulWidget { - final List<(String, String, Ruleset)> games; - final int initialGameIndex; - + /// A view that allows the user to choose a game from a list of available games + /// - [games]: A list of tuples containing the game name, description and ruleset + /// - [initialGameIndex]: The index of the initially selected game const ChooseGameView({ super.key, required this.games, required this.initialGameIndex, }); + /// A list of tuples containing the game name, description and ruleset + final List<(String, String, Ruleset)> games; + + /// The index of the initially selected game + final int initialGameIndex; + @override State createState() => _ChooseGameViewState(); } diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart index 9e34460..00a0276 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart @@ -7,15 +7,21 @@ import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart'; import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; class ChooseGroupView extends StatefulWidget { - final List groups; - final String initialGroupId; - + /// A view that allows the user to choose a group from a list of groups. + /// - [groups]: A list of available groups to choose from + /// - [initialGroupId]: The ID of the initially selected group const ChooseGroupView({ super.key, required this.groups, required this.initialGroupId, }); + /// A list of available groups to choose from + final List groups; + + /// The ID of the initially selected group + final String initialGroupId; + @override State createState() => _ChooseGroupViewState(); } @@ -140,10 +146,11 @@ class _ChooseGroupViewState extends State { filteredGroups.clear(); filteredGroups.addAll( widget.groups.where( - (group) => - group.name.toLowerCase().contains(query.toLowerCase()) || + (group) => + group.name.toLowerCase().contains(query.toLowerCase()) || group.members.any( - (player) => player.name.toLowerCase().contains(query.toLowerCase()), + (player) => + player.name.toLowerCase().contains(query.toLowerCase()), ), ), ); diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_ruleset_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_ruleset_view.dart index ca021af..3b1f37b 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_ruleset_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_ruleset_view.dart @@ -5,15 +5,21 @@ import 'package:game_tracker/l10n/generated/app_localizations.dart'; import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart'; class ChooseRulesetView extends StatefulWidget { - final List<(Ruleset, String)> rulesets; - final int initialRulesetIndex; - + /// A view that allows the user to choose a ruleset from a list of available rulesets + /// - [rulesets]: A list of tuples containing the ruleset and its description + /// - [initialRulesetIndex]: The index of the initially selected ruleset const ChooseRulesetView({ super.key, required this.rulesets, required this.initialRulesetIndex, }); + /// A list of tuples containing the ruleset and its description + final List<(Ruleset, String)> rulesets; + + /// The index of the initially selected ruleset + final int initialRulesetIndex; + @override State createState() => _ChooseRulesetViewState(); } diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index b9885a4..694a82d 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -18,9 +18,13 @@ import 'package:game_tracker/presentation/widgets/tiles/choose_tile.dart'; import 'package:provider/provider.dart'; class CreateMatchView extends StatefulWidget { - final VoidCallback? onWinnerChanged; + /// A view that allows creating a new match + /// [onWinnerChanged]: Optional callback invoked when the winner is changed const CreateMatchView({super.key, this.onWinnerChanged}); + /// Optional callback invoked when the winner is changed + final VoidCallback? onWinnerChanged; + @override State createState() => _CreateMatchViewState(); } @@ -202,7 +206,8 @@ class _CreateMatchViewState extends State { if (selectedGroup != null) { filteredPlayerList = playerList .where( - (p) => !selectedGroup!.members.any((m) => m.id == p.id), + (p) => + !selectedGroup!.members.any((m) => m.id == p.id), ) .toList(); } else { diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 0d624f0..1deb385 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -8,11 +8,17 @@ import 'package:game_tracker/presentation/widgets/tiles/custom_radio_list_tile.d import 'package:provider/provider.dart'; class MatchResultView extends StatefulWidget { + /// A view that allows selecting and saving the winner of a match + /// [match]: The match for which the winner is to be selected + /// [onWinnerChanged]: Optional callback invoked when the winner is changed + const MatchResultView({super.key, required this.match, this.onWinnerChanged}); + + /// The match for which the winner is to be selected final Match match; + /// Optional callback invoked when the winner is changed final VoidCallback? onWinnerChanged; - const MatchResultView({super.key, required this.match, this.onWinnerChanged}); @override State createState() => _MatchResultViewState(); } diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index 7705d18..b656c61 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -19,6 +19,7 @@ import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:provider/provider.dart'; class MatchView extends StatefulWidget { + /// A view that displays a list of matches const MatchView({super.key}); @override diff --git a/lib/presentation/views/main_menu/settings_view/licenses/license_detail_view.dart b/lib/presentation/views/main_menu/settings_view/licenses/license_detail_view.dart index 02c3adf..e46fc31 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/license_detail_view.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/license_detail_view.dart @@ -5,10 +5,13 @@ import 'package:game_tracker/presentation/views/main_menu/settings_view/licenses import 'package:url_launcher/url_launcher.dart'; class LicenseDetailView extends StatelessWidget { - final Package package; - + /// A detailed view displaying information about a software package license. + /// - [package]: The package data to be displayed. const LicenseDetailView({super.key, required this.package}); + /// The package data to be displayed. + final Package package; + @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); diff --git a/lib/presentation/views/main_menu/settings_view/licenses_view.dart b/lib/presentation/views/main_menu/settings_view/licenses_view.dart index 603162f..58aae5b 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses_view.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses_view.dart @@ -5,6 +5,7 @@ import 'package:game_tracker/presentation/views/main_menu/settings_view/licenses import 'package:game_tracker/presentation/widgets/tiles/license_tile.dart'; class LicensesView extends StatelessWidget { + /// A view that displays a list of open source licenses used in the app const LicensesView({super.key}); @override diff --git a/lib/presentation/views/main_menu/settings_view/settings_view.dart b/lib/presentation/views/main_menu/settings_view/settings_view.dart index e32696d..4f8c73a 100644 --- a/lib/presentation/views/main_menu/settings_view/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view/settings_view.dart @@ -14,6 +14,8 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; class SettingsView extends StatefulWidget { + /// The settings view of the application, allowing users to manage data + /// and view legal information. const SettingsView({super.key}); @override diff --git a/lib/presentation/views/main_menu/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view.dart index 53569ad..fc7165d 100644 --- a/lib/presentation/views/main_menu/statistics_view.dart +++ b/lib/presentation/views/main_menu/statistics_view.dart @@ -10,6 +10,7 @@ import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:provider/provider.dart'; class StatisticsView extends StatefulWidget { + /// A view that displays player statistics const StatisticsView({super.key}); @override diff --git a/lib/presentation/widgets/app_skeleton.dart b/lib/presentation/widgets/app_skeleton.dart index 1d74456..98f2ca7 100644 --- a/lib/presentation/widgets/app_skeleton.dart +++ b/lib/presentation/widgets/app_skeleton.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:skeletonizer/skeletonizer.dart'; -/// A widget that provides a skeleton loading effect to its child widget tree. -/// - [child]: The widget tree to apply the skeleton effect to. -/// - [enabled]: A boolean to enable or disable the skeleton effect. -/// - [fixLayoutBuilder]: A boolean to fix the layout builder for AnimatedSwitcher. class AppSkeleton extends StatefulWidget { + /// A widget that provides a skeleton loading effect to its child widget tree. + /// - [child]: The widget tree to apply the skeleton effect to. + /// - [enabled]: A boolean to enable or disable the skeleton effect. + /// - [fixLayoutBuilder]: A boolean to fix the layout builder for AnimatedSwitcher. const AppSkeleton({ super.key, required this.child, diff --git a/lib/presentation/widgets/buttons/custom_width_button.dart b/lib/presentation/widgets/buttons/custom_width_button.dart index 7e52648..8d45540 100644 --- a/lib/presentation/widgets/buttons/custom_width_button.dart +++ b/lib/presentation/widgets/buttons/custom_width_button.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/enums.dart'; -/// A custom button widget that is designed to have a width relative to the screen size. -/// It supports three types of buttons: primary, secondary, and text buttons. -/// - [text]: The text to display on the button. -/// - [buttonType]: The type of button to display. Defaults to [ButtonType.primary]. -/// - [sizeRelativeToWidth]: The size of the button relative to the width of the screen. -/// - [onPressed]: The callback to be invoked when the button is pressed. class CustomWidthButton extends StatelessWidget { + /// A custom button widget that is designed to have a width relative to the screen size. + /// It supports three types of buttons: primary, secondary, and text buttons. + /// - [text]: The text to display on the button. + /// - [buttonType]: The type of button to display. Defaults to [ButtonType.primary]. + /// - [sizeRelativeToWidth]: The size of the button relative to the width of the screen. + /// - [onPressed]: The callback to be invoked when the button is pressed. const CustomWidthButton({ super.key, required this.text, diff --git a/lib/presentation/widgets/buttons/main_menu_button.dart b/lib/presentation/widgets/buttons/main_menu_button.dart index d29566c..747c31e 100644 --- a/lib/presentation/widgets/buttons/main_menu_button.dart +++ b/lib/presentation/widgets/buttons/main_menu_button.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -/// A button for the main menu with an optional icon and a press animation. -/// - [text]: The text of the button. -/// - [icon]: The icon of the button. -/// - [onPressed]: The callback to be invoked when the button is pressed. class MainMenuButton extends StatefulWidget { + /// A button for the main menu with an optional icon and a press animation. + /// - [text]: The text of the button. + /// - [icon]: The icon of the button. + /// - [onPressed]: The callback to be invoked when the button is pressed. const MainMenuButton({ super.key, required this.text, diff --git a/lib/presentation/widgets/buttons/quick_create_button.dart b/lib/presentation/widgets/buttons/quick_create_button.dart index 40ebeab..e013186 100644 --- a/lib/presentation/widgets/buttons/quick_create_button.dart +++ b/lib/presentation/widgets/buttons/quick_create_button.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A button widget designed for quick creating matches in the [HomeView] -/// - [text]: The text to display on the button. -/// - [onPressed]: The callback to be invoked when the button is pressed. class QuickCreateButton extends StatefulWidget { + /// A button widget designed for quick creating matches in the [HomeView] + /// - [text]: The text to display on the button. + /// - [onPressed]: The callback to be invoked when the button is pressed. const QuickCreateButton({ super.key, required this.text, diff --git a/lib/presentation/widgets/navbar_item.dart b/lib/presentation/widgets/navbar_item.dart index abf7acc..45f2976 100644 --- a/lib/presentation/widgets/navbar_item.dart +++ b/lib/presentation/widgets/navbar_item.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A navigation bar item widget that represents a single tab in a navigation bar. -/// - [index]: The index of the tab. -/// - [isSelected]: A boolean indicating whether the tab is currently selected. -/// - [icon]: The icon to display for the tab. -/// - [label]: The label to display for the tab. -/// - [onTabTapped]: The callback to be invoked when the tab is tapped. class NavbarItem extends StatefulWidget { + /// A navigation bar item widget that represents a single tab in a navigation bar. + /// - [index]: The index of the tab. + /// - [isSelected]: A boolean indicating whether the tab is currently selected. + /// - [icon]: The icon to display for the tab. + /// - [label]: The label to display for the tab. + /// - [onTabTapped]: The callback to be invoked when the tab is tapped. const NavbarItem({ super.key, required this.index, diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 2b9bb50..8b2f2c1 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -11,14 +11,14 @@ import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart'; import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:provider/provider.dart'; -/// A widget that allows users to select players from a list, -/// with search functionality and the ability to add new players. -/// - [availablePlayers]: An optional list of players to choose from. If null, all -/// players from the database are used. -/// - [initialSelectedPlayers]: An optional list of players that should be pre-selected. -/// - [onChanged]: A callback function that is invoked whenever the selection changes, -/// providing the updated list of selected players. class PlayerSelection extends StatefulWidget { + /// A widget that allows users to select players from a list, + /// with search functionality and the ability to add new players. + /// - [availablePlayers]: An optional list of players to choose from. If null, + /// all players from the database are used. + /// - [initialSelectedPlayers]: An optional list of players that should be pre-selected. + /// - [onChanged]: A callback function that is invoked whenever the selection + /// changes, providing the updated list of selected players. const PlayerSelection({ super.key, this.availablePlayers, diff --git a/lib/presentation/widgets/text_input/custom_search_bar.dart b/lib/presentation/widgets/text_input/custom_search_bar.dart index bf7971a..1b453da 100644 --- a/lib/presentation/widgets/text_input/custom_search_bar.dart +++ b/lib/presentation/widgets/text_input/custom_search_bar.dart @@ -1,16 +1,16 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A custom search bar widget that encapsulates a [SearchBar] with additional customization options. -/// - [controller]: The controller for the search bar's text input. -/// - [hintText]: The hint text displayed in the search bar when it is empty. -/// - [trailingButtonShown]: Whether to show the trailing button. -/// - [trailingButtonicon]: The icon for the trailing button. -/// - [trailingButtonEnabled]: Whether the trailing button is in enabled state. -/// - [onTrailingButtonPressed]: The callback invoked when the trailing button is pressed. -/// - [onChanged]: The callback invoked when the text in the search bar changes. -/// - [constraints]: The constraints for the search bar. class CustomSearchBar extends StatelessWidget { + /// A custom search bar widget that encapsulates a [SearchBar] with additional customization options. + /// - [controller]: The controller for the search bar's text input. + /// - [hintText]: The hint text displayed in the search bar when it is empty. + /// - [trailingButtonShown]: Whether to show the trailing button. + /// - [trailingButtonicon]: The icon for the trailing button. + /// - [trailingButtonEnabled]: Whether the trailing button is in enabled state. + /// - [onTrailingButtonPressed]: The callback invoked when the trailing button is pressed. + /// - [onChanged]: The callback invoked when the text in the search bar changes. + /// - [constraints]: The constraints for the search bar. const CustomSearchBar({ super.key, required this.controller, diff --git a/lib/presentation/widgets/text_input/text_input_field.dart b/lib/presentation/widgets/text_input/text_input_field.dart index a409c68..7d11767 100644 --- a/lib/presentation/widgets/text_input/text_input_field.dart +++ b/lib/presentation/widgets/text_input/text_input_field.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A custom text input field widget that encapsulates a [TextField] with specific styling. -/// - [controller]: The controller for the text input field. -/// - [onChanged]: The callback invoked when the text in the field changes. -/// - [hintText]: The hint text displayed in the text input field when it is empty class TextInputField extends StatelessWidget { + /// A custom text input field widget that encapsulates a [TextField] with specific styling. + /// - [controller]: The controller for the text input field. + /// - [onChanged]: The callback invoked when the text in the field changes. + /// - [hintText]: The hint text displayed in the text input field when it is empty const TextInputField({ super.key, required this.controller, diff --git a/lib/presentation/widgets/tiles/choose_tile.dart b/lib/presentation/widgets/tiles/choose_tile.dart index f6ec940..595816e 100644 --- a/lib/presentation/widgets/tiles/choose_tile.dart +++ b/lib/presentation/widgets/tiles/choose_tile.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A tile widget that allows users to choose an option by tapping on it. -/// - [title]: The title text displayed on the tile. -/// - [trailingText]: Optional trailing text displayed on the tile. -/// - [onPressed]: The callback invoked when the tile is tapped. class ChooseTile extends StatefulWidget { + /// A tile widget that allows users to choose an option by tapping on it. + /// - [title]: The title text displayed on the tile. + /// - [trailingText]: Optional trailing text displayed on the tile. + /// - [onPressed]: The callback invoked when the tile is tapped. const ChooseTile({ super.key, required this.title, diff --git a/lib/presentation/widgets/tiles/custom_radio_list_tile.dart b/lib/presentation/widgets/tiles/custom_radio_list_tile.dart index 706aabb..2d8dc7a 100644 --- a/lib/presentation/widgets/tiles/custom_radio_list_tile.dart +++ b/lib/presentation/widgets/tiles/custom_radio_list_tile.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A custom radio list tile widget that encapsulates a [Radio] button with additional styling and functionality. -/// - [text]: The text to display next to the radio button. -/// - [value]: The value associated with the radio button. -/// - [onContainerTap]: The callback invoked when the container is tapped. class CustomRadioListTile extends StatelessWidget { + /// A custom radio list tile widget that encapsulates a [Radio] button with additional styling and functionality. + /// - [text]: The text to display next to the radio button. + /// - [value]: The value associated with the radio button. + /// - [onContainerTap]: The callback invoked when the container is tapped. const CustomRadioListTile({ super.key, required this.text, diff --git a/lib/presentation/widgets/tiles/group_tile.dart b/lib/presentation/widgets/tiles/group_tile.dart index 64d9caa..a06c6b5 100644 --- a/lib/presentation/widgets/tiles/group_tile.dart +++ b/lib/presentation/widgets/tiles/group_tile.dart @@ -3,10 +3,10 @@ import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart'; -/// A tile widget that displays information about a group, including its name and members. -/// - [group]: The group data to be displayed. -/// - [isHighlighted]: Whether the tile should be highlighted. class GroupTile extends StatelessWidget { + /// A tile widget that displays information about a group, including its name and members. + /// - [group]: The group data to be displayed. + /// - [isHighlighted]: Whether the tile should be highlighted. const GroupTile({super.key, required this.group, this.isHighlighted = false}); /// The group data to be displayed. diff --git a/lib/presentation/widgets/tiles/info_tile.dart b/lib/presentation/widgets/tiles/info_tile.dart index 3e11679..280c7d7 100644 --- a/lib/presentation/widgets/tiles/info_tile.dart +++ b/lib/presentation/widgets/tiles/info_tile.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A tile widget that displays a title with an icon and some content below it. -/// - [title]: The title text displayed on the tile. -/// - [icon]: The icon displayed next to the title. -/// - [content]: The content widget displayed below the title. -/// - [padding]: Optional padding for the tile content. -/// - [height]: Optional height for the tile. -/// - [width]: Optional width for the tile. class InfoTile extends StatefulWidget { + /// A tile widget that displays a title with an icon and some content below it. + /// - [title]: The title text displayed on the tile. + /// - [icon]: The icon displayed next to the title. + /// - [content]: The content widget displayed below the title. + /// - [padding]: Optional padding for the tile content. + /// - [height]: Optional height for the tile. + /// - [width]: Optional width for the tile. const InfoTile({ super.key, required this.title, diff --git a/lib/presentation/widgets/tiles/license_tile.dart b/lib/presentation/widgets/tiles/license_tile.dart index 5850d9e..14ee2bf 100644 --- a/lib/presentation/widgets/tiles/license_tile.dart +++ b/lib/presentation/widgets/tiles/license_tile.dart @@ -4,10 +4,13 @@ import 'package:game_tracker/presentation/views/main_menu/settings_view/licenses import 'package:game_tracker/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart'; class LicenseTile extends StatelessWidget { - final Package package; - + /// A tile widget that displays information about a software package license. + /// - [package]: The package data to be displayed. const LicenseTile({super.key, required this.package}); + /// The package data to be displayed. + final Package package; + @override Widget build(BuildContext context) { return GestureDetector( diff --git a/lib/presentation/widgets/tiles/match_summary_tile.dart b/lib/presentation/widgets/tiles/match_summary_tile.dart deleted file mode 100644 index 719037b..0000000 --- a/lib/presentation/widgets/tiles/match_summary_tile.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:game_tracker/core/custom_theme.dart'; -import 'package:skeletonizer/skeletonizer.dart'; - -class MatchSummaryTile extends StatefulWidget { - final String matchTitle; - final String game; - final String ruleset; - final String players; - final String winner; - - const MatchSummaryTile({ - super.key, - required this.matchTitle, - required this.game, - required this.ruleset, - required this.players, - required this.winner, - }); - - @override - State createState() => _MatchSummaryTileState(); -} - -class _MatchSummaryTileState extends State { - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - widget.matchTitle, - style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(width: 5), - Text( - widget.game, - style: const TextStyle(fontSize: 14, color: Colors.grey), - ), - ], - ), - const SizedBox(height: 5), - Container( - padding: const EdgeInsets.symmetric(horizontal: 4), - height: 20, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: CustomTheme.primaryColor, - ), - child: Skeleton.ignore( - child: Text( - widget.ruleset, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - ), - Center( - heightFactor: 1.5, - child: Text( - widget.players, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - Center( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 4), - width: 220, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: Colors.yellow.shade300, - ), - child: Skeleton.ignore( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.emoji_events, color: Colors.black, size: 20), - Text( - widget.winner, - textAlign: TextAlign.center, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ], - ), - ), - ), - ), - ], - ); - } -} diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index 88ae1f1..11cdea0 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -6,13 +6,13 @@ import 'package:game_tracker/l10n/generated/app_localizations.dart'; import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart'; import 'package:intl/intl.dart'; -/// A tile widget that displays information about a match, including its name, -/// creation date, associated group, winner, and players. -/// - [match]: The match data to be displayed. -/// - [onTap]: The callback invoked when the tile is tapped. -/// - [width]: Optional width for the tile. -/// - [compact]: Whether to display the tile in a compact mode class MatchTile extends StatefulWidget { + /// A tile widget that displays information about a match, including its name, + /// creation date, associated group, winner, and players. + /// - [match]: The match data to be displayed. + /// - [onTap]: The callback invoked when the tile is tapped. + /// - [width]: Optional width for the tile. + /// - [compact]: Whether to display the tile in a compact mode const MatchTile({ super.key, required this.match, diff --git a/lib/presentation/widgets/tiles/quick_info_tile.dart b/lib/presentation/widgets/tiles/quick_info_tile.dart index 839f6c3..4d6ef2e 100644 --- a/lib/presentation/widgets/tiles/quick_info_tile.dart +++ b/lib/presentation/widgets/tiles/quick_info_tile.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A tile widget that displays a title with an icon and a numeric value below it. -/// - [title]: The title text displayed on the tile. -/// - [icon]: The icon displayed next to the title. -/// - [value]: The numeric value displayed below the title. -/// - [height]: Optional height for the tile. -/// - [width]: Optional width for the tile. -/// - [padding]: Optional padding for the tile content. class QuickInfoTile extends StatefulWidget { + /// A tile widget that displays a title with an icon and a numeric value below it. + /// - [title]: The title text displayed on the tile. + /// - [icon]: The icon displayed next to the title. + /// - [value]: The numeric value displayed below the title. + /// - [height]: Optional height for the tile. + /// - [width]: Optional width for the tile. + /// - [padding]: Optional padding for the tile content. const QuickInfoTile({ super.key, required this.title, diff --git a/lib/presentation/widgets/tiles/settings_list_tile.dart b/lib/presentation/widgets/tiles/settings_list_tile.dart index ba05225..53fb041 100644 --- a/lib/presentation/widgets/tiles/settings_list_tile.dart +++ b/lib/presentation/widgets/tiles/settings_list_tile.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A customizable settings list tile widget that displays an icon, title, and an optional suffix widget. -/// - [icon]: The icon displayed on the left side of the tile. -/// - [title]: The title text displayed next to the icon. -/// - [suffixWidget]: An optional widget displayed on the right side of the tile. -/// - [onPressed]: The callback invoked when the tile is tapped. class SettingsListTile extends StatelessWidget { + /// A customizable settings list tile widget that displays an icon, title, and an optional suffix widget. + /// - [icon]: The icon displayed on the left side of the tile. + /// - [title]: The title text displayed next to the icon. + /// - [suffixWidget]: An optional widget displayed on the right side of the tile. + /// - [onPressed]: The callback invoked when the tile is tapped. const SettingsListTile({ super.key, required this.icon, diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index 2c0ced0..2ac0dfd 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -4,14 +4,14 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart'; import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart'; -/// A tile widget that displays statistical data using horizontal bars. -/// - [icon]: The icon displayed next to the title. -/// - [title]: The title text displayed on the tile. -/// - [width]: The width of the tile. -/// - [values]: A list of tuples containing labels and their corresponding numeric values. -/// - [itemCount]: The maximum number of items to display. -/// - [barColor]: The color of the bars representing the values. class StatisticsTile extends StatelessWidget { + /// A tile widget that displays statistical data using horizontal bars. + /// - [icon]: The icon displayed next to the title. + /// - [title]: The title text displayed on the tile. + /// - [width]: The width of the tile. + /// - [values]: A list of tuples containing labels and their corresponding numeric values. + /// - [itemCount]: The maximum number of items to display. + /// - [barColor]: The color of the bars representing the values. const StatisticsTile({ super.key, required this.icon, diff --git a/lib/presentation/widgets/tiles/text_icon_list_tile.dart b/lib/presentation/widgets/tiles/text_icon_list_tile.dart index 7d3fe1c..e468e95 100644 --- a/lib/presentation/widgets/tiles/text_icon_list_tile.dart +++ b/lib/presentation/widgets/tiles/text_icon_list_tile.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A list tile widget that displays text with an optional icon button. -/// - [text]: The text to display in the tile. -/// - [onPressed]: The callback to be invoked when the icon is pressed. -/// - [iconEnabled]: A boolean to determine if the icon should be displayed. class TextIconListTile extends StatelessWidget { + /// A list tile widget that displays text with an optional icon button. + /// - [text]: The text to display in the tile. + /// - [onPressed]: The callback to be invoked when the icon is pressed. + /// - [iconEnabled]: A boolean to determine if the icon should be displayed. const TextIconListTile({ super.key, required this.text, diff --git a/lib/presentation/widgets/tiles/text_icon_tile.dart b/lib/presentation/widgets/tiles/text_icon_tile.dart index 7142b27..90c32b7 100644 --- a/lib/presentation/widgets/tiles/text_icon_tile.dart +++ b/lib/presentation/widgets/tiles/text_icon_tile.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A tile widget that displays text with an optional icon that can be tapped. -/// - [text]: The text to display in the tile. -/// - [iconEnabled]: A boolean to determine if the icon should be displayed. -/// - [onIconTap]: The callback to be invoked when the icon is tapped. class TextIconTile extends StatelessWidget { + /// A tile widget that displays text with an optional icon that can be tapped. + /// - [text]: The text to display in the tile. + /// - [iconEnabled]: A boolean to determine if the icon should be displayed. + /// - [onIconTap]: The callback to be invoked when the icon is tapped. const TextIconTile({ super.key, required this.text, diff --git a/lib/presentation/widgets/tiles/title_description_list_tile.dart b/lib/presentation/widgets/tiles/title_description_list_tile.dart index 781149e..3141cbe 100644 --- a/lib/presentation/widgets/tiles/title_description_list_tile.dart +++ b/lib/presentation/widgets/tiles/title_description_list_tile.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A list tile widget that displays a title and description, with optional highlighting and badge. -/// - [title]: The title text displayed on the tile. -/// - [description]: The description text displayed below the title. -/// - [onPressed]: The callback invoked when the tile is tapped. -/// - [isHighlighted]: A boolean to determine if the tile should be highlighted. -/// - [badgeText]: Optional text to display in a badge on the right side of the title. -/// - [badgeColor]: Optional color for the badge background. class TitleDescriptionListTile extends StatelessWidget { + /// A list tile widget that displays a title and description, with optional highlighting and badge. + /// - [title]: The title text displayed on the tile. + /// - [description]: The description text displayed below the title. + /// - [onPressed]: The callback invoked when the tile is tapped. + /// - [isHighlighted]: A boolean to determine if the tile should be highlighted. + /// - [badgeText]: Optional text to display in a badge on the right side of the title. + /// - [badgeColor]: Optional color for the badge background. const TitleDescriptionListTile({ super.key, required this.title, diff --git a/lib/presentation/widgets/top_centered_message.dart b/lib/presentation/widgets/top_centered_message.dart index ecd863c..e651180 100644 --- a/lib/presentation/widgets/top_centered_message.dart +++ b/lib/presentation/widgets/top_centered_message.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -/// A widget that displays a message centered at the top of the screen with an icon, title, and message. -/// - [icon]: The icon to display above the title. -/// - [title]: The title text to display. -/// - [message]: The message text to display below the title. class TopCenteredMessage extends StatelessWidget { + /// A widget that displays a message centered at the top of the screen with an icon, title, and message. + /// - [icon]: The icon to display above the title. + /// - [title]: The title text to display. + /// - [message]: The message text to display below the title. const TopCenteredMessage({ super.key, required this.icon, From 5350113ee14e71a66ad304fbe16a6ec631a51e36 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 13 Jan 2026 22:54:19 +0100 Subject: [PATCH 14/15] Updated doc strings --- .../buttons/animated_dialog_button.dart | 18 ++++++++------- .../widgets/custom_alert_dialog.dart | 23 +++++++++---------- pubspec.yaml | 2 +- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/lib/presentation/widgets/buttons/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart index c0ce560..65c0510 100644 --- a/lib/presentation/widgets/buttons/animated_dialog_button.dart +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -1,18 +1,20 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A custom animated button widget that provides a scaling and opacity effect -/// when pressed. This widget is designed to be used in dialogs or other UI -/// components where a visually appealing button is required. -/// -/// Parameters: -/// - [onPressed]: Callback function that is triggered when the button is pressed. -/// - [child]: The child widget to be displayed inside the button, typically a text or icon. class AnimatedDialogButton extends StatefulWidget { - const AnimatedDialogButton({super.key, required this.onPressed, required this.child}); + /// A custom animated button widget that provides a scaling and opacity effect + /// when pressed. + /// - [onPressed]: Callback function that is triggered when the button is pressed. + /// - [child]: The child widget to be displayed inside the button, typically a text or icon. + const AnimatedDialogButton({ + super.key, + required this.onPressed, + required this.child, + }); /// Callback function that is triggered when the button is pressed. final VoidCallback onPressed; + /// The child widget to be displayed inside the button, typically a text or icon. final Widget child; diff --git a/lib/presentation/widgets/custom_alert_dialog.dart b/lib/presentation/widgets/custom_alert_dialog.dart index 832369a..af5b45a 100644 --- a/lib/presentation/widgets/custom_alert_dialog.dart +++ b/lib/presentation/widgets/custom_alert_dialog.dart @@ -1,17 +1,13 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -/// A custom alert dialog widget that follows the application's design theme. -/// -/// This widget provides a styled alternative to the default Flutter AlertDialog, -/// with consistent colors, borders, and layout that match the app's custom theme. -/// -/// Parameters: -/// - [title]: The title text displayed at the top of the dialog. -/// - [content]: The main content text displayed in the body of the dialog. -/// - [actions]: A list of action widgets (typically buttons) displayed at the bottom -/// of the dialog. These actions are horizontally spaced around the dialog's width. class CustomAlertDialog extends StatelessWidget { + /// A custom alert dialog widget that provides a os unspecific AlertDialog, + /// with consistent colors, borders, and layout that match the app's custom theme. + /// - [title]: The title text displayed at the top of the dialog. + /// - [content]: The main content text displayed in the body of the dialog. + /// - [actions]: A list of action widgets (typically buttons) displayed at the bottom + /// of the dialog. These actions are horizontally spaced around the dialog's width. const CustomAlertDialog({ super.key, required this.title, @@ -26,8 +22,11 @@ class CustomAlertDialog extends StatelessWidget { @override Widget build(BuildContext context) { return AlertDialog( - title: Text(title, style: const TextStyle(color: CustomTheme.textColor,),), - content: Text(content, style: const TextStyle(color: CustomTheme.textColor),), + title: Text(title, style: const TextStyle(color: CustomTheme.textColor)), + content: Text( + content, + style: const TextStyle(color: CustomTheme.textColor), + ), actions: actions, backgroundColor: CustomTheme.boxColor, actionsAlignment: MainAxisAlignment.spaceAround, diff --git a/pubspec.yaml b/pubspec.yaml index 83d5079..46efd94 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: game_tracker description: "Game Tracking App for Card Games" publish_to: 'none' -version: 0.0.6+209 +version: 0.0.7+211 environment: sdk: ^3.8.1 From 2cadab004d0b8204fa0164db2b0981d36db94ec9 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 13 Jan 2026 22:58:35 +0100 Subject: [PATCH 15/15] Merge cleaning --- .../main_menu/settings_view/settings_view.dart | 16 +++++++++++++--- pubspec.yaml | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/settings_view.dart b/lib/presentation/views/main_menu/settings_view/settings_view.dart index e34d75b..b51cad0 100644 --- a/lib/presentation/views/main_menu/settings_view/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view/settings_view.dart @@ -123,11 +123,21 @@ class _SettingsViewState extends State { actions: [ AnimatedDialogButton( onPressed: () => Navigator.of(context).pop(false), - child: Text(loc.cancel, style: const TextStyle(color: CustomTheme.textColor)), + child: Text( + loc.cancel, + style: const TextStyle( + color: CustomTheme.textColor, + ), + ), ), AnimatedDialogButton( onPressed: () => Navigator.of(context).pop(true), - child: Text(loc.delete, style: TextStyle(color: CustomTheme.secondaryColor)), + child: Text( + loc.delete, + style: TextStyle( + color: CustomTheme.secondaryColor, + ), + ), ), ], ), @@ -135,7 +145,7 @@ class _SettingsViewState extends State { if (confirmed == true && context.mounted) { DataTransferService.deleteAllData(context); showSnackbar( - context: context, + context: scaffoldMessengerContext, message: AppLocalizations.of( context, ).data_successfully_deleted, diff --git a/pubspec.yaml b/pubspec.yaml index 46efd94..e9fd894 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: game_tracker description: "Game Tracking App for Card Games" publish_to: 'none' -version: 0.0.7+211 +version: 0.0.7+212 environment: sdk: ^3.8.1