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/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 2dc1adb..4347fdf 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -83,7 +83,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 9242bdd..2642dcc 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -372,7 +372,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 9c059f2..eebf3f9 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -599,7 +599,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 b512525..277f5c4 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -276,7 +276,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 8f038a0..79d924e 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -275,7 +275,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/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 new file mode 100644 index 0000000..719b47d --- /dev/null +++ b/lib/presentation/views/main_menu/group_view/create_group_view.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:game_tracker/core/custom_theme.dart'; +import 'package:game_tracker/core/enums.dart'; +import 'package:game_tracker/data/db/database.dart'; +import 'package:game_tracker/data/dto/group.dart'; +import 'package:game_tracker/data/dto/player.dart'; +import 'package:game_tracker/l10n/generated/app_localizations.dart'; +import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart'; +import 'package:game_tracker/presentation/widgets/player_selection.dart'; +import 'package:game_tracker/presentation/widgets/text_input/text_input_field.dart'; +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 + State createState() => _CreateGroupViewState(); +} + +class _CreateGroupViewState extends State { + late final AppDatabase db; + + /// Controller for the group name input field + final _groupNameController = TextEditingController(); + + /// List of currently selected players + List selectedPlayers = []; + + @override + void initState() { + super.initState(); + db = Provider.of(context, listen: false); + _groupNameController.addListener(() { + setState(() {}); + }); + } + + @override + void dispose() { + _groupNameController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + return ScaffoldMessenger( + child: Scaffold( + backgroundColor: CustomTheme.backgroundColor, + appBar: AppBar(title: Text(loc.create_new_group)), + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + margin: CustomTheme.standardMargin, + child: TextInputField( + controller: _groupNameController, + hintText: loc.group_name, + ), + ), + Expanded( + child: PlayerSelection( + onChanged: (value) { + setState(() { + selectedPlayers = [...value]; + }); + }, + ), + ), + CustomWidthButton( + text: loc.create_group, + sizeRelativeToWidth: 0.95, + buttonType: ButtonType.primary, + onPressed: + (_groupNameController.text.isEmpty || + (selectedPlayers.length < 2)) + ? null + : () async { + bool success = await db.groupDao.addGroup( + group: Group( + name: _groupNameController.text.trim(), + members: selectedPlayers, + ), + ); + if (!context.mounted) return; + if (success) { + Navigator.pop(context); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: CustomTheme.boxColor, + content: Center( + child: Text( + AppLocalizations.of( + context, + ).error_creating_group, + style: const TextStyle(color: Colors.white), + ), + ), + ), + ); + } + }, + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ); + } +} 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 e8b66b9..add4a7d 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 cad3e9c..a13cd86 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(); } @@ -206,7 +210,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 9a3d33c..22b3ce9 100644 --- a/lib/presentation/views/main_menu/settings_view/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view/settings_view.dart @@ -7,6 +7,8 @@ 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/views/main_menu/settings_view/licenses_view.dart'; +import 'package:game_tracker/presentation/widgets/buttons/animated_dialog_button.dart'; +import 'package:game_tracker/presentation/widgets/custom_alert_dialog.dart'; import 'package:game_tracker/presentation/widgets/tiles/settings_list_tile.dart'; import 'package:game_tracker/services/data_transfer_service.dart'; import 'package:intl/intl.dart'; @@ -14,6 +16,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 @@ -21,10 +25,6 @@ class SettingsView extends StatefulWidget { } class _SettingsViewState extends State { - - /// GlobalKey for ScaffoldMessenger to show snackbars only on this screen - final _scaffoldMessengerKey = GlobalKey(); - PackageInfo _packageInfo = PackageInfo( appName: 'n.A.', packageName: 'n.A.', @@ -42,225 +42,263 @@ class _SettingsViewState extends State { Widget build(BuildContext context) { final loc = AppLocalizations.of(context); return ScaffoldMessenger( - key: _scaffoldMessengerKey, - 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 (!mounted) return; - showExportSnackBar(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 (!mounted) return; - showImportSnackBar(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( - 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')), - }, - ), - GestureDetector( - child: const FaIcon(FontAwesomeIcons.github), - onTap: () => { - launchUrl( - Uri.parse( - 'https://github.com/liquiddevelopmentde', - ), + 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: context, + builder: (context) => CustomAlertDialog( + title: '${loc.delete_all_data}?', + content: loc.this_cannot_be_undone, + actions: [ + AnimatedDialogButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text( + loc.cancel, + style: const TextStyle( + color: CustomTheme.textColor, ), - }, - ), - GestureDetector( - child: Icon( - Platform.isIOS - ? CupertinoIcons.mail_solid - : Icons.email, ), - onTap: () => launchUrl( - Uri.parse('mailto:hi@liquid-dev.de'), + ), + AnimatedDialogButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text( + loc.delete, + style: TextStyle( + color: CustomTheme.secondaryColor, + ), ), ), ], ), - ), - 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 && context.mounted) { + DataTransferService.deleteAllData(context); + showSnackbar( + context: scaffoldMessengerContext, + message: AppLocalizations.of( + context, + ).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, + ), + ), + ], + ), + ), + ), + ], ), - ], - ), - ), + ), + ); + }, ), ); } /// Displays a snackbar based on the import result. /// + /// [context] The BuildContext to show the snackbar in. /// [result] The result of the import operation. void showImportSnackBar({ + required BuildContext context, required ImportResult result, }) { final loc = AppLocalizations.of(context); switch (result) { case ImportResult.success: - showSnackbar(message: loc.data_successfully_imported); + showSnackbar(context: context, message: loc.data_successfully_imported); case ImportResult.invalidSchema: - showSnackbar(message: loc.invalid_schema); + showSnackbar(context: context, message: loc.invalid_schema); case ImportResult.fileReadError: - showSnackbar(message: loc.error_reading_file); + showSnackbar(context: context, message: loc.error_reading_file); case ImportResult.canceled: - showSnackbar(message: loc.import_canceled); + showSnackbar(context: context, message: loc.import_canceled); case ImportResult.formatException: - showSnackbar(message: loc.format_exception); + showSnackbar(context: context, message: loc.format_exception); case ImportResult.unknownException: - showSnackbar(message: loc.unknown_exception); + showSnackbar(context: context, message: loc.unknown_exception); } } /// Displays a snackbar based on the export result. /// + /// [context] The BuildContext to show the snackbar in. /// [result] The result of the export operation. void showExportSnackBar({ + required BuildContext context, required ExportResult result, }) { final loc = AppLocalizations.of(context); @@ -276,18 +314,21 @@ class _SettingsViewState extends State { /// Displays a snackbar with the given message and optional action. /// + /// [context] The BuildContext to show the snackbar in. /// [message] The message to display in the snackbar. /// [duration] The duration for which the snackbar is displayed. /// [action] An optional callback function to execute when the action button is pressed. void showSnackbar({ + required BuildContext context, required String message, Duration duration = const Duration(seconds: 3), VoidCallback? action, }) { + if (!context.mounted) return; + final loc = AppLocalizations.of(context); - final messenger = _scaffoldMessengerKey.currentState!; - 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, 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/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart new file mode 100644 index 0000000..65c0510 --- /dev/null +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:game_tracker/core/custom_theme.dart'; + +class AnimatedDialogButton extends StatefulWidget { + /// 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; + + @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: const EdgeInsets.symmetric(horizontal: 26, vertical: 6), + child: widget.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/custom_alert_dialog.dart b/lib/presentation/widgets/custom_alert_dialog.dart new file mode 100644 index 0000000..af5b45a --- /dev/null +++ b/lib/presentation/widgets/custom_alert_dialog.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:game_tracker/core/custom_theme.dart'; + +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, + required this.content, + required this.actions, + }); + + final String title; + final String content; + final List 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, + actionsAlignment: MainAxisAlignment.spaceAround, + shape: RoundedRectangleBorder( + borderRadius: CustomTheme.standardBorderRadiusAll, + side: BorderSide(color: CustomTheme.boxBorder), + ), + ); + } +} 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 25a3704..a0c5a63 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, @@ -311,6 +311,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, 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 eb1d4ab..05dbd23 100644 --- a/lib/presentation/widgets/tiles/group_tile.dart +++ b/lib/presentation/widgets/tiles/group_tile.dart @@ -3,11 +3,11 @@ 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. -/// - [onTap]: An optional callback function to handle tap events. 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. + /// - [onTap]: An optional callback function to handle tap events. const GroupTile({super.key, required this.group, this.isHighlighted = false, this.onTap}); /// 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, diff --git a/pubspec.yaml b/pubspec.yaml index 83d5079..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.6+209 +version: 0.0.7+212 environment: sdk: ^3.8.1