From 94b113eb95fe7f84d52161d323d707ac0a5583ab Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 28 Nov 2025 14:07:42 +0100 Subject: [PATCH 01/32] Renamed widget --- .../views/main_menu/create_game/choose_ruleset_view.dart | 4 ++-- ...uleset_list_tile.dart => title_description_list_tile.dart} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename lib/presentation/widgets/tiles/{ruleset_list_tile.dart => title_description_list_tile.dart} (94%) diff --git a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart index b54f56e..647cdc4 100644 --- a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart @@ -1,7 +1,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/presentation/widgets/tiles/ruleset_list_tile.dart'; +import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart'; class ChooseRulesetView extends StatefulWidget { final List<(Ruleset, String, String)> rulesets; @@ -85,7 +85,7 @@ class _ChooseRulesetViewState extends State { padding: const EdgeInsets.only(bottom: 85), itemCount: widget.rulesets.length, itemBuilder: (BuildContext context, int index) { - return RulesetListTile( + return TitleDescriptionListTile( onPressed: () async { setState(() { selectedRulesetIndex = index; diff --git a/lib/presentation/widgets/tiles/ruleset_list_tile.dart b/lib/presentation/widgets/tiles/title_description_list_tile.dart similarity index 94% rename from lib/presentation/widgets/tiles/ruleset_list_tile.dart rename to lib/presentation/widgets/tiles/title_description_list_tile.dart index 13eaf82..5b370d4 100644 --- a/lib/presentation/widgets/tiles/ruleset_list_tile.dart +++ b/lib/presentation/widgets/tiles/title_description_list_tile.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; -class RulesetListTile extends StatelessWidget { +class TitleDescriptionListTile extends StatelessWidget { final String title; final String description; final VoidCallback? onPressed; final bool isHighlighted; - const RulesetListTile({ + const TitleDescriptionListTile({ super.key, required this.title, required this.description, From 236a737fd1ffce9d2cd13db86e46435f6e32a8e7 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 28 Nov 2025 14:41:50 +0100 Subject: [PATCH 02/32] Implemented choose game view --- lib/core/enums.dart | 18 +++- .../create_game/choose_game_view.dart | 65 +++++++++++++ .../create_game/choose_ruleset_view.dart | 92 ++++--------------- .../create_game/create_game_view.dart | 46 +++++++--- .../tiles/title_description_list_tile.dart | 44 +++++++-- 5 files changed, 167 insertions(+), 98 deletions(-) create mode 100644 lib/presentation/views/main_menu/create_game/choose_game_view.dart diff --git a/lib/core/enums.dart b/lib/core/enums.dart index af1f4a6..68752fb 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -27,5 +27,19 @@ enum ExportResult { success, canceled, unknownException } /// - [Ruleset.singleWinner]: The game is won by a single player /// - [Ruleset.singleLoser]: The game is lost by a single player /// - [Ruleset.mostPoints]: The player with the most points wins. -/// - [Ruleset.lastPoints]: The player with the fewest points wins. -enum Ruleset { singleWinner, singleLoser, mostPoints, lastPoints } +/// - [Ruleset.leastPoints]: The player with the fewest points wins. +enum Ruleset { singleWinner, singleLoser, mostPoints, leastPoints } + +/// Translates a [Ruleset] enum value to its corresponding string representation. +String translateRulesetToString(Ruleset ruleset) { + switch (ruleset) { + case Ruleset.singleWinner: + return 'Single Winner'; + case Ruleset.singleLoser: + return 'Single Loser'; + case Ruleset.mostPoints: + return 'Most Points'; + case Ruleset.leastPoints: + return 'Least Points'; + } +} diff --git a/lib/presentation/views/main_menu/create_game/choose_game_view.dart b/lib/presentation/views/main_menu/create_game/choose_game_view.dart new file mode 100644 index 0000000..1c75b32 --- /dev/null +++ b/lib/presentation/views/main_menu/create_game/choose_game_view.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:game_tracker/core/custom_theme.dart'; +import 'package:game_tracker/core/enums.dart'; +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; + + const ChooseGameView({ + super.key, + required this.games, + required this.initialGameIndex, + }); + + @override + State createState() => _ChooseGameViewState(); +} + +class _ChooseGameViewState extends State { + late int selectedGameIndex; + + @override + void initState() { + selectedGameIndex = widget.initialGameIndex; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: CustomTheme.backgroundColor, + appBar: AppBar( + backgroundColor: CustomTheme.backgroundColor, + scrolledUnderElevation: 0, + title: const Text( + 'Choose Game', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + centerTitle: true, + ), + body: ListView.builder( + padding: const EdgeInsets.only(bottom: 85), + itemCount: widget.games.length, + itemBuilder: (BuildContext context, int index) { + return TitleDescriptionListTile( + title: widget.games[index].$1, + description: widget.games[index].$2, + badgeText: translateRulesetToString(widget.games[index].$3), + isHighlighted: selectedGameIndex == index, + onPressed: () async { + setState(() { + selectedGameIndex = index; + }); + Future.delayed(const Duration(milliseconds: 500), () { + if (!context.mounted) return; + Navigator.of(context).pop(selectedGameIndex); + }); + }, + ); + }, + ), + ); + } +} diff --git a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart index 647cdc4..2e45a7c 100644 --- a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart @@ -6,6 +6,7 @@ import 'package:game_tracker/presentation/widgets/tiles/title_description_list_t class ChooseRulesetView extends StatefulWidget { final List<(Ruleset, String, String)> rulesets; final int initialRulesetIndex; + const ChooseRulesetView({ super.key, required this.rulesets, @@ -41,78 +42,25 @@ class _ChooseRulesetViewState extends State { ), centerTitle: true, ), - body: Column( - children: [ - Container( - color: CustomTheme.backgroundColor, - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: TabBar( - padding: const EdgeInsets.symmetric(horizontal: 5), - // Label Settings - labelStyle: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - labelColor: Colors.white, - unselectedLabelStyle: const TextStyle(fontSize: 14), - unselectedLabelColor: Colors.white70, - // Indicator Settings - indicator: CustomTheme.standardBoxDecoration, - indicatorSize: TabBarIndicatorSize.tab, - indicatorWeight: 1, - indicatorPadding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 0, - ), - // Divider Settings - dividerHeight: 0, - tabs: const [ - Tab(text: 'Rulesets'), - Tab(text: 'Gametypes'), - ], - ), - ), - const Divider( - indent: 30, - endIndent: 30, - thickness: 3, - radius: BorderRadius.all(Radius.circular(12)), - ), - Expanded( - child: TabBarView( - children: [ - ListView.builder( - padding: const EdgeInsets.only(bottom: 85), - itemCount: widget.rulesets.length, - itemBuilder: (BuildContext context, int index) { - return TitleDescriptionListTile( - onPressed: () async { - setState(() { - selectedRulesetIndex = index; - }); - Future.delayed(const Duration(milliseconds: 500), () { - if (!context.mounted) return; - Navigator.of( - context, - ).pop(widget.rulesets[index].$1); - }); - }, - title: widget.rulesets[index].$2, - description: widget.rulesets[index].$3, - isHighlighted: selectedRulesetIndex == index, - ); - }, - ), - const Center( - child: Text( - 'No gametypes available', - style: TextStyle(color: Colors.white70), - ), - ), - ], - ), - ), - ], + body: ListView.builder( + padding: const EdgeInsets.only(bottom: 85), + itemCount: widget.rulesets.length, + itemBuilder: (BuildContext context, int index) { + return TitleDescriptionListTile( + onPressed: () async { + setState(() { + selectedRulesetIndex = index; + }); + Future.delayed(const Duration(milliseconds: 500), () { + if (!context.mounted) return; + Navigator.of(context).pop(widget.rulesets[index].$1); + }); + }, + title: widget.rulesets[index].$2, + description: widget.rulesets[index].$3, + isHighlighted: selectedRulesetIndex == index, + ); + }, ), ), ); diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 485118b..68883d0 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -5,6 +5,7 @@ import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/dto/game.dart'; import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/player.dart'; +import 'package:game_tracker/presentation/views/main_menu/create_game/choose_game_view.dart'; import 'package:game_tracker/presentation/views/main_menu/create_game/choose_group_view.dart'; import 'package:game_tracker/presentation/views/main_menu/create_game/choose_ruleset_view.dart'; import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart'; @@ -53,6 +54,8 @@ class _CreateGameViewState extends State { /// the [ChooseRulesetView] int selectedRulesetIndex = -1; + int selectedGameIndex = -1; + /// The currently selected players List? selectedPlayers; @@ -75,12 +78,17 @@ class _CreateGameViewState extends State { 'Traditional ruleset: the player with the most points wins.', ), ( - Ruleset.lastPoints, + Ruleset.leastPoints, 'Least Points', 'Inverse scoring: the player with the fewest points wins.', ), ]; + List<(String, String, Ruleset)> games = [ + ('Cabo', 'A memory card game', Ruleset.leastPoints), + ('Uno', 'The Classic', Ruleset.singleWinner), + ]; + @override void initState() { super.initState(); @@ -122,6 +130,27 @@ class _CreateGameViewState extends State { }, ), ), + ChooseTile( + title: 'Game', + trailingText: selectedGameIndex == -1 + ? 'None' + : games[selectedGameIndex].$1, + onPressed: () async { + selectedGameIndex = await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => ChooseGameView( + games: games, + initialGameIndex: selectedGameIndex, + ), + ), + ); + selectedRuleset = games[selectedGameIndex].$3; + selectedRulesetIndex = rulesets.indexWhere( + (r) => r.$1 == selectedRuleset, + ); + setState(() {}); + }, + ), ChooseTile( title: 'Ruleset', trailingText: selectedRuleset == null @@ -139,6 +168,7 @@ class _CreateGameViewState extends State { selectedRulesetIndex = rulesets.indexWhere( (r) => r.$1 == selectedRuleset, ); + selectedGameIndex = -1; setState(() {}); }, ), @@ -207,20 +237,6 @@ class _CreateGameViewState extends State { ); } - /// Translates a [Ruleset] enum value to its corresponding string representation. - String translateRulesetToString(Ruleset ruleset) { - switch (ruleset) { - case Ruleset.singleWinner: - return 'Single Winner'; - case Ruleset.singleLoser: - return 'Single Loser'; - case Ruleset.mostPoints: - return 'Most Points'; - case Ruleset.lastPoints: - return 'Least Points'; - } - } - /// Determines whether the "Create Game" button should be enabled based on /// the current state of the input fields. bool _enableCreateGameButton() { diff --git a/lib/presentation/widgets/tiles/title_description_list_tile.dart b/lib/presentation/widgets/tiles/title_description_list_tile.dart index 5b370d4..ac20b3f 100644 --- a/lib/presentation/widgets/tiles/title_description_list_tile.dart +++ b/lib/presentation/widgets/tiles/title_description_list_tile.dart @@ -6,6 +6,8 @@ class TitleDescriptionListTile extends StatelessWidget { final String description; final VoidCallback? onPressed; final bool isHighlighted; + final String? badgeText; + final Color? badgeColor; const TitleDescriptionListTile({ super.key, @@ -13,6 +15,8 @@ class TitleDescriptionListTile extends StatelessWidget { required this.description, this.onPressed, this.isHighlighted = false, + this.badgeText, + this.badgeColor, }); @override @@ -28,20 +32,42 @@ class TitleDescriptionListTile extends StatelessWidget { duration: const Duration(milliseconds: 200), child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Flexible( - child: Text( - title, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, - ), + Text( + title, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, ), ), + if (badgeText != null) ...[ + const Spacer(), + Container( + margin: const EdgeInsets.only(top: 4), + padding: const EdgeInsets.symmetric( + vertical: 2, + horizontal: 6, + ), + decoration: BoxDecoration( + color: badgeColor ?? CustomTheme.primaryColor, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + badgeText!, + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ), + ], ], ), const SizedBox(height: 5), From 4dbc106e38de013499e282059200e4c29d3bb301 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 28 Nov 2025 23:14:13 +0100 Subject: [PATCH 03/32] Added searchbar --- .../create_game/choose_game_view.dart | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_game_view.dart b/lib/presentation/views/main_menu/create_game/choose_game_view.dart index 1c75b32..8d8279e 100644 --- a/lib/presentation/views/main_menu/create_game/choose_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_game_view.dart @@ -1,6 +1,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/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart'; class ChooseGameView extends StatefulWidget { @@ -19,6 +20,7 @@ class ChooseGameView extends StatefulWidget { class _ChooseGameViewState extends State { late int selectedGameIndex; + final TextEditingController searchBarController = TextEditingController(); @override void initState() { @@ -39,26 +41,36 @@ class _ChooseGameViewState extends State { ), centerTitle: true, ), - body: ListView.builder( - padding: const EdgeInsets.only(bottom: 85), - itemCount: widget.games.length, - itemBuilder: (BuildContext context, int index) { - return TitleDescriptionListTile( - title: widget.games[index].$1, - description: widget.games[index].$2, - badgeText: translateRulesetToString(widget.games[index].$3), - isHighlighted: selectedGameIndex == index, - onPressed: () async { - setState(() { - selectedGameIndex = index; - }); - Future.delayed(const Duration(milliseconds: 500), () { - if (!context.mounted) return; - Navigator.of(context).pop(selectedGameIndex); - }); - }, - ); - }, + body: Column( + children: [ + CustomSearchBar( + controller: searchBarController, + hintText: 'Game Name', + ), + const SizedBox(height: 5), + Expanded( + child: ListView.builder( + itemCount: widget.games.length, + itemBuilder: (BuildContext context, int index) { + return TitleDescriptionListTile( + title: widget.games[index].$1, + description: widget.games[index].$2, + badgeText: translateRulesetToString(widget.games[index].$3), + isHighlighted: selectedGameIndex == index, + onPressed: () async { + setState(() { + selectedGameIndex = index; + }); + Future.delayed(const Duration(milliseconds: 500), () { + if (!context.mounted) return; + Navigator.of(context).pop(selectedGameIndex); + }); + }, + ); + }, + ), + ), + ], ), ); } From bcd7bf751b7ff6ecb90c84798cc837598873ba5a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 28 Nov 2025 23:14:29 +0100 Subject: [PATCH 04/32] Added todo & example data --- .../views/main_menu/create_game/create_game_view.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 68883d0..eb5468c 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -61,6 +61,7 @@ class _CreateGameViewState extends State { /// List of available rulesets with their display names and descriptions /// as tuples of (Ruleset, String, String) + /// TODO: Replace when rulesets are implemented List<(Ruleset, String, String)> rulesets = [ ( Ruleset.singleWinner, @@ -84,9 +85,10 @@ class _CreateGameViewState extends State { ), ]; + // TODO: Replace when games are implemented List<(String, String, Ruleset)> games = [ - ('Cabo', 'A memory card game', Ruleset.leastPoints), - ('Uno', 'The Classic', Ruleset.singleWinner), + ('Example Game 1', 'This is a discription', Ruleset.leastPoints), + ('Example Game 2', '', Ruleset.singleWinner), ]; @override From b4ccb567b508e85c6d83f79e12f6efe6a2419106 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 28 Nov 2025 23:15:08 +0100 Subject: [PATCH 05/32] Added conditional description --- .../widgets/tiles/title_description_list_tile.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/presentation/widgets/tiles/title_description_list_tile.dart b/lib/presentation/widgets/tiles/title_description_list_tile.dart index ac20b3f..7bc4221 100644 --- a/lib/presentation/widgets/tiles/title_description_list_tile.dart +++ b/lib/presentation/widgets/tiles/title_description_list_tile.dart @@ -32,7 +32,7 @@ class TitleDescriptionListTile extends StatelessWidget { duration: const Duration(milliseconds: 200), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.start, @@ -70,9 +70,11 @@ class TitleDescriptionListTile extends StatelessWidget { ], ], ), - const SizedBox(height: 5), - Text(description, style: const TextStyle(fontSize: 14)), - const SizedBox(height: 2.5), + if (description.isNotEmpty) ...[ + const SizedBox(height: 5), + Text(description, style: const TextStyle(fontSize: 14)), + const SizedBox(height: 2.5), + ], ], ), ), From 75b62d0854fb9b6dce79dee74743925ecfabbca9 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 6 Dec 2025 16:58:12 +0100 Subject: [PATCH 06/32] Replaced temp navigator --- lib/presentation/views/main_menu/game_history_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/game_history_view.dart b/lib/presentation/views/main_menu/game_history_view.dart index 31d1b56..303ea67 100644 --- a/lib/presentation/views/main_menu/game_history_view.dart +++ b/lib/presentation/views/main_menu/game_history_view.dart @@ -5,7 +5,7 @@ import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/dto/game.dart'; import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/player.dart'; -import 'package:game_tracker/presentation/views/main_menu/create_group_view.dart'; +import 'package:game_tracker/presentation/views/main_menu/create_game/create_game_view.dart'; import 'package:game_tracker/presentation/views/main_menu/game_result_view.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart'; @@ -133,7 +133,7 @@ class _GameHistoryViewState extends State { context, MaterialPageRoute( builder: (context) { - return const CreateGroupView(); + return const CreateGameView(); }, ), ); From 10aad4712438188a0d7bb798df2db8b05bf49446 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 6 Dec 2025 17:13:33 +0100 Subject: [PATCH 07/32] Updated choosing mechanism --- .../create_game/choose_game_view.dart | 66 +++++++++++-------- .../create_game/choose_group_view.dart | 21 ++++-- .../create_game/choose_ruleset_view.dart | 20 ++++-- 3 files changed, 67 insertions(+), 40 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_game_view.dart b/lib/presentation/views/main_menu/create_game/choose_game_view.dart index 8d8279e..5b2463b 100644 --- a/lib/presentation/views/main_menu/create_game/choose_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_game_view.dart @@ -35,42 +35,50 @@ class _ChooseGameViewState extends State { appBar: AppBar( backgroundColor: CustomTheme.backgroundColor, scrolledUnderElevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of( + context, + ).pop(selectedGameIndex == -1 ? null : selectedGameIndex); + }, + ), title: const Text( 'Choose Game', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), centerTitle: true, ), - body: Column( - children: [ - CustomSearchBar( - controller: searchBarController, - hintText: 'Game Name', - ), - const SizedBox(height: 5), - Expanded( - child: ListView.builder( - itemCount: widget.games.length, - itemBuilder: (BuildContext context, int index) { - return TitleDescriptionListTile( - title: widget.games[index].$1, - description: widget.games[index].$2, - badgeText: translateRulesetToString(widget.games[index].$3), - isHighlighted: selectedGameIndex == index, - onPressed: () async { - setState(() { - selectedGameIndex = index; - }); - Future.delayed(const Duration(milliseconds: 500), () { - if (!context.mounted) return; - Navigator.of(context).pop(selectedGameIndex); - }); - }, - ); - }, + body: Container( + decoration: CustomTheme.standardBoxDecoration, + padding: const EdgeInsets.all(10), + child: Column( + children: [ + CustomSearchBar( + controller: searchBarController, + hintText: 'Game Name', ), - ), - ], + const SizedBox(height: 5), + Expanded( + child: ListView.builder( + itemCount: widget.games.length, + itemBuilder: (BuildContext context, int index) { + return TitleDescriptionListTile( + title: widget.games[index].$1, + description: widget.games[index].$2, + badgeText: translateRulesetToString(widget.games[index].$3), + isHighlighted: selectedGameIndex == index, + onPressed: () async { + setState(() { + selectedGameIndex = index; + }); + }, + ); + }, + ), + ), + ], + ), ), ); } diff --git a/lib/presentation/views/main_menu/create_game/choose_group_view.dart b/lib/presentation/views/main_menu/create_game/choose_group_view.dart index c98ce6d..de512bf 100644 --- a/lib/presentation/views/main_menu/create_game/choose_group_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_group_view.dart @@ -33,6 +33,16 @@ class _ChooseGroupViewState extends State { appBar: AppBar( backgroundColor: CustomTheme.backgroundColor, scrolledUnderElevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of(context).pop( + selectedGroupIndex == -1 + ? null + : widget.groups[selectedGroupIndex], + ); + }, + ), title: const Text( 'Choose Group', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), @@ -46,12 +56,11 @@ class _ChooseGroupViewState extends State { return GestureDetector( onTap: () { setState(() { - selectedGroupIndex = index; - }); - - Future.delayed(const Duration(milliseconds: 500), () { - if (!context.mounted) return; - Navigator.of(context).pop(widget.groups[index]); + if (selectedGroupIndex == index) { + selectedGroupIndex = -1; + } else { + selectedGroupIndex = index; + } }); }, child: GroupTile( diff --git a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart index 2e45a7c..13a9647 100644 --- a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart @@ -36,6 +36,16 @@ class _ChooseRulesetViewState extends State { appBar: AppBar( backgroundColor: CustomTheme.backgroundColor, scrolledUnderElevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of(context).pop( + selectedRulesetIndex == -1 + ? null + : widget.rulesets[selectedRulesetIndex].$1, + ); + }, + ), title: const Text( 'Choose Ruleset', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), @@ -49,11 +59,11 @@ class _ChooseRulesetViewState extends State { return TitleDescriptionListTile( onPressed: () async { setState(() { - selectedRulesetIndex = index; - }); - Future.delayed(const Duration(milliseconds: 500), () { - if (!context.mounted) return; - Navigator.of(context).pop(widget.rulesets[index].$1); + if (selectedRulesetIndex == index) { + selectedRulesetIndex = -1; + } else { + selectedRulesetIndex = index; + } }); }, title: widget.rulesets[index].$2, From 3d12f0c160c09d6fa22d4c87470095be7fff7b34 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 6 Dec 2025 17:23:20 +0100 Subject: [PATCH 08/32] Fixed width problem in choose game view --- .../create_game/choose_game_view.dart | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_game_view.dart b/lib/presentation/views/main_menu/create_game/choose_game_view.dart index 5b2463b..992f02b 100644 --- a/lib/presentation/views/main_menu/create_game/choose_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_game_view.dart @@ -49,36 +49,35 @@ class _ChooseGameViewState extends State { ), centerTitle: true, ), - body: Container( - decoration: CustomTheme.standardBoxDecoration, - padding: const EdgeInsets.all(10), - child: Column( - children: [ - CustomSearchBar( + body: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: CustomSearchBar( controller: searchBarController, hintText: 'Game Name', ), - const SizedBox(height: 5), - Expanded( - child: ListView.builder( - itemCount: widget.games.length, - itemBuilder: (BuildContext context, int index) { - return TitleDescriptionListTile( - title: widget.games[index].$1, - description: widget.games[index].$2, - badgeText: translateRulesetToString(widget.games[index].$3), - isHighlighted: selectedGameIndex == index, - onPressed: () async { - setState(() { - selectedGameIndex = index; - }); - }, - ); - }, - ), + ), + const SizedBox(height: 5), + Expanded( + child: ListView.builder( + itemCount: widget.games.length, + itemBuilder: (BuildContext context, int index) { + return TitleDescriptionListTile( + title: widget.games[index].$1, + description: widget.games[index].$2, + badgeText: translateRulesetToString(widget.games[index].$3), + isHighlighted: selectedGameIndex == index, + onPressed: () async { + setState(() { + selectedGameIndex = index; + }); + }, + ); + }, ), - ], - ), + ), + ], ), ); } From a1a995777bdb288abf679e2b6c77dc0c2404f5c7 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 6 Dec 2025 17:23:40 +0100 Subject: [PATCH 09/32] Adjusted margin --- .../views/main_menu/create_game/create_game_view.dart | 4 +--- .../widgets/tiles/title_description_list_tile.dart | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index eb5468c..f83d723 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -123,7 +123,7 @@ class _CreateGameViewState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 5), child: TextInputField( controller: _gameNameController, hintText: 'Game name', @@ -213,7 +213,6 @@ class _CreateGameViewState extends State { }, ), ), - CustomWidthButton( text: 'Create game', sizeRelativeToWidth: 0.95, @@ -232,7 +231,6 @@ class _CreateGameViewState extends State { } : null, ), - const SizedBox(height: 20), ], ), ), diff --git a/lib/presentation/widgets/tiles/title_description_list_tile.dart b/lib/presentation/widgets/tiles/title_description_list_tile.dart index 7bc4221..2d517f2 100644 --- a/lib/presentation/widgets/tiles/title_description_list_tile.dart +++ b/lib/presentation/widgets/tiles/title_description_list_tile.dart @@ -24,7 +24,7 @@ class TitleDescriptionListTile extends StatelessWidget { return GestureDetector( onTap: onPressed, child: AnimatedContainer( - margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 10), padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12), decoration: isHighlighted ? CustomTheme.highlightedBoxDecoration From 6ae0471fa214ab4ee1f900464facf3b1ce2dd6a8 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 6 Dec 2025 17:42:11 +0100 Subject: [PATCH 10/32] Implemented final navigation to GameResultView --- .../create_game/create_game_view.dart | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index f83d723..bbb392b 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/enums.dart'; @@ -8,6 +9,7 @@ import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/presentation/views/main_menu/create_game/choose_game_view.dart'; import 'package:game_tracker/presentation/views/main_menu/create_game/choose_group_view.dart'; import 'package:game_tracker/presentation/views/main_menu/create_game/choose_ruleset_view.dart'; +import 'package:game_tracker/presentation/views/main_menu/game_result_view.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'; @@ -225,9 +227,20 @@ class _CreateGameViewState extends State { group: selectedGroup!, players: selectedPlayers, ); - // TODO: Replace with navigation to GameResultView() - print('Created game: $game'); - Navigator.pop(context); + final db = Provider.of( + context, + listen: false, + ); + await db.gameDao.addGame(game: game); + if (context.mounted) { + Navigator.pushReplacement( + context, + CupertinoPageRoute( + fullscreenDialog: true, + builder: (context) => GameResultView(game: game), + ), + ); + } } : null, ), From d2d0a82c9b2cf3a86f478ad5d838cd196c65c986 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 18:58:49 +0100 Subject: [PATCH 11/32] Implemented deselecting game --- .../main_menu/create_game/choose_game_view.dart | 10 ++++++---- .../main_menu/create_game/create_game_view.dart | 13 ++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_game_view.dart b/lib/presentation/views/main_menu/create_game/choose_game_view.dart index 992f02b..53a4fcb 100644 --- a/lib/presentation/views/main_menu/create_game/choose_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_game_view.dart @@ -38,9 +38,7 @@ class _ChooseGameViewState extends State { leading: IconButton( icon: const Icon(Icons.arrow_back_ios), onPressed: () { - Navigator.of( - context, - ).pop(selectedGameIndex == -1 ? null : selectedGameIndex); + Navigator.of(context).pop(selectedGameIndex); }, ), title: const Text( @@ -70,7 +68,11 @@ class _ChooseGameViewState extends State { isHighlighted: selectedGameIndex == index, onPressed: () async { setState(() { - selectedGameIndex = index; + if (selectedGameIndex == index) { + selectedGameIndex = -1; + } else { + selectedGameIndex = index; + } }); }, ); diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index bbb392b..4bf05e7 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -148,11 +148,14 @@ class _CreateGameViewState extends State { ), ), ); - selectedRuleset = games[selectedGameIndex].$3; - selectedRulesetIndex = rulesets.indexWhere( - (r) => r.$1 == selectedRuleset, - ); - setState(() {}); + if (selectedGameIndex != -1) { + setState(() { + selectedRuleset = games[selectedGameIndex].$3; + selectedRulesetIndex = rulesets.indexWhere( + (r) => r.$1 == selectedRuleset, + ); + }); + } }, ), ChooseTile( From f60c11fc08a2d5b912cae2930c5aa2fd66afbc1f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 19:01:19 +0100 Subject: [PATCH 12/32] Added searchbar for choose_group_view --- .../create_game/choose_group_view.dart | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_group_view.dart b/lib/presentation/views/main_menu/create_game/choose_group_view.dart index de512bf..f805d6e 100644 --- a/lib/presentation/views/main_menu/create_game/choose_group_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_group_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/data/dto/group.dart'; +import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart'; class ChooseGroupView extends StatefulWidget { @@ -19,6 +20,8 @@ class ChooseGroupView extends StatefulWidget { class _ChooseGroupViewState extends State { late int selectedGroupIndex; + final TextEditingController controller = TextEditingController(); + final String hintText = 'Group Name'; @override void initState() { @@ -49,26 +52,36 @@ class _ChooseGroupViewState extends State { ), centerTitle: true, ), - body: ListView.builder( - padding: const EdgeInsets.only(bottom: 85), - itemCount: widget.groups.length, - itemBuilder: (BuildContext context, int index) { - return GestureDetector( - onTap: () { - setState(() { - if (selectedGroupIndex == index) { - selectedGroupIndex = -1; - } else { - selectedGroupIndex = index; - } - }); - }, - child: GroupTile( - group: widget.groups[index], - isHighlighted: selectedGroupIndex == index, + body: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: CustomSearchBar(controller: controller, hintText: hintText), + ), + Expanded( + child: ListView.builder( + padding: const EdgeInsets.only(bottom: 85), + itemCount: widget.groups.length, + itemBuilder: (BuildContext context, int index) { + return GestureDetector( + onTap: () { + setState(() { + if (selectedGroupIndex == index) { + selectedGroupIndex = -1; + } else { + selectedGroupIndex = index; + } + }); + }, + child: GroupTile( + group: widget.groups[index], + isHighlighted: selectedGroupIndex == index, + ), + ); + }, ), - ); - }, + ), + ], ), ); } From b4ba4f8d74bcb842bda144f5b90da00b407f43db Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 19:12:43 +0100 Subject: [PATCH 13/32] Implemented search bar in choose_group_view --- .../create_game/choose_group_view.dart | 56 ++++++++++++++----- .../create_game/create_game_view.dart | 8 +-- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_group_view.dart b/lib/presentation/views/main_menu/create_game/choose_group_view.dart index f805d6e..e27adf3 100644 --- a/lib/presentation/views/main_menu/create_game/choose_group_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_group_view.dart @@ -6,12 +6,12 @@ import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart'; class ChooseGroupView extends StatefulWidget { final List groups; - final int initialGroupIndex; + final String initialGroupId; const ChooseGroupView({ super.key, required this.groups, - required this.initialGroupIndex, + required this.initialGroupId, }); @override @@ -19,13 +19,15 @@ class ChooseGroupView extends StatefulWidget { } class _ChooseGroupViewState extends State { - late int selectedGroupIndex; + late String selectedGroupId; final TextEditingController controller = TextEditingController(); final String hintText = 'Group Name'; + late final List filteredGroups; @override void initState() { - selectedGroupIndex = widget.initialGroupIndex; + selectedGroupId = widget.initialGroupId; + filteredGroups = [...widget.groups]; super.initState(); } @@ -40,9 +42,11 @@ class _ChooseGroupViewState extends State { icon: const Icon(Icons.arrow_back_ios), onPressed: () { Navigator.of(context).pop( - selectedGroupIndex == -1 + selectedGroupId == '' ? null - : widget.groups[selectedGroupIndex], + : widget.groups.firstWhere( + (group) => group.id == selectedGroupId, + ), ); }, ), @@ -56,26 +60,34 @@ class _ChooseGroupViewState extends State { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 10), - child: CustomSearchBar(controller: controller, hintText: hintText), + child: CustomSearchBar( + controller: controller, + hintText: hintText, + onChanged: (value) { + setState(() { + filterGroups(value); + }); + }, + ), ), Expanded( child: ListView.builder( padding: const EdgeInsets.only(bottom: 85), - itemCount: widget.groups.length, + itemCount: filteredGroups.length, itemBuilder: (BuildContext context, int index) { return GestureDetector( onTap: () { setState(() { - if (selectedGroupIndex == index) { - selectedGroupIndex = -1; + if (selectedGroupId != filteredGroups[index].id) { + selectedGroupId = filteredGroups[index].id; } else { - selectedGroupIndex = index; + selectedGroupId = ''; } }); }, child: GroupTile( - group: widget.groups[index], - isHighlighted: selectedGroupIndex == index, + group: filteredGroups[index], + isHighlighted: selectedGroupId == filteredGroups[index].id, ), ); }, @@ -85,4 +97,22 @@ class _ChooseGroupViewState extends State { ), ); } + + /// Filters the groups based on the search query. + /// TODO: Maybe implement also targetting player names? + void filterGroups(String query) { + setState(() { + if (query.isEmpty) { + filteredGroups.clear(); + filteredGroups.addAll(widget.groups); + } else { + filteredGroups.clear(); + filteredGroups.addAll( + widget.groups.where( + (group) => group.name.toLowerCase().contains(query.toLowerCase()), + ), + ); + } + }); + } } diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 4bf05e7..fdae05c 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -47,7 +47,7 @@ class _CreateGameViewState extends State { /// The index of the currently selected group in [groupsList] to mark it in /// the [ChooseGroupView] - int selectedGroupIndex = -1; + String selectedGroupId = ''; /// The currently selected ruleset Ruleset? selectedRuleset; @@ -189,13 +189,11 @@ class _CreateGameViewState extends State { MaterialPageRoute( builder: (context) => ChooseGroupView( groups: groupsList, - initialGroupIndex: selectedGroupIndex, + initialGroupId: selectedGroupId, ), ), ); - selectedGroupIndex = groupsList.indexWhere( - (g) => g.id == selectedGroup?.id, - ); + selectedGroupId = selectedGroup?.id ?? ''; setState(() {}); }, ), From 6c0cb92e567d6ccb9a42326153c9d64fe658fe7e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 19:15:49 +0100 Subject: [PATCH 14/32] Removed unneccesary title in tupe --- .../main_menu/create_game/choose_ruleset_view.dart | 6 +++--- .../views/main_menu/create_game/create_game_view.dart | 10 +++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart index 13a9647..537f749 100644 --- a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart @@ -4,7 +4,7 @@ import 'package:game_tracker/core/enums.dart'; import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart'; class ChooseRulesetView extends StatefulWidget { - final List<(Ruleset, String, String)> rulesets; + final List<(Ruleset, String)> rulesets; final int initialRulesetIndex; const ChooseRulesetView({ @@ -66,8 +66,8 @@ class _ChooseRulesetViewState extends State { } }); }, - title: widget.rulesets[index].$2, - description: widget.rulesets[index].$3, + title: translateRulesetToString(widget.rulesets[index].$1), + description: widget.rulesets[index].$2, isHighlighted: selectedRulesetIndex == index, ); }, diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index fdae05c..0a6a505 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -61,28 +61,24 @@ class _CreateGameViewState extends State { /// The currently selected players List? selectedPlayers; - /// List of available rulesets with their display names and descriptions - /// as tuples of (Ruleset, String, String) + /// List of available rulesets with their descriptions + /// as tuples of (Ruleset, String) /// TODO: Replace when rulesets are implemented - List<(Ruleset, String, String)> rulesets = [ + List<(Ruleset, String)> rulesets = [ ( Ruleset.singleWinner, - 'Single Winner', 'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.', ), ( Ruleset.singleLoser, - 'Single Loser', 'Exactly one loser is determined; last place receives the penalty or consequence.', ), ( Ruleset.mostPoints, - 'Most Points', 'Traditional ruleset: the player with the most points wins.', ), ( Ruleset.leastPoints, - 'Least Points', 'Inverse scoring: the player with the fewest points wins.', ), ]; From c214a26c5401e25353b9dacaca497ea86f6e4800 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 19:17:20 +0100 Subject: [PATCH 15/32] Removed onChanged --- .../views/main_menu/create_game/create_game_view.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 0a6a505..2d227d1 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -125,9 +125,6 @@ class _CreateGameViewState extends State { child: TextInputField( controller: _gameNameController, hintText: 'Game name', - onChanged: (value) { - setState(() {}); - }, ), ), ChooseTile( From 45abc79f9515a71e0b63f998b08feb96160f65f3 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 19:17:56 +0100 Subject: [PATCH 16/32] Removed unneccesary db declaration --- .../views/main_menu/create_game/create_game_view.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 2d227d1..4e7c227 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -221,10 +221,6 @@ class _CreateGameViewState extends State { group: selectedGroup!, players: selectedPlayers, ); - final db = Provider.of( - context, - listen: false, - ); await db.gameDao.addGame(game: game); if (context.mounted) { Navigator.pushReplacement( From a1ed17355a2a1581a784985044774ffd9d742d2e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 19:23:38 +0100 Subject: [PATCH 17/32] Minimum of 2 players required to create a game, unless group is selected --- .../views/main_menu/create_game/create_game_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 4e7c227..59e3c0d 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -245,7 +245,7 @@ class _CreateGameViewState extends State { bool _enableCreateGameButton() { return _gameNameController.text.isNotEmpty && (selectedGroup != null || - (selectedPlayers != null && selectedPlayers!.isNotEmpty)) && + (selectedPlayers != null && selectedPlayers!.length > 1)) && selectedRuleset != null; } } From 7bc75d60a72c4a25e05d791e742f400e42951586 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 19:31:11 +0100 Subject: [PATCH 18/32] Fixed text overflow --- .../tiles/title_description_list_tile.dart | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/presentation/widgets/tiles/title_description_list_tile.dart b/lib/presentation/widgets/tiles/title_description_list_tile.dart index 2d517f2..7a138a0 100644 --- a/lib/presentation/widgets/tiles/title_description_list_tile.dart +++ b/lib/presentation/widgets/tiles/title_description_list_tile.dart @@ -38,17 +38,23 @@ class TitleDescriptionListTile extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - title, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, + SizedBox( + width: 230, + child: Text( + title, + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), ), ), if (badgeText != null) ...[ const Spacer(), Container( + constraints: const BoxConstraints(maxWidth: 100), margin: const EdgeInsets.only(top: 4), padding: const EdgeInsets.symmetric( vertical: 2, @@ -60,6 +66,9 @@ class TitleDescriptionListTile extends StatelessWidget { ), child: Text( badgeText!, + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, style: const TextStyle( color: Colors.white, fontSize: 12, From f1f3fd7b6ed0f564a2a5aa07b17726fccb266b5f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 22:03:28 +0100 Subject: [PATCH 19/32] Fixed deselecting game bug --- .../views/main_menu/create_game/create_game_view.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 59e3c0d..7a7d904 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -141,14 +141,17 @@ class _CreateGameViewState extends State { ), ), ); - if (selectedGameIndex != -1) { - setState(() { + setState(() { + if (selectedGameIndex != -1) { + print('selectedGameIndex: $selectedGameIndex'); selectedRuleset = games[selectedGameIndex].$3; selectedRulesetIndex = rulesets.indexWhere( (r) => r.$1 == selectedRuleset, ); - }); - } + } else { + selectedRuleset = null; + } + }); }, ), ChooseTile( From 708157df54232352ac41384a8a3d2ffb8651e03d Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 22:15:19 +0100 Subject: [PATCH 20/32] Implemented leaving players in the player selection and filtering those who are in the group --- .../create_game/create_game_view.dart | 3 ++- .../widgets/player_selection.dart | 21 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 7a7d904..6643a52 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -196,7 +196,8 @@ class _CreateGameViewState extends State { Expanded( child: PlayerSelection( key: ValueKey(selectedGroup?.id ?? 'no_group'), - initialPlayers: selectedGroup == null + initialSelectedPlayers: selectedPlayers ?? [], + availablePlayers: selectedGroup == null ? playerList : playerList .where( diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 092a613..d48e241 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -11,12 +11,14 @@ import 'package:provider/provider.dart'; class PlayerSelection extends StatefulWidget { final Function(List value) onChanged; - final List initialPlayers; + final List availablePlayers; + final List? initialSelectedPlayers; const PlayerSelection({ super.key, required this.onChanged, - this.initialPlayers = const [], + this.availablePlayers = const [], + this.initialSelectedPlayers, }); @override @@ -51,10 +53,19 @@ class _PlayerSelectionState extends State { suggestedPlayers = skeletonData; _allPlayersFuture.then((loadedPlayers) { setState(() { - if (widget.initialPlayers.isNotEmpty) { - allPlayers = [...widget.initialPlayers]; - suggestedPlayers = [...widget.initialPlayers]; + // If a list of available players is provided, use that list. + if (widget.availablePlayers.isNotEmpty) { + allPlayers = [...widget.availablePlayers]; + suggestedPlayers = [...widget.availablePlayers]; + + if (widget.initialSelectedPlayers != null) { + // Ensures that only players available for selection are pre-selected. + selectedPlayers = widget.initialSelectedPlayers! + .where((p) => widget.availablePlayers.contains(p)) + .toList(); + } } else { + // Otherwise, use the loaded players from the database. loadedPlayers.sort((a, b) => a.name.compareTo(b.name)); allPlayers = [...loadedPlayers]; suggestedPlayers = [...loadedPlayers]; From 7cff48ebc07429c0805cb3a75bbf0c50824b4f93 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 7 Dec 2025 22:23:43 +0100 Subject: [PATCH 21/32] Added empty message --- .../create_game/choose_group_view.dart | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_group_view.dart b/lib/presentation/views/main_menu/create_game/choose_group_view.dart index e27adf3..4e81a86 100644 --- a/lib/presentation/views/main_menu/create_game/choose_group_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_group_view.dart @@ -3,6 +3,7 @@ import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart'; 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; @@ -71,26 +72,35 @@ class _ChooseGroupViewState extends State { ), ), Expanded( - child: ListView.builder( - padding: const EdgeInsets.only(bottom: 85), - itemCount: filteredGroups.length, - itemBuilder: (BuildContext context, int index) { - return GestureDetector( - onTap: () { - setState(() { - if (selectedGroupId != filteredGroups[index].id) { - selectedGroupId = filteredGroups[index].id; - } else { - selectedGroupId = ''; - } - }); - }, - child: GroupTile( - group: filteredGroups[index], - isHighlighted: selectedGroupId == filteredGroups[index].id, - ), - ); - }, + child: Visibility( + visible: filteredGroups.isNotEmpty, + replacement: const TopCenteredMessage( + icon: Icons.info, + title: 'No group', + message: 'There is no group matching your search.', + ), + child: ListView.builder( + padding: const EdgeInsets.only(bottom: 85), + itemCount: filteredGroups.length, + itemBuilder: (BuildContext context, int index) { + return GestureDetector( + onTap: () { + setState(() { + if (selectedGroupId != filteredGroups[index].id) { + selectedGroupId = filteredGroups[index].id; + } else { + selectedGroupId = ''; + } + }); + }, + child: GroupTile( + group: filteredGroups[index], + isHighlighted: + selectedGroupId == filteredGroups[index].id, + ), + ); + }, + ), ), ), ], From 062c2681bf0216f942ac807fe085e694e55f9012 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 9 Dec 2025 18:10:31 +0100 Subject: [PATCH 22/32] Corrected info message --- .../views/main_menu/create_game/choose_group_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/choose_group_view.dart b/lib/presentation/views/main_menu/create_game/choose_group_view.dart index 4e81a86..c30d6ef 100644 --- a/lib/presentation/views/main_menu/create_game/choose_group_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_group_view.dart @@ -76,8 +76,8 @@ class _ChooseGroupViewState extends State { visible: filteredGroups.isNotEmpty, replacement: const TopCenteredMessage( icon: Icons.info, - title: 'No group', - message: 'There is no group matching your search.', + title: 'Info', + message: 'There is no group matching your search', ), child: ListView.builder( padding: const EdgeInsets.only(bottom: 85), From 701500c7e2db2d358da6dcdc25b5a84460851f1e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 9 Dec 2025 18:26:05 +0100 Subject: [PATCH 23/32] Fixed state problem with games list --- .../create_game/create_game_view.dart | 8 ++++-- .../views/main_menu/game_history_view.dart | 27 ++++++++++--------- .../views/main_menu/game_result_view.dart | 4 ++- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 6643a52..8b3271c 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -17,7 +17,8 @@ import 'package:game_tracker/presentation/widgets/tiles/choose_tile.dart'; import 'package:provider/provider.dart'; class CreateGameView extends StatefulWidget { - const CreateGameView({super.key}); + final VoidCallback? onWinnerChanged; + const CreateGameView({super.key, this.onWinnerChanged}); @override State createState() => _CreateGameViewState(); @@ -231,7 +232,10 @@ class _CreateGameViewState extends State { context, CupertinoPageRoute( fullscreenDialog: true, - builder: (context) => GameResultView(game: game), + builder: (context) => GameResultView( + game: game, + onWinnerChanged: widget.onWinnerChanged, + ), ), ); } diff --git a/lib/presentation/views/main_menu/game_history_view.dart b/lib/presentation/views/main_menu/game_history_view.dart index 5d52ac8..46cb1db 100644 --- a/lib/presentation/views/main_menu/game_history_view.dart +++ b/lib/presentation/views/main_menu/game_history_view.dart @@ -98,17 +98,16 @@ class _GameHistoryViewState extends State { } return GameHistoryTile( onTap: () async { - await Navigator.push( + Navigator.push( context, CupertinoPageRoute( fullscreenDialog: true, - builder: (context) => - GameResultView(game: games[index]), + builder: (context) => GameResultView( + game: games[index], + onWinnerChanged: refreshGameList, + ), ), ); - setState(() { - _gameListFuture = db.gameDao.getAllGames(); - }); }, game: games[index], ); @@ -123,17 +122,13 @@ class _GameHistoryViewState extends State { text: 'Create Game', sizeRelativeToWidth: 0.90, onPressed: () async { - await Navigator.push( + Navigator.push( context, MaterialPageRoute( - builder: (context) { - return const CreateGameView(); - }, + builder: (context) => + CreateGameView(onWinnerChanged: refreshGameList), ), ); - setState(() { - _gameListFuture = db.gameDao.getAllGames(); - }); }, ), ), @@ -141,4 +136,10 @@ class _GameHistoryViewState extends State { ), ); } + + void refreshGameList() { + setState(() { + _gameListFuture = db.gameDao.getAllGames(); + }); + } } diff --git a/lib/presentation/views/main_menu/game_result_view.dart b/lib/presentation/views/main_menu/game_result_view.dart index f13553b..6e60410 100644 --- a/lib/presentation/views/main_menu/game_result_view.dart +++ b/lib/presentation/views/main_menu/game_result_view.dart @@ -9,8 +9,9 @@ import 'package:provider/provider.dart'; class GameResultView extends StatefulWidget { final Game game; - const GameResultView({super.key, required this.game}); + final VoidCallback? onWinnerChanged; + const GameResultView({super.key, required this.game, this.onWinnerChanged}); @override State createState() => _GameResultViewState(); } @@ -131,6 +132,7 @@ class _GameResultViewState extends State { winnerId: _selectedPlayer!.id, ); } + widget.onWinnerChanged?.call(); } List getAllPlayers(Game game) { From c4094a547e40de2989d713544a01f7a6e52a0d00 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 9 Dec 2025 20:52:14 +0100 Subject: [PATCH 24/32] Fixed button state problem --- .../views/main_menu/create_game/create_game_view.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 8b3271c..b9a8141 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -126,6 +126,11 @@ class _CreateGameViewState extends State { child: TextInputField( controller: _gameNameController, hintText: 'Game name', + onChanged: (value) { + setState(() { + _gameNameController; + }); + }, ), ), ChooseTile( @@ -144,7 +149,6 @@ class _CreateGameViewState extends State { ); setState(() { if (selectedGameIndex != -1) { - print('selectedGameIndex: $selectedGameIndex'); selectedRuleset = games[selectedGameIndex].$3; selectedRulesetIndex = rulesets.indexWhere( (r) => r.$1 == selectedRuleset, @@ -251,9 +255,12 @@ class _CreateGameViewState extends State { /// Determines whether the "Create Game" button should be enabled based on /// the current state of the input fields. bool _enableCreateGameButton() { - return _gameNameController.text.isNotEmpty && + final value = + _gameNameController.text.isNotEmpty && (selectedGroup != null || (selectedPlayers != null && selectedPlayers!.length > 1)) && selectedRuleset != null; + print('button: $value'); + return value; } } From 27ff599a888fa8ae38e10e1ba7fa6969e4748fde Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 9 Dec 2025 20:52:38 +0100 Subject: [PATCH 25/32] Reverted method --- .../views/main_menu/create_game/create_game_view.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index b9a8141..d7e89d6 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -255,12 +255,9 @@ class _CreateGameViewState extends State { /// Determines whether the "Create Game" button should be enabled based on /// the current state of the input fields. bool _enableCreateGameButton() { - final value = - _gameNameController.text.isNotEmpty && + return _gameNameController.text.isNotEmpty && (selectedGroup != null || (selectedPlayers != null && selectedPlayers!.length > 1)) && selectedRuleset != null; - print('button: $value'); - return value; } } From 0d0806dfbb6b90fdb7b580cfd9d36f68e28694c3 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 9 Dec 2025 22:25:45 +0100 Subject: [PATCH 26/32] Implemented listener --- .../views/main_menu/create_game/create_game_view.dart | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index d7e89d6..427ebf2 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -93,6 +93,10 @@ class _CreateGameViewState extends State { @override void initState() { super.initState(); + _gameNameController.addListener(() { + setState(() {}); + }); + db = Provider.of(context, listen: false); _allGroupsFuture = db.groupDao.getAllGroups(); @@ -126,11 +130,6 @@ class _CreateGameViewState extends State { child: TextInputField( controller: _gameNameController, hintText: 'Game name', - onChanged: (value) { - setState(() { - _gameNameController; - }); - }, ), ), ChooseTile( From 5d2fed74ac345a3e360d68021af8aa6a3cebc152 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 9 Dec 2025 23:59:34 +0100 Subject: [PATCH 27/32] Fixed sorting problem --- lib/presentation/widgets/player_selection.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index d48e241..8007d39 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -55,8 +55,9 @@ class _PlayerSelectionState extends State { setState(() { // If a list of available players is provided, use that list. if (widget.availablePlayers.isNotEmpty) { + widget.availablePlayers.sort((a, b) => a.name.compareTo(b.name)); allPlayers = [...widget.availablePlayers]; - suggestedPlayers = [...widget.availablePlayers]; + suggestedPlayers = [...allPlayers]; if (widget.initialSelectedPlayers != null) { // Ensures that only players available for selection are pre-selected. From 23cdddfbd96d495c2d71afc405aa2b34d5dbe2d1 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 10 Dec 2025 12:38:39 +0100 Subject: [PATCH 28/32] Fixed problem with player selection --- .../create_game/create_game_view.dart | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 427ebf2..b1165b5 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -197,25 +197,30 @@ class _CreateGameViewState extends State { setState(() {}); }, ), - Expanded( - child: PlayerSelection( - key: ValueKey(selectedGroup?.id ?? 'no_group'), - initialSelectedPlayers: selectedPlayers ?? [], - availablePlayers: selectedGroup == null - ? playerList - : playerList - .where( - (p) => !selectedGroup!.members.any( - (m) => m.id == p.id, - ), - ) - .toList(), - onChanged: (value) { - setState(() { - selectedPlayers = value; - }); - }, - ), + FutureBuilder( + future: _allPlayersFuture, + builder: + (BuildContext context, AsyncSnapshot snapshot) => + Expanded( + child: PlayerSelection( + key: ValueKey(selectedGroup?.id ?? 'no_group'), + initialSelectedPlayers: selectedPlayers ?? [], + availablePlayers: selectedGroup == null + ? playerList + : playerList + .where( + (p) => !selectedGroup!.members.any( + (m) => m.id == p.id, + ), + ) + .toList(), + onChanged: (value) { + setState(() { + selectedPlayers = value; + }); + }, + ), + ), ), CustomWidthButton( text: 'Create game', From 93ced81e7e5efdbac0cc2a7fca70cf817339b9f0 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 10 Dec 2025 13:55:31 +0100 Subject: [PATCH 29/32] Fixed filtering problem with object reference --- lib/presentation/widgets/player_selection.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 8007d39..e2114b2 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -62,7 +62,11 @@ class _PlayerSelectionState extends State { if (widget.initialSelectedPlayers != null) { // Ensures that only players available for selection are pre-selected. selectedPlayers = widget.initialSelectedPlayers! - .where((p) => widget.availablePlayers.contains(p)) + .where( + (p) => widget.availablePlayers.any( + (available) => available.id == p.id, + ), + ) .toList(); } } else { From e1626225ac57e526b0ade59b792b5e562c564b45 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 10 Dec 2025 13:56:03 +0100 Subject: [PATCH 30/32] Removed future builder --- .../create_game/create_game_view.dart | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index b1165b5..427ebf2 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -197,30 +197,25 @@ class _CreateGameViewState extends State { setState(() {}); }, ), - FutureBuilder( - future: _allPlayersFuture, - builder: - (BuildContext context, AsyncSnapshot snapshot) => - Expanded( - child: PlayerSelection( - key: ValueKey(selectedGroup?.id ?? 'no_group'), - initialSelectedPlayers: selectedPlayers ?? [], - availablePlayers: selectedGroup == null - ? playerList - : playerList - .where( - (p) => !selectedGroup!.members.any( - (m) => m.id == p.id, - ), - ) - .toList(), - onChanged: (value) { - setState(() { - selectedPlayers = value; - }); - }, - ), - ), + Expanded( + child: PlayerSelection( + key: ValueKey(selectedGroup?.id ?? 'no_group'), + initialSelectedPlayers: selectedPlayers ?? [], + availablePlayers: selectedGroup == null + ? playerList + : playerList + .where( + (p) => !selectedGroup!.members.any( + (m) => m.id == p.id, + ), + ) + .toList(), + onChanged: (value) { + setState(() { + selectedPlayers = value; + }); + }, + ), ), CustomWidthButton( text: 'Create game', From 3b3d298ff55118b2bb7df3826d9535eb72c17455 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 10 Dec 2025 14:04:15 +0100 Subject: [PATCH 31/32] Small refactoring --- .../create_game/create_game_view.dart | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 427ebf2..8b6f516 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -43,6 +43,12 @@ class _CreateGameViewState extends State { /// List of all players from the database List playerList = []; + /// List of players filtered based on the selected group + /// If a group is selected, this list contains all players from [playerList] + /// who are not members of the selected group. If no group is selected, + /// this list is identical to [playerList]. + List filteredPlayerList = []; + /// The currently selected group Group? selectedGroup; @@ -57,6 +63,8 @@ class _CreateGameViewState extends State { /// the [ChooseRulesetView] int selectedRulesetIndex = -1; + /// The index of the currently selected game in [games] to mark it in + /// the [ChooseGameView] int selectedGameIndex = -1; /// The currently selected players @@ -105,6 +113,7 @@ class _CreateGameViewState extends State { Future.wait([_allGroupsFuture, _allPlayersFuture]).then((result) async { groupsList = result[0] as List; playerList = result[1] as List; + filteredPlayerList = List.from(playerList); }); } @@ -194,6 +203,15 @@ class _CreateGameViewState extends State { ), ); selectedGroupId = selectedGroup?.id ?? ''; + if (selectedGroup != null) { + filteredPlayerList = playerList + .where( + (p) => !selectedGroup!.members.any((m) => m.id == p.id), + ) + .toList(); + } else { + filteredPlayerList = List.from(playerList); + } setState(() {}); }, ), @@ -201,15 +219,7 @@ class _CreateGameViewState extends State { child: PlayerSelection( key: ValueKey(selectedGroup?.id ?? 'no_group'), initialSelectedPlayers: selectedPlayers ?? [], - availablePlayers: selectedGroup == null - ? playerList - : playerList - .where( - (p) => !selectedGroup!.members.any( - (m) => m.id == p.id, - ), - ) - .toList(), + availablePlayers: filteredPlayerList, onChanged: (value) { setState(() { selectedPlayers = value; @@ -226,7 +236,7 @@ class _CreateGameViewState extends State { Game game = Game( name: _gameNameController.text.trim(), createdAt: DateTime.now(), - group: selectedGroup!, + group: selectedGroup, players: selectedPlayers, ); await db.gameDao.addGame(game: game); From d0059b44a8c62928e03c3691a01081b2c986cba3 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 10 Dec 2025 21:41:37 +0100 Subject: [PATCH 32/32] Moved statement --- .../views/main_menu/create_game/create_game_view.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 8b6f516..eef2de6 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -113,8 +113,9 @@ class _CreateGameViewState extends State { Future.wait([_allGroupsFuture, _allPlayersFuture]).then((result) async { groupsList = result[0] as List; playerList = result[1] as List; - filteredPlayerList = List.from(playerList); }); + + filteredPlayerList = List.from(playerList); } @override