10 Commits

Author SHA1 Message Date
e364e15d0a Merge pull request 'Spieler erstellen in CreateGroupView' (#43) from feature/42-spieler-erstellen-in-create-group-view-implementieren into development
Reviewed-on: #43
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
2025-11-20 21:18:21 +00:00
195ebf569a Added icon as parameter for custom search bar
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m5s
2025-11-20 22:17:20 +01:00
eb7b247cae Fixed error adding player with empty name 2025-11-20 22:11:23 +01:00
01fede2951 Added Visibility Widget 2025-11-20 22:09:08 +01:00
b67f321276 Added name parameters and function doc 2025-11-20 22:05:44 +01:00
d16beed490 felix mach jetzt
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m4s
2025-11-20 21:54:43 +01:00
0111774308 Trim whitespace from group and player names in CreateGroupView
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m7s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2025-11-20 21:26:59 +01:00
8ff3c01435 added missing consts & mounted check
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m0s
Pull Request Pipeline / lint (pull_request) Successful in 2m4s
2025-11-20 16:59:49 +01:00
bce4cdcb2d Enable player creation via search bar in CreateGroupView
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m0s
Pull Request Pipeline / lint (pull_request) Failing after 2m5s
2025-11-20 16:53:38 +01:00
fa0e9a5dfd add trailing button functionality to CustomSearchBar 2025-11-20 16:53:14 +01:00
2 changed files with 141 additions and 41 deletions

View File

@@ -37,6 +37,24 @@ class _CreateGroupViewState extends State<CreateGroupView> {
void initState() { void initState() {
super.initState(); super.initState();
db = Provider.of<AppDatabase>(context, listen: false); db = Provider.of<AppDatabase>(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 = db.playerDao.getAllPlayers();
_allPlayersFuture.then((loadedPlayers) { _allPlayersFuture.then((loadedPlayers) {
setState(() { setState(() {
@@ -99,6 +117,19 @@ class _CreateGroupViewState extends State<CreateGroupView> {
minHeight: 45, minHeight: 45,
), ),
hintText: 'Search for players', hintText: 'Search for players',
trailingButtonShown: true,
trailingButtonicon: Icons.add_circle,
trailingButtonEnabled: _searchBarController.text
.trim()
.isNotEmpty,
onTrailingButtonPressed: () async {
addNewPlayerFromSearch(
context: context,
searchBarController: _searchBarController,
db: db,
loadPlayerList: loadPlayerList,
);
},
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
if (value.isEmpty) { if (value.isEmpty) {
@@ -216,25 +247,16 @@ class _CreateGroupViewState extends State<CreateGroupView> {
layoutBuilder: layoutBuilder:
AnimatedSwitcher.defaultLayoutBuilder, AnimatedSwitcher.defaultLayoutBuilder,
), ),
child: child: Visibility(
visible:
(suggestedPlayers.isEmpty && (suggestedPlayers.isEmpty &&
allPlayers.isNotEmpty) allPlayers.isNotEmpty),
? TopCenteredMessage( replacement: ListView.builder(
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, itemCount: suggestedPlayers.length,
itemBuilder: itemBuilder:
(BuildContext context, int index) { (BuildContext context, int index) {
return TextIconListTile( return TextIconListTile(
text: suggestedPlayers[index] text: suggestedPlayers[index].name,
.name,
onPressed: () { onPressed: () {
setState(() { setState(() {
if (!selectedPlayers.contains( if (!selectedPlayers.contains(
@@ -244,8 +266,9 @@ class _CreateGroupViewState extends State<CreateGroupView> {
suggestedPlayers[index], suggestedPlayers[index],
); );
selectedPlayers.sort( selectedPlayers.sort(
(a, b) => a.name (a, b) => a.name.compareTo(
.compareTo(b.name), b.name,
),
); );
suggestedPlayers.remove( suggestedPlayers.remove(
suggestedPlayers[index], suggestedPlayers[index],
@@ -256,6 +279,16 @@ class _CreateGroupViewState extends State<CreateGroupView> {
); );
}, },
), ),
child: TopCenteredMessage(
icon: Icons.info,
title: 'Info',
message:
(selectedPlayers.length ==
allPlayers.length)
? 'No more players to add.'
: 'No players found with that name.',
),
),
), ),
); );
}, },
@@ -274,7 +307,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
: () async { : () async {
bool success = await db.groupDao.addGroup( bool success = await db.groupDao.addGroup(
group: Group( group: Group(
name: _groupNameController.text, name: _groupNameController.text.trim(),
members: selectedPlayers, members: selectedPlayers,
), ),
); );
@@ -307,3 +340,47 @@ class _CreateGroupViewState extends State<CreateGroupView> {
); );
} }
} }
/// 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;
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),
),
),
),
);
}
}

View File

@@ -6,11 +6,19 @@ class CustomSearchBar extends StatelessWidget {
final String hintText; final String hintText;
final ValueChanged<String>? onChanged; final ValueChanged<String>? onChanged;
final BoxConstraints? constraints; final BoxConstraints? constraints;
final bool trailingButtonShown;
final bool trailingButtonEnabled;
final VoidCallback? onTrailingButtonPressed;
final IconData trailingButtonicon;
const CustomSearchBar({ const CustomSearchBar({
super.key, super.key,
required this.controller, required this.controller,
required this.hintText, required this.hintText,
this.trailingButtonShown = false,
this.trailingButtonicon = Icons.clear,
this.trailingButtonEnabled = true,
this.onTrailingButtonPressed,
this.onChanged, this.onChanged,
this.constraints, this.constraints,
}); });
@@ -22,9 +30,24 @@ class CustomSearchBar extends StatelessWidget {
constraints: constraints:
constraints ?? const BoxConstraints(maxHeight: 45, minHeight: 45), constraints ?? const BoxConstraints(maxHeight: 45, minHeight: 45),
hintText: hintText, hintText: hintText,
onChanged: onChanged, onChanged: trailingButtonEnabled ? onChanged : null,
hintStyle: WidgetStateProperty.all(const TextStyle(fontSize: 16)), hintStyle: WidgetStateProperty.all(const TextStyle(fontSize: 16)),
leading: const Icon(Icons.search), leading: const Icon(Icons.search),
trailing: [
Visibility(
visible: trailingButtonShown,
child: GestureDetector(
onTap: trailingButtonEnabled ? onTrailingButtonPressed : null,
child: Icon(
trailingButtonicon,
color: trailingButtonEnabled
? null
: Colors.grey.withValues(alpha: 0.2),
),
),
),
const SizedBox(width: 5),
],
backgroundColor: WidgetStateProperty.all(CustomTheme.boxColor), backgroundColor: WidgetStateProperty.all(CustomTheme.boxColor),
side: WidgetStateProperty.all(BorderSide(color: CustomTheme.boxBorder)), side: WidgetStateProperty.all(BorderSide(color: CustomTheme.boxBorder)),
shape: WidgetStateProperty.all( shape: WidgetStateProperty.all(