CreateGameView erstellen #67
@@ -27,5 +27,19 @@ enum ExportResult { success, canceled, unknownException }
|
|||||||
/// - [Ruleset.singleWinner]: The game is won by a single player
|
/// - [Ruleset.singleWinner]: The game is won by a single player
|
||||||
/// - [Ruleset.singleLoser]: The game is lost by a single player
|
/// - [Ruleset.singleLoser]: The game is lost by a single player
|
||||||
/// - [Ruleset.mostPoints]: The player with the most points wins.
|
/// - [Ruleset.mostPoints]: The player with the most points wins.
|
||||||
/// - [Ruleset.lastPoints]: The player with the fewest points wins.
|
/// - [Ruleset.leastPoints]: The player with the fewest points wins.
|
||||||
enum Ruleset { singleWinner, singleLoser, mostPoints, lastPoints }
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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<ChooseGameView> createState() => _ChooseGameViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChooseGameViewState extends State<ChooseGameView> {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
flixcoo marked this conversation as resolved
Outdated
|
|||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import 'package:game_tracker/presentation/widgets/tiles/title_description_list_t
|
|||||||
class ChooseRulesetView extends StatefulWidget {
|
class ChooseRulesetView extends StatefulWidget {
|
||||||
final List<(Ruleset, String, String)> rulesets;
|
final List<(Ruleset, String, String)> rulesets;
|
||||||
final int initialRulesetIndex;
|
final int initialRulesetIndex;
|
||||||
|
|
||||||
const ChooseRulesetView({
|
const ChooseRulesetView({
|
||||||
super.key,
|
super.key,
|
||||||
required this.rulesets,
|
required this.rulesets,
|
||||||
@@ -41,78 +42,25 @@ class _ChooseRulesetViewState extends State<ChooseRulesetView> {
|
|||||||
),
|
),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: Column(
|
body: ListView.builder(
|
||||||
children: [
|
padding: const EdgeInsets.only(bottom: 85),
|
||||||
Container(
|
itemCount: widget.rulesets.length,
|
||||||
color: CustomTheme.backgroundColor,
|
itemBuilder: (BuildContext context, int index) {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
return TitleDescriptionListTile(
|
||||||
child: TabBar(
|
onPressed: () async {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
setState(() {
|
||||||
|
sneeex marked this conversation as resolved
Outdated
sneeex
commented
warum so viele comments? ist doch klar wofür die settings jeweils sind, also label indicator, divider etc warum so viele comments? ist doch klar wofür die settings jeweils sind, also label indicator, divider etc
flixcoo
commented
Wollte das einfach bisschen klarer aufteilen Wollte das einfach bisschen klarer aufteilen
|
|||||||
// Label Settings
|
selectedRulesetIndex = index;
|
||||||
labelStyle: const TextStyle(
|
});
|
||||||
fontSize: 16,
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
fontWeight: FontWeight.bold,
|
if (!context.mounted) return;
|
||||||
),
|
Navigator.of(context).pop(widget.rulesets[index].$1);
|
||||||
labelColor: Colors.white,
|
});
|
||||||
unselectedLabelStyle: const TextStyle(fontSize: 14),
|
},
|
||||||
unselectedLabelColor: Colors.white70,
|
title: widget.rulesets[index].$2,
|
||||||
// Indicator Settings
|
description: widget.rulesets[index].$3,
|
||||||
indicator: CustomTheme.standardBoxDecoration,
|
isHighlighted: selectedRulesetIndex == index,
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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/game.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/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_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/create_game/choose_ruleset_view.dart';
|
||||||
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
|
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
|
||||||
@@ -53,6 +54,8 @@ class _CreateGameViewState extends State<CreateGameView> {
|
|||||||
/// the [ChooseRulesetView]
|
/// the [ChooseRulesetView]
|
||||||
int selectedRulesetIndex = -1;
|
int selectedRulesetIndex = -1;
|
||||||
|
|
||||||
|
int selectedGameIndex = -1;
|
||||||
|
|
||||||
/// The currently selected players
|
/// The currently selected players
|
||||||
List<Player>? selectedPlayers;
|
List<Player>? selectedPlayers;
|
||||||
|
|
||||||
@@ -75,12 +78,17 @@ class _CreateGameViewState extends State<CreateGameView> {
|
|||||||
'Traditional ruleset: the player with the most points wins.',
|
'Traditional ruleset: the player with the most points wins.',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Ruleset.lastPoints,
|
Ruleset.leastPoints,
|
||||||
'Least Points',
|
'Least Points',
|
||||||
'Inverse scoring: the player with the fewest points wins.',
|
'Inverse scoring: the player with the fewest points wins.',
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
List<(String, String, Ruleset)> games = [
|
||||||
|
flixcoo marked this conversation as resolved
Outdated
sneeex
commented
Du hast doch in enums die func translateRulesetToString für die Namen, warum nutzt du nicht die einfach für die Namen hier? Du hast doch in enums die func translateRulesetToString für die Namen, warum nutzt du nicht die einfach für die Namen hier?
flixcoo
commented
Hab ich danach erst implementiert, erledigt. Hab ich danach erst implementiert, erledigt.
|
|||||||
|
('Cabo', 'A memory card game', Ruleset.leastPoints),
|
||||||
|
('Uno', 'The Classic', Ruleset.singleWinner),
|
||||||
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -122,6 +130,27 @@ class _CreateGameViewState extends State<CreateGameView> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
flixcoo marked this conversation as resolved
Outdated
sneeex
commented
bitte mit listener implementieren, außerdem hat das _gameNameController hier keinen Effekt, würde auch nur mit setState und leerem Inhalt gehen bitte mit listener implementieren, außerdem hat das _gameNameController hier keinen Effekt, würde auch nur mit setState und leerem Inhalt gehen
|
|||||||
|
ChooseTile(
|
||||||
|
title: 'Game',
|
||||||
|
flixcoo marked this conversation as resolved
Outdated
sneeex
commented
die Function für onChanged wird nicht gebraucht, weil ja schon ein Listener auf den TextEditingController gesetzt ist die Function für onChanged wird nicht gebraucht, weil ja schon ein Listener auf den TextEditingController gesetzt ist
|
|||||||
|
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(
|
ChooseTile(
|
||||||
title: 'Ruleset',
|
title: 'Ruleset',
|
||||||
trailingText: selectedRuleset == null
|
trailingText: selectedRuleset == null
|
||||||
@@ -139,6 +168,7 @@ class _CreateGameViewState extends State<CreateGameView> {
|
|||||||
selectedRulesetIndex = rulesets.indexWhere(
|
selectedRulesetIndex = rulesets.indexWhere(
|
||||||
(r) => r.$1 == selectedRuleset,
|
(r) => r.$1 == selectedRuleset,
|
||||||
);
|
);
|
||||||
|
selectedGameIndex = -1;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -207,20 +237,6 @@ class _CreateGameViewState extends State<CreateGameView> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sneeex marked this conversation as resolved
Outdated
sneeex
commented
<img width="318" alt="grafik.png" src="attachments/1bb19145-834c-4a72-9399-a43972f77e46">
game lässt sich nicht erstellen, obwohl spieler, game name und ruleset ausgewählt ist (funktioniert auch mit 2 spielern nicht, hier muss aber darauf geachtet werden, dass ein spiel nur mit min. 2 spielern erstellt werden kann)
flixcoo
commented
Ja ups haha ich hab ganz vergessen irgendwas mit den ausgewählten Spielern zu machen, hab die einfach nur geprinted haha. Hab die jetzt auch included und sollte jetzt funktionieren Ja ups haha ich hab ganz vergessen irgendwas mit den ausgewählten Spielern zu machen, hab die einfach nur geprinted haha. Hab die jetzt auch included und sollte jetzt funktionieren
|
|||||||
/// 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
|
/// Determines whether the "Create Game" button should be enabled based on
|
||||||
/// the current state of the input fields.
|
/// the current state of the input fields.
|
||||||
bool _enableCreateGameButton() {
|
bool _enableCreateGameButton() {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ class TitleDescriptionListTile extends StatelessWidget {
|
|||||||
final String description;
|
final String description;
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
final bool isHighlighted;
|
final bool isHighlighted;
|
||||||
|
final String? badgeText;
|
||||||
|
final Color? badgeColor;
|
||||||
|
|
||||||
const TitleDescriptionListTile({
|
const TitleDescriptionListTile({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -13,6 +15,8 @@ class TitleDescriptionListTile extends StatelessWidget {
|
|||||||
required this.description,
|
required this.description,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
this.isHighlighted = false,
|
this.isHighlighted = false,
|
||||||
|
this.badgeText,
|
||||||
|
this.badgeColor,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -28,20 +32,42 @@ class TitleDescriptionListTile extends StatelessWidget {
|
|||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Text(
|
||||||
child: Text(
|
title,
|
||||||
title,
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: const TextStyle(
|
||||||
style: const TextStyle(
|
fontWeight: FontWeight.bold,
|
||||||
fontWeight: FontWeight.bold,
|
fontSize: 18,
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
flixcoo marked this conversation as resolved
Outdated
sneeex
commented
|
|||||||
),
|
),
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
flixcoo marked this conversation as resolved
sneeex
commented
|
|||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
|
|||||||
Es ist kein OnChanged implementiert, die search functionality ist insgesamt nicht gegeben hier.
Note: Das onChanged sollte aber vllt. auch required sein in der CustomSearchBar (weiß gerade nicht ob ich das aus nem grund optional gelassen habe, macht aber ja glaube keinen sinn oder?)
Also ich habs explizit weggelassen, weil ich der einfachhalt halber das aktuell ausgesuchte Spiel über einen Index markiere. Wenn ich das jetzt implementieren würde, müsste ich dass unnötig kompliziert implementieren, weil die Games am Ende eh eine ID haben über die ich das markierte Identifizieren kann, deswegen würd ich das an dieser Stelle weglassen
dann mach die searchbar weg, entweder searchbar und functionality oder nicht, aber nicht so halbe sachen.
Ja aber das ist ja auch kacke, das layout an sich soll ja schon stehen
dann mach die functionality rein, entweder oder.
Du hast ja nur kein bock