CreateGroupView erstellt #28

Merged
flixcoo merged 37 commits from feature/5-creategroupview-erstellen into development 2025-11-19 17:32:44 +00:00
Showing only changes of commit 35f2f8754a - Show all commits

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/presentation/widgets/full_width_button.dart'; import 'package:game_tracker/presentation/widgets/full_width_button.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
@@ -15,24 +16,30 @@ class CreateGroupView extends StatefulWidget {
} }
class _CreateGroupViewState extends State<CreateGroupView> { class _CreateGroupViewState extends State<CreateGroupView> {
List<Player> selectedPlayers = [ List<Player> selectedPlayers = [];
Player(id: '0', name: 'Player 0'), List<Player> suggestedPlayers = [];
Player(id: '0', name: 'Player 0'), List<Player> allPlayers = [];
Player(id: '0', name: 'Player 0'), late final AppDatabase db;
Player(id: '0', name: 'Player 0'),
];
late Future<List<Player>> _allPlayersFuture; late Future<List<Player>> _allPlayersFuture;
late final List<Player> skeletonData = List.filled( late final List<Player> skeletonData = List.filled(
7, 7,
Player(id: '0', name: 'Player 0'), Player(id: '0', name: 'Player 0'),
); );
final _groupNameController = TextEditingController();
final _searchBarController = TextEditingController();
@override @override
sneeex marked this conversation as resolved Outdated

id nach #35 nicht mehr notwendig

id nach #35 nicht mehr notwendig
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final db = Provider.of<AppDatabase>(context, listen: false); db = Provider.of<AppDatabase>(context, listen: false);
_allPlayersFuture = db.playerDao.getAllPlayers(); _allPlayersFuture = db.playerDao.getAllPlayers();
_allPlayersFuture.then((loadedPlayers) {
setState(() {
allPlayers = loadedPlayers;
suggestedPlayers = loadedPlayers;
});
});
} }
@override @override
@@ -55,6 +62,10 @@ class _CreateGroupViewState extends State<CreateGroupView> {
Container( Container(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: TextField( child: TextField(
controller: _groupNameController,
onChanged: (value) {
setState(() {});
},
decoration: InputDecoration( decoration: InputDecoration(
filled: true, filled: true,
fillColor: CustomTheme.boxColor, fillColor: CustomTheme.boxColor,
@@ -90,6 +101,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SearchBar( SearchBar(
controller: _searchBarController,
constraints: BoxConstraints(maxHeight: 45, minHeight: 45), constraints: BoxConstraints(maxHeight: 45, minHeight: 45),
hintText: "Search for players", hintText: "Search for players",
hintStyle: WidgetStateProperty.all( hintStyle: WidgetStateProperty.all(
@@ -107,10 +119,23 @@ class _CreateGroupViewState extends State<CreateGroupView> {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
sneeex marked this conversation as resolved Outdated

single quotes

single quotes
), ),
sneeex marked this conversation as resolved Outdated

const TextStyle()

`const TextStyle()`
onChanged: (value) {
setState(() {
if (value.isEmpty) {
suggestedPlayers = allPlayers;
} else {
sneeex marked this conversation as resolved Outdated

const SizedBox()

`const SizedBox()`
suggestedPlayers = allPlayers.where((player) {
return player.name.toLowerCase().contains(
value.toLowerCase(),
);
}).toList();
}
});
sneeex marked this conversation as resolved Outdated

lieber var player in selectedPlayers?

lieber `var player in selectedPlayers`?
},
), ),
SizedBox(height: 10), SizedBox(height: 10),
Text( Text(
"Ausgewählte Spieler: (X)", "Ausgewählte Spieler: (${selectedPlayers.length})",
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@@ -123,7 +148,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
spacing: 8.0, spacing: 8.0,
runSpacing: 8.0, runSpacing: 8.0,
children: <Widget>[ children: <Widget>[
for (var player in selectedPlayers) for (var selectedPlayer in selectedPlayers)
Container( Container(
padding: EdgeInsets.all(5), padding: EdgeInsets.all(5),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -136,7 +161,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
children: [ children: [
SizedBox(width: 12), SizedBox(width: 12),
Text( Text(
player.name, selectedPlayer.name,
sneeex marked this conversation as resolved Outdated

const SizedBox()

`const SizedBox()`
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@@ -147,7 +172,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
child: const Icon(Icons.close, size: 20), child: const Icon(Icons.close, size: 20),
onTap: () { onTap: () {
setState(() { setState(() {
selectedPlayers.remove(player); selectedPlayers.remove(selectedPlayer);
}); });
}, },
), ),
@@ -183,7 +208,10 @@ class _CreateGroupViewState extends State<CreateGroupView> {
} }
if (snapshot.connectionState == if (snapshot.connectionState ==
ConnectionState.done && ConnectionState.done &&
(!snapshot.hasData || snapshot.data!.isEmpty)) { (!snapshot.hasData ||
snapshot.data!.isEmpty ||
(suggestedPlayers.isEmpty &&
allPlayers.isEmpty))) {
return const Center( return const Center(
child: TopCenteredMessage( child: TopCenteredMessage(
icon: Icons.info, icon: Icons.info,
@@ -195,9 +223,6 @@ class _CreateGroupViewState extends State<CreateGroupView> {
final bool isLoading = final bool isLoading =
snapshot.connectionState == snapshot.connectionState ==
ConnectionState.waiting; ConnectionState.waiting;
final List<Player> players = isLoading
? skeletonData
: (snapshot.data ?? []);
return Expanded( return Expanded(
child: Skeletonizer( child: Skeletonizer(
effect: PulseEffect( effect: PulseEffect(
@@ -217,48 +242,69 @@ class _CreateGroupViewState extends State<CreateGroupView> {
layoutBuilder: layoutBuilder:
AnimatedSwitcher.defaultLayoutBuilder, AnimatedSwitcher.defaultLayoutBuilder,
), ),
child: ListView.builder( child:
itemCount: players.length, (suggestedPlayers.isEmpty &&
itemBuilder: !allPlayers.isEmpty)
(BuildContext context, int index) { ? TopCenteredMessage(
return Container( icon: Icons.info,
margin: const EdgeInsets.symmetric( title: 'Info',
horizontal: 5, message:
vertical: 5, 'No players found with that name.',
), )
padding: const EdgeInsets.symmetric( : ListView.builder(
horizontal: 10, itemCount: suggestedPlayers.length,
), itemBuilder: (BuildContext context, int index) {
decoration: BoxDecoration( return Container(
color: CustomTheme.boxColor, margin: const EdgeInsets.symmetric(
border: Border.all( horizontal: 5,
color: CustomTheme.boxBorder, vertical: 5,
), ),
borderRadius: BorderRadius.circular( padding: const EdgeInsets.symmetric(
12, horizontal: 10,
), ),
), decoration: BoxDecoration(
child: Row( color: CustomTheme.boxColor,
mainAxisAlignment: border: Border.all(
sneeex marked this conversation as resolved Outdated

single quotes

single quotes
MainAxisAlignment.spaceBetween, color: CustomTheme.boxBorder,
mainAxisSize: MainAxisSize.max, ),
children: [ borderRadius:
sneeex marked this conversation as resolved Outdated

Button sieht deaktiviert aus wie ein SecondaryButton, styling anpassen. Ggf. Attribut festlegen, welches festlegt ob Button primary styling oder secondary styling hat

Button sieht deaktiviert aus wie ein SecondaryButton, styling anpassen. Ggf. Attribut festlegen, welches festlegt ob Button primary styling oder secondary styling hat
Text( BorderRadius.circular(12),
players[index].name, ),
style: TextStyle( child: Row(
fontSize: 16, mainAxisAlignment:
fontWeight: FontWeight.w500, MainAxisAlignment
.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
sneeex marked this conversation as resolved Outdated

const bzw #35 reinmergen und weglassen

`const` bzw #35 reinmergen und weglassen
Text(
sneeex marked this conversation as resolved Outdated

Füg hier folgende Zeile ein und entferne die anderen beiden ifs

if (!context.mounted) return;
Füg hier folgende Zeile ein und entferne die anderen beiden `if`s ```dart if (!context.mounted) return; ```

Füg hier folgende Zeile ein und entferne die anderen beiden ifs

if (!context.mounted) return;

Aber wenn's nicht mounted ist, will ich doch trotzdem noch die Felder clearen?

> Füg hier folgende Zeile ein und entferne die anderen beiden `if`s > ```dart > if (!context.mounted) return; > ``` Aber wenn's nicht mounted ist, will ich doch trotzdem noch die Felder clearen?

Der Context ist nicht mehr verfügbar, wenn z.B. das Widget garnicht mehr im widget tree ist. Also ist das dann eh egal

Der Context ist nicht mehr verfügbar, wenn z.B. das Widget garnicht mehr im widget tree ist. Also ist das dann eh egal
suggestedPlayers[index].name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
), ),
), IconButton(
IconButton( icon: Icon(
sneeex marked this conversation as resolved Outdated

Bei asynchronen aufgaben mounted check

Bei asynchronen aufgaben `mounted` check
icon: Icon(Icons.add, size: 20), Icons.add,
onPressed: () {}, size: 20,
sneeex marked this conversation as resolved Outdated

mounted check

`mounted` check
), ),
], onPressed: () {
), setState(() {
sneeex marked this conversation as resolved Outdated

const

`const`
); //GroupTile(group: groups[index]); if (!selectedPlayers.contains(
}, suggestedPlayers[index],
sneeex marked this conversation as resolved Outdated

single quotes

single quotes
), )) {
selectedPlayers.add(
suggestedPlayers[index],
);
}
});
},
),
],
),
sneeex marked this conversation as resolved Outdated

const

`const`
);
},
),
), ),
); );
}, },
@@ -271,14 +317,39 @@ class _CreateGroupViewState extends State<CreateGroupView> {
text: "Create group", text: "Create group",
infillColor: CustomTheme.primaryColor, infillColor: CustomTheme.primaryColor,
borderColor: CustomTheme.primaryColor, borderColor: CustomTheme.primaryColor,
disabledInfillColor: CustomTheme.boxColor,
sizeRelativeToWidth: 0.95, sizeRelativeToWidth: 0.95,
onPressed: () {}, onPressed:
(_groupNameController.text.isEmpty || selectedPlayers.isEmpty)
? null
: () {
String id = "ID_" + _groupNameController.text;
String name = _groupNameController.text;
List<Player> members = selectedPlayers;
db.groupDao.addGroup(
group: Group(id: id, name: name, members: members),
);
print(name);
print(id);
for (int i = 0; i < members.length; i++) {
print(members[i].name);
print(members[i].id);
}
if (true) {
//eigentlich wenn create group erfolgreich
_groupNameController.clear();
_searchBarController.clear();
selectedPlayers.clear();
}
setState(() {});
},
), ),
SizedBox(height: 10), SizedBox(height: 10),
FullWidthButton( FullWidthButton(
text: "Cancel", text: "Cancel",
infillColor: CustomTheme.boxColor, infillColor: CustomTheme.boxColor,
borderColor: CustomTheme.primaryColor, borderColor: CustomTheme.primaryColor,
disabledInfillColor: CustomTheme.boxColor,
sizeRelativeToWidth: 0.95, sizeRelativeToWidth: 0.95,
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
@@ -293,6 +364,22 @@ class _CreateGroupViewState extends State<CreateGroupView> {
Future<void> addSamplePlayers(BuildContext context) async { Future<void> addSamplePlayers(BuildContext context) async {
final db = Provider.of<AppDatabase>(context, listen: false); final db = Provider.of<AppDatabase>(context, listen: false);
/*await db.groupDao.addGroup(
group: Group(
id: "dg1",
name: "Debug Gruppe 1",
members: [
Player(id: '1', name: 'Spieler 1'),
Player(id: '2', name: 'Spieler 2'),
Player(id: '3', name: 'Spieler 3'),
],
),
);
final group = await db.groupDao.getGroupById(groupId: "dg1");
print(group.name);
print(group.id);
print(group.members.length);
*/
final playerCount = await db.playerDao.getPlayerCount(); final playerCount = await db.playerDao.getPlayerCount();
if (playerCount == 0) { if (playerCount == 0) {
for (int i = 1; i <= 10; i++) { for (int i = 1; i <= 10; i++) {