MVP #141

Merged
flixcoo merged 705 commits from development into main 2026-01-09 12:55:50 +00:00
Showing only changes of commit 7781284289 - Show all commits

View File

@@ -3,10 +3,15 @@ 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/group.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/presentation/widgets/custom_search_bar.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/text_input_field.dart';
import 'package:game_tracker/presentation/widgets/tiles/text_icon_list_tile.dart';
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:skeletonizer/skeletonizer.dart'; import 'package:skeletonizer/skeletonizer.dart';
import 'package:uuid/uuid.dart';
class CreateGroupView extends StatefulWidget { class CreateGroupView extends StatefulWidget {
const CreateGroupView({super.key}); const CreateGroupView({super.key});
@@ -61,29 +66,12 @@ class _CreateGroupViewState extends State<CreateGroupView> {
children: [ children: [
Container( Container(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: TextField( child: TextInputField(
controller: _groupNameController, controller: _groupNameController,
hintText: 'Group name',
onChanged: (value) { onChanged: (value) {
setState(() {}); setState(() {});
}, },
decoration: InputDecoration(
filled: true,
fillColor: CustomTheme.boxColor,
hint: Text(
"Group name",
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 18),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(color: CustomTheme.boxBorder),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(color: CustomTheme.boxBorder),
),
floatingLabelBehavior: FloatingLabelBehavior.never,
),
), ),
), ),
Expanded( Expanded(
@@ -104,29 +92,16 @@ class _CreateGroupViewState extends State<CreateGroupView> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SearchBar( CustomSearchBar(
controller: _searchBarController, 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(
TextStyle(fontSize: 16),
),
leading: Icon(Icons.search),
backgroundColor: WidgetStateProperty.all(
CustomTheme.boxColor,
),
side: WidgetStateProperty.all(
BorderSide(color: CustomTheme.boxBorder),
),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
if (value.isEmpty) { if (value.isEmpty) {
suggestedPlayers = [...allPlayers]; suggestedPlayers = allPlayers.where((player) {
return !selectedPlayers.contains(player);
}).toList();
} else { } else {
suggestedPlayers = allPlayers.where((player) { suggestedPlayers = allPlayers.where((player) {
final bool nameMatches = player.name final bool nameMatches = player.name
@@ -156,40 +131,18 @@ class _CreateGroupViewState extends State<CreateGroupView> {
runSpacing: 8.0, runSpacing: 8.0,
children: <Widget>[ children: <Widget>[
for (var selectedPlayer in selectedPlayers) for (var selectedPlayer in selectedPlayers)
Container( TextIconTile(
padding: EdgeInsets.all(5), text: selectedPlayer.name,
decoration: BoxDecoration( icon: Icons.close,
color: CustomTheme.onBoxColor, onIconTap: () {
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 12),
Flexible(
child: Text(
selectedPlayer.name,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
SizedBox(width: 3),
GestureDetector(
child: const Icon(Icons.close, size: 20),
onTap: () {
setState(() { setState(() {
final currentSearch = _searchBarController final currentSearch = _searchBarController.text
.text
.toLowerCase(); .toLowerCase();
selectedPlayers.remove(selectedPlayer); selectedPlayers.remove(selectedPlayer);
if (currentSearch.isEmpty || if (currentSearch.isEmpty ||
selectedPlayer.name selectedPlayer.name.toLowerCase().contains(
.toLowerCase() currentSearch,
.contains(currentSearch)) { )) {
suggestedPlayers.add(selectedPlayer); suggestedPlayers.add(selectedPlayer);
suggestedPlayers.sort( suggestedPlayers.sort(
(a, b) => a.name.compareTo(b.name), (a, b) => a.name.compareTo(b.name),
@@ -200,9 +153,6 @@ class _CreateGroupViewState extends State<CreateGroupView> {
), ),
], ],
), ),
),
],
),
SizedBox(height: 10), SizedBox(height: 10),
Text( Text(
"Alle Spieler:", "Alle Spieler:",
@@ -214,7 +164,11 @@ class _CreateGroupViewState extends State<CreateGroupView> {
SizedBox(height: 10), SizedBox(height: 10),
FutureBuilder( FutureBuilder(
future: _allPlayersFuture, future: _allPlayersFuture,
builder: (BuildContext context, AsyncSnapshot<List<Player>> snapshot) { builder:
(
BuildContext context,
AsyncSnapshot<List<Player>> snapshot,
) {
if (snapshot.hasError) { if (snapshot.hasError) {
return const Center( return const Center(
child: TopCenteredMessage( child: TopCenteredMessage(
@@ -224,7 +178,8 @@ class _CreateGroupViewState extends State<CreateGroupView> {
), ),
); );
} }
if (snapshot.connectionState == ConnectionState.done && if (snapshot.connectionState ==
ConnectionState.done &&
(!snapshot.hasData || (!snapshot.hasData ||
snapshot.data!.isEmpty || snapshot.data!.isEmpty ||
(selectedPlayers.isEmpty && (selectedPlayers.isEmpty &&
@@ -238,7 +193,8 @@ class _CreateGroupViewState extends State<CreateGroupView> {
); );
} }
final bool isLoading = final bool isLoading =
snapshot.connectionState == ConnectionState.waiting; snapshot.connectionState ==
ConnectionState.waiting;
return Expanded( return Expanded(
child: Skeletonizer( child: Skeletonizer(
effect: PulseEffect( effect: PulseEffect(
@@ -248,12 +204,13 @@ class _CreateGroupViewState extends State<CreateGroupView> {
), ),
enabled: isLoading, enabled: isLoading,
enableSwitchAnimation: true, enableSwitchAnimation: true,
switchAnimationConfig: const SwitchAnimationConfig( switchAnimationConfig:
const SwitchAnimationConfig(
duration: Duration(milliseconds: 200), duration: Duration(milliseconds: 200),
switchInCurve: Curves.linear, switchInCurve: Curves.linear,
switchOutCurve: Curves.linear, switchOutCurve: Curves.linear,
transitionBuilder: transitionBuilder: AnimatedSwitcher
AnimatedSwitcher.defaultTransitionBuilder, .defaultTransitionBuilder,
layoutBuilder: layoutBuilder:
AnimatedSwitcher.defaultLayoutBuilder, AnimatedSwitcher.defaultLayoutBuilder,
), ),
@@ -271,41 +228,12 @@ class _CreateGroupViewState extends State<CreateGroupView> {
) )
: ListView.builder( : ListView.builder(
itemCount: suggestedPlayers.length, itemCount: suggestedPlayers.length,
itemBuilder: (BuildContext context, int index) { itemBuilder:
return Container( (BuildContext context, int index) {
margin: const EdgeInsets.symmetric( return IconListTile(
horizontal: 5, text: suggestedPlayers[index]
vertical: 5, .name,
), icon: Icons.add,
padding: const EdgeInsets.symmetric(
horizontal: 10,
),
decoration: BoxDecoration(
color: CustomTheme.boxColor,
border: Border.all(
color: CustomTheme.boxBorder,
),
borderRadius: BorderRadius.circular(
12,
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Text(
suggestedPlayers[index].name,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
IconButton(
icon: Icon(Icons.add, size: 20),
onPressed: () { onPressed: () {
setState(() { setState(() {
if (!selectedPlayers.contains( if (!selectedPlayers.contains(
@@ -324,9 +252,6 @@ class _CreateGroupViewState extends State<CreateGroupView> {
} }
}); });
}, },
),
],
),
); );
}, },
), ),
@@ -348,11 +273,12 @@ class _CreateGroupViewState extends State<CreateGroupView> {
(_groupNameController.text.isEmpty || selectedPlayers.isEmpty) (_groupNameController.text.isEmpty || selectedPlayers.isEmpty)
? null ? null
: () async { : () async {
String id = "ID_" + _groupNameController.text;
String name = _groupNameController.text;
List<Player> members = selectedPlayers;
bool success = await db.groupDao.addGroup( bool success = await db.groupDao.addGroup(
group: Group(id: id, name: name, members: members), group: Group(
id: Uuid().v4(),
name: _groupNameController.text,
members: selectedPlayers,
),
); );
if (success) { if (success) {
_groupNameController.clear(); _groupNameController.clear();