From fa0e9a5dfd7c957e51b370bb9505211b3da51ca0 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 20 Nov 2025 16:53:14 +0100 Subject: [PATCH 1/9] add trailing button functionality to CustomSearchBar --- .../widgets/custom_search_bar.dart | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/presentation/widgets/custom_search_bar.dart b/lib/presentation/widgets/custom_search_bar.dart index b482efb..61a2154 100644 --- a/lib/presentation/widgets/custom_search_bar.dart +++ b/lib/presentation/widgets/custom_search_bar.dart @@ -6,11 +6,17 @@ class CustomSearchBar extends StatelessWidget { final String hintText; final ValueChanged? onChanged; final BoxConstraints? constraints; + final bool trailingButtonEnabled; + final bool trailingButtonShown; + final VoidCallback? onTrailingButtonPressed; const CustomSearchBar({ super.key, required this.controller, required this.hintText, + this.trailingButtonShown = false, + this.trailingButtonEnabled = true, + this.onTrailingButtonPressed, this.onChanged, this.constraints, }); @@ -25,6 +31,20 @@ class CustomSearchBar extends StatelessWidget { onChanged: onChanged, hintStyle: WidgetStateProperty.all(const TextStyle(fontSize: 16)), leading: const Icon(Icons.search), + trailing: trailingButtonShown + ? [ + GestureDetector( + onTap: onTrailingButtonPressed, + child: Icon( + Icons.add_circle, + color: trailingButtonEnabled + ? null + : Colors.grey.withValues(alpha: 0.2), + ), + ), + SizedBox(width: 5), + ] + : null, backgroundColor: WidgetStateProperty.all(CustomTheme.boxColor), side: WidgetStateProperty.all(BorderSide(color: CustomTheme.boxBorder)), shape: WidgetStateProperty.all( From bce4cdcb2d32caf4a0b3d6398909d08ec2ee4652 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 20 Nov 2025 16:53:38 +0100 Subject: [PATCH 2/9] Enable player creation via search bar in CreateGroupView --- .../views/main_menu/create_group_view.dart | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/lib/presentation/views/main_menu/create_group_view.dart b/lib/presentation/views/main_menu/create_group_view.dart index db8890f..45f5af8 100644 --- a/lib/presentation/views/main_menu/create_group_view.dart +++ b/lib/presentation/views/main_menu/create_group_view.dart @@ -37,6 +37,10 @@ class _CreateGroupViewState extends State { void initState() { super.initState(); db = Provider.of(context, listen: false); + loadPlayerList(); + } + + void loadPlayerList() { _allPlayersFuture = db.playerDao.getAllPlayers(); _allPlayersFuture.then((loadedPlayers) { setState(() { @@ -99,6 +103,41 @@ class _CreateGroupViewState extends State { minHeight: 45, ), hintText: 'Search for players', + trailingButtonShown: true, + trailingButtonEnabled: + _searchBarController.text.isNotEmpty, + onTrailingButtonPressed: () async { + String playerName = _searchBarController.text; + if (playerName.isEmpty) return; + bool success = await db.playerDao.addPlayer( + player: Player(name: playerName), + ); + if (success) { + loadPlayerList(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: CustomTheme.boxColor, + content: Center( + child: Text( + 'Successfully added player $playerName.', + style: TextStyle(color: Colors.white), + ), + ), + ), + ); + _searchBarController.clear(); + } else { + SnackBar( + backgroundColor: CustomTheme.boxColor, + content: Center( + child: Text( + 'Could not add player $playerName.', + style: TextStyle(color: Colors.white), + ), + ), + ); + } + }, onChanged: (value) { setState(() { if (value.isEmpty) { From 8ff3c014358af8341caf4a0f17d1e95a09cc1e35 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 20 Nov 2025 16:59:49 +0100 Subject: [PATCH 3/9] added missing consts & mounted check --- lib/presentation/views/main_menu/create_group_view.dart | 5 +++-- lib/presentation/widgets/custom_search_bar.dart | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/main_menu/create_group_view.dart b/lib/presentation/views/main_menu/create_group_view.dart index 45f5af8..3beb62a 100644 --- a/lib/presentation/views/main_menu/create_group_view.dart +++ b/lib/presentation/views/main_menu/create_group_view.dart @@ -112,6 +112,7 @@ class _CreateGroupViewState extends State { bool success = await db.playerDao.addPlayer( player: Player(name: playerName), ); + if (!context.mounted) return; if (success) { loadPlayerList(); ScaffoldMessenger.of(context).showSnackBar( @@ -120,7 +121,7 @@ class _CreateGroupViewState extends State { content: Center( child: Text( 'Successfully added player $playerName.', - style: TextStyle(color: Colors.white), + style: const TextStyle(color: Colors.white), ), ), ), @@ -132,7 +133,7 @@ class _CreateGroupViewState extends State { content: Center( child: Text( 'Could not add player $playerName.', - style: TextStyle(color: Colors.white), + style: const TextStyle(color: Colors.white), ), ), ); diff --git a/lib/presentation/widgets/custom_search_bar.dart b/lib/presentation/widgets/custom_search_bar.dart index 61a2154..8a483e5 100644 --- a/lib/presentation/widgets/custom_search_bar.dart +++ b/lib/presentation/widgets/custom_search_bar.dart @@ -42,7 +42,7 @@ class CustomSearchBar extends StatelessWidget { : Colors.grey.withValues(alpha: 0.2), ), ), - SizedBox(width: 5), + const SizedBox(width: 5), ] : null, backgroundColor: WidgetStateProperty.all(CustomTheme.boxColor), From 01117743087c2d74d45d1f514c6682758d166e10 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 20 Nov 2025 21:26:59 +0100 Subject: [PATCH 4/9] Trim whitespace from group and player names in CreateGroupView --- lib/presentation/views/main_menu/create_group_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/create_group_view.dart b/lib/presentation/views/main_menu/create_group_view.dart index 3beb62a..057a16f 100644 --- a/lib/presentation/views/main_menu/create_group_view.dart +++ b/lib/presentation/views/main_menu/create_group_view.dart @@ -107,7 +107,7 @@ class _CreateGroupViewState extends State { trailingButtonEnabled: _searchBarController.text.isNotEmpty, onTrailingButtonPressed: () async { - String playerName = _searchBarController.text; + String playerName = _searchBarController.text.trim(); if (playerName.isEmpty) return; bool success = await db.playerDao.addPlayer( player: Player(name: playerName), @@ -314,7 +314,7 @@ class _CreateGroupViewState extends State { : () async { bool success = await db.groupDao.addGroup( group: Group( - name: _groupNameController.text, + name: _groupNameController.text.trim(), members: selectedPlayers, ), ); From d16beed490dbb6c84a73c80d1da1a2d435e82e9a Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Thu, 20 Nov 2025 21:54:43 +0100 Subject: [PATCH 5/9] felix mach jetzt --- .../views/main_menu/create_group_view.dart | 92 ++++++++++++------- .../widgets/custom_search_bar.dart | 31 ++++--- 2 files changed, 76 insertions(+), 47 deletions(-) diff --git a/lib/presentation/views/main_menu/create_group_view.dart b/lib/presentation/views/main_menu/create_group_view.dart index 057a16f..590f342 100644 --- a/lib/presentation/views/main_menu/create_group_view.dart +++ b/lib/presentation/views/main_menu/create_group_view.dart @@ -37,9 +37,23 @@ class _CreateGroupViewState extends State { void initState() { super.initState(); db = Provider.of(context, listen: false); + _searchBarController.addListener(() { + setState(() {}); + }); + _groupNameController.addListener(() { + setState(() {}); + }); loadPlayerList(); } + @override + void dispose() { + _groupNameController.dispose(); + _searchBarController + .dispose(); // Listener entfernen und Controller aufräumen + super.dispose(); + } + void loadPlayerList() { _allPlayersFuture = db.playerDao.getAllPlayers(); _allPlayersFuture.then((loadedPlayers) { @@ -104,40 +118,16 @@ class _CreateGroupViewState extends State { ), hintText: 'Search for players', trailingButtonShown: true, - trailingButtonEnabled: - _searchBarController.text.isNotEmpty, + trailingButtonEnabled: _searchBarController.text + .trim() + .isNotEmpty, onTrailingButtonPressed: () async { - String playerName = _searchBarController.text.trim(); - if (playerName.isEmpty) return; - bool success = await db.playerDao.addPlayer( - player: Player(name: playerName), + addNewPlayerFromSearch( + context, + _searchBarController, + db, + loadPlayerList, ); - if (!context.mounted) return; - if (success) { - loadPlayerList(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - backgroundColor: CustomTheme.boxColor, - content: Center( - child: Text( - 'Successfully added player $playerName.', - style: const TextStyle(color: Colors.white), - ), - ), - ), - ); - _searchBarController.clear(); - } else { - SnackBar( - backgroundColor: CustomTheme.boxColor, - content: Center( - child: Text( - 'Could not add player $playerName.', - style: const TextStyle(color: Colors.white), - ), - ), - ); - } }, onChanged: (value) { setState(() { @@ -347,3 +337,41 @@ class _CreateGroupViewState extends State { ); } } + +void addNewPlayerFromSearch( + context, + searchBarController, + db, + loadPlayerList, +) async { + String playerName = searchBarController.text.trim(); + bool success = await db.playerDao.addPlayer(player: Player(name: playerName)); + if (!context.mounted) return; + if (success) { + loadPlayerList(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: CustomTheme.boxColor, + content: Center( + child: Text( + 'Successfully added player $playerName.', + style: const TextStyle(color: Colors.white), + ), + ), + ), + ); + searchBarController.clear(); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: CustomTheme.boxColor, + content: Center( + child: Text( + 'Could not add player $playerName.', + style: const TextStyle(color: Colors.white), + ), + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/custom_search_bar.dart b/lib/presentation/widgets/custom_search_bar.dart index 8a483e5..4b5fd24 100644 --- a/lib/presentation/widgets/custom_search_bar.dart +++ b/lib/presentation/widgets/custom_search_bar.dart @@ -28,23 +28,24 @@ class CustomSearchBar extends StatelessWidget { constraints: constraints ?? const BoxConstraints(maxHeight: 45, minHeight: 45), hintText: hintText, - onChanged: onChanged, + onChanged: trailingButtonEnabled ? onChanged : null, hintStyle: WidgetStateProperty.all(const TextStyle(fontSize: 16)), leading: const Icon(Icons.search), - trailing: trailingButtonShown - ? [ - GestureDetector( - onTap: onTrailingButtonPressed, - child: Icon( - Icons.add_circle, - color: trailingButtonEnabled - ? null - : Colors.grey.withValues(alpha: 0.2), - ), - ), - const SizedBox(width: 5), - ] - : null, + trailing: [ + Visibility( + visible: trailingButtonShown, + child: GestureDetector( + onTap: onTrailingButtonPressed, + child: Icon( + Icons.add_circle, + color: trailingButtonEnabled + ? null + : Colors.grey.withValues(alpha: 0.2), + ), + ), + ), + const SizedBox(width: 5), + ], backgroundColor: WidgetStateProperty.all(CustomTheme.boxColor), side: WidgetStateProperty.all(BorderSide(color: CustomTheme.boxBorder)), shape: WidgetStateProperty.all( From b67f3212761db7080bd30b388eb3c10835efc891 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 20 Nov 2025 22:05:44 +0100 Subject: [PATCH 6/9] Added name parameters and function doc --- .../views/main_menu/create_group_view.dart | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/presentation/views/main_menu/create_group_view.dart b/lib/presentation/views/main_menu/create_group_view.dart index 590f342..5d53562 100644 --- a/lib/presentation/views/main_menu/create_group_view.dart +++ b/lib/presentation/views/main_menu/create_group_view.dart @@ -123,10 +123,10 @@ class _CreateGroupViewState extends State { .isNotEmpty, onTrailingButtonPressed: () async { addNewPlayerFromSearch( - context, - _searchBarController, - db, - loadPlayerList, + context: context, + searchBarController: _searchBarController, + db: db, + loadPlayerList: loadPlayerList, ); }, onChanged: (value) { @@ -338,12 +338,18 @@ class _CreateGroupViewState extends State { } } -void addNewPlayerFromSearch( - context, - searchBarController, - db, - loadPlayerList, -) async { +/// Adds a new player to the database from the search bar input. +/// Shows a snackbar indicating success or failure. +/// [context] - BuildContext to show the snackbar. +/// [searchBarController] - TextEditingController of the search bar. +/// [db] - AppDatabase instance to interact with the database. +/// [loadPlayerList] - Function to reload the player list after adding. +void addNewPlayerFromSearch({ + required BuildContext context, + required TextEditingController searchBarController, + required AppDatabase db, + required Function loadPlayerList, +}) async { String playerName = searchBarController.text.trim(); bool success = await db.playerDao.addPlayer(player: Player(name: playerName)); if (!context.mounted) return; From 01fede29519be32a8ecc166e4e82796ceb110f8f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 20 Nov 2025 22:09:08 +0100 Subject: [PATCH 7/9] Added Visibility Widget --- .../views/main_menu/create_group_view.dart | 80 ++++++++++--------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/lib/presentation/views/main_menu/create_group_view.dart b/lib/presentation/views/main_menu/create_group_view.dart index 5d53562..de8b2d2 100644 --- a/lib/presentation/views/main_menu/create_group_view.dart +++ b/lib/presentation/views/main_menu/create_group_view.dart @@ -246,46 +246,48 @@ class _CreateGroupViewState extends State { layoutBuilder: AnimatedSwitcher.defaultLayoutBuilder, ), - child: - (suggestedPlayers.isEmpty && - allPlayers.isNotEmpty) - ? TopCenteredMessage( - icon: Icons.info, - title: 'Info', - message: - (selectedPlayers.length == - allPlayers.length) - ? 'No more players to add.' - : 'No players found with that name.', - ) - : ListView.builder( - itemCount: suggestedPlayers.length, - itemBuilder: - (BuildContext context, int index) { - return TextIconListTile( - text: suggestedPlayers[index] - .name, - onPressed: () { - setState(() { - if (!selectedPlayers.contains( - suggestedPlayers[index], - )) { - selectedPlayers.add( - suggestedPlayers[index], - ); - selectedPlayers.sort( - (a, b) => a.name - .compareTo(b.name), - ); - suggestedPlayers.remove( - suggestedPlayers[index], - ); - } - }); - }, - ); + child: Visibility( + visible: + (suggestedPlayers.isEmpty && + allPlayers.isNotEmpty), + replacement: ListView.builder( + itemCount: suggestedPlayers.length, + itemBuilder: + (BuildContext context, int index) { + return TextIconListTile( + text: suggestedPlayers[index].name, + onPressed: () { + setState(() { + if (!selectedPlayers.contains( + suggestedPlayers[index], + )) { + selectedPlayers.add( + suggestedPlayers[index], + ); + selectedPlayers.sort( + (a, b) => a.name.compareTo( + b.name, + ), + ); + suggestedPlayers.remove( + suggestedPlayers[index], + ); + } + }); }, - ), + ); + }, + ), + child: TopCenteredMessage( + icon: Icons.info, + title: 'Info', + message: + (selectedPlayers.length == + allPlayers.length) + ? 'No more players to add.' + : 'No players found with that name.', + ), + ), ), ); }, From eb7b247cae739c0d76186b2048b20cf5e60ce4aa Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 20 Nov 2025 22:11:23 +0100 Subject: [PATCH 8/9] Fixed error adding player with empty name --- lib/presentation/widgets/custom_search_bar.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/widgets/custom_search_bar.dart b/lib/presentation/widgets/custom_search_bar.dart index 4b5fd24..f506605 100644 --- a/lib/presentation/widgets/custom_search_bar.dart +++ b/lib/presentation/widgets/custom_search_bar.dart @@ -35,7 +35,7 @@ class CustomSearchBar extends StatelessWidget { Visibility( visible: trailingButtonShown, child: GestureDetector( - onTap: onTrailingButtonPressed, + onTap: trailingButtonEnabled ? onTrailingButtonPressed : null, child: Icon( Icons.add_circle, color: trailingButtonEnabled From 195ebf569ab24c7a2899295cb00d9fc21d1527b4 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 20 Nov 2025 22:17:20 +0100 Subject: [PATCH 9/9] Added icon as parameter for custom search bar --- lib/presentation/views/main_menu/create_group_view.dart | 1 + lib/presentation/widgets/custom_search_bar.dart | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/create_group_view.dart b/lib/presentation/views/main_menu/create_group_view.dart index de8b2d2..c54369e 100644 --- a/lib/presentation/views/main_menu/create_group_view.dart +++ b/lib/presentation/views/main_menu/create_group_view.dart @@ -118,6 +118,7 @@ class _CreateGroupViewState extends State { ), hintText: 'Search for players', trailingButtonShown: true, + trailingButtonicon: Icons.add_circle, trailingButtonEnabled: _searchBarController.text .trim() .isNotEmpty, diff --git a/lib/presentation/widgets/custom_search_bar.dart b/lib/presentation/widgets/custom_search_bar.dart index f506605..e3fe976 100644 --- a/lib/presentation/widgets/custom_search_bar.dart +++ b/lib/presentation/widgets/custom_search_bar.dart @@ -6,15 +6,17 @@ class CustomSearchBar extends StatelessWidget { final String hintText; final ValueChanged? onChanged; final BoxConstraints? constraints; - final bool trailingButtonEnabled; final bool trailingButtonShown; + final bool trailingButtonEnabled; final VoidCallback? onTrailingButtonPressed; + final IconData trailingButtonicon; const CustomSearchBar({ super.key, required this.controller, required this.hintText, this.trailingButtonShown = false, + this.trailingButtonicon = Icons.clear, this.trailingButtonEnabled = true, this.onTrailingButtonPressed, this.onChanged, @@ -37,7 +39,7 @@ class CustomSearchBar extends StatelessWidget { child: GestureDetector( onTap: trailingButtonEnabled ? onTrailingButtonPressed : null, child: Icon( - Icons.add_circle, + trailingButtonicon, color: trailingButtonEnabled ? null : Colors.grey.withValues(alpha: 0.2),