8 Commits

Author SHA1 Message Date
7bc75d60a7 Fixed text overflow
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m33s
Pull Request Pipeline / lint (pull_request) Successful in 2m36s
2025-12-07 19:31:11 +01:00
a1ed17355a Minimum of 2 players required to create a game, unless group is selected 2025-12-07 19:23:38 +01:00
45abc79f95 Removed unneccesary db declaration 2025-12-07 19:17:56 +01:00
c214a26c54 Removed onChanged 2025-12-07 19:17:20 +01:00
6c0cb92e56 Removed unneccesary title in tupe 2025-12-07 19:17:13 +01:00
b4ba4f8d74 Implemented search bar in choose_group_view 2025-12-07 19:12:43 +01:00
f60c11fc08 Added searchbar for choose_group_view 2025-12-07 19:01:19 +01:00
d2d0a82c9b Implemented deselecting game 2025-12-07 18:58:49 +01:00
5 changed files with 107 additions and 63 deletions

View File

@@ -38,9 +38,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.arrow_back_ios), icon: const Icon(Icons.arrow_back_ios),
onPressed: () { onPressed: () {
Navigator.of( Navigator.of(context).pop(selectedGameIndex);
context,
).pop(selectedGameIndex == -1 ? null : selectedGameIndex);
}, },
), ),
title: const Text( title: const Text(
@@ -70,7 +68,11 @@ class _ChooseGameViewState extends State<ChooseGameView> {
isHighlighted: selectedGameIndex == index, isHighlighted: selectedGameIndex == index,
onPressed: () async { onPressed: () async {
setState(() { setState(() {
if (selectedGameIndex == index) {
selectedGameIndex = -1;
} else {
selectedGameIndex = index; selectedGameIndex = index;
}
}); });
}, },
); );

View File

@@ -1,16 +1,17 @@
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/dto/group.dart'; import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart';
class ChooseGroupView extends StatefulWidget { class ChooseGroupView extends StatefulWidget {
final List<Group> groups; final List<Group> groups;
final int initialGroupIndex; final String initialGroupId;
const ChooseGroupView({ const ChooseGroupView({
super.key, super.key,
required this.groups, required this.groups,
required this.initialGroupIndex, required this.initialGroupId,
}); });
@override @override
@@ -18,11 +19,15 @@ class ChooseGroupView extends StatefulWidget {
} }
class _ChooseGroupViewState extends State<ChooseGroupView> { class _ChooseGroupViewState extends State<ChooseGroupView> {
late int selectedGroupIndex; late String selectedGroupId;
final TextEditingController controller = TextEditingController();
final String hintText = 'Group Name';
late final List<Group> filteredGroups;
@override @override
void initState() { void initState() {
selectedGroupIndex = widget.initialGroupIndex; selectedGroupId = widget.initialGroupId;
filteredGroups = [...widget.groups];
super.initState(); super.initState();
} }
@@ -37,9 +42,11 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
icon: const Icon(Icons.arrow_back_ios), icon: const Icon(Icons.arrow_back_ios),
onPressed: () { onPressed: () {
Navigator.of(context).pop( Navigator.of(context).pop(
selectedGroupIndex == -1 selectedGroupId == ''
? null ? null
: widget.groups[selectedGroupIndex], : widget.groups.firstWhere(
(group) => group.id == selectedGroupId,
),
); );
}, },
), ),
@@ -49,27 +56,63 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
), ),
centerTitle: true, centerTitle: true,
), ),
body: ListView.builder( body: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: CustomSearchBar(
controller: controller,
hintText: hintText,
onChanged: (value) {
setState(() {
filterGroups(value);
});
},
),
),
Expanded(
child: ListView.builder(
padding: const EdgeInsets.only(bottom: 85), padding: const EdgeInsets.only(bottom: 85),
itemCount: widget.groups.length, itemCount: filteredGroups.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
if (selectedGroupIndex == index) { if (selectedGroupId != filteredGroups[index].id) {
selectedGroupIndex = -1; selectedGroupId = filteredGroups[index].id;
} else { } else {
selectedGroupIndex = index; selectedGroupId = '';
} }
}); });
}, },
child: GroupTile( child: GroupTile(
group: widget.groups[index], group: filteredGroups[index],
isHighlighted: selectedGroupIndex == index, isHighlighted: selectedGroupId == filteredGroups[index].id,
), ),
); );
}, },
), ),
),
],
),
); );
} }
/// Filters the groups based on the search query.
/// TODO: Maybe implement also targetting player names?
void filterGroups(String query) {
setState(() {
if (query.isEmpty) {
filteredGroups.clear();
filteredGroups.addAll(widget.groups);
} else {
filteredGroups.clear();
filteredGroups.addAll(
widget.groups.where(
(group) => group.name.toLowerCase().contains(query.toLowerCase()),
),
);
}
});
}
} }

View File

@@ -4,7 +4,7 @@ import 'package:game_tracker/core/enums.dart';
import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart';
class ChooseRulesetView extends StatefulWidget { class ChooseRulesetView extends StatefulWidget {
final List<(Ruleset, String, String)> rulesets; final List<(Ruleset, String)> rulesets;
final int initialRulesetIndex; final int initialRulesetIndex;
const ChooseRulesetView({ const ChooseRulesetView({
@@ -66,8 +66,8 @@ class _ChooseRulesetViewState extends State<ChooseRulesetView> {
} }
}); });
}, },
title: widget.rulesets[index].$2, title: translateRulesetToString(widget.rulesets[index].$1),
description: widget.rulesets[index].$3, description: widget.rulesets[index].$2,
isHighlighted: selectedRulesetIndex == index, isHighlighted: selectedRulesetIndex == index,
); );
}, },

View File

@@ -47,7 +47,7 @@ class _CreateGameViewState extends State<CreateGameView> {
/// The index of the currently selected group in [groupsList] to mark it in /// The index of the currently selected group in [groupsList] to mark it in
/// the [ChooseGroupView] /// the [ChooseGroupView]
int selectedGroupIndex = -1; String selectedGroupId = '';
/// The currently selected ruleset /// The currently selected ruleset
Ruleset? selectedRuleset; Ruleset? selectedRuleset;
@@ -61,28 +61,24 @@ class _CreateGameViewState extends State<CreateGameView> {
/// The currently selected players /// The currently selected players
List<Player>? selectedPlayers; List<Player>? selectedPlayers;
/// List of available rulesets with their display names and descriptions /// List of available rulesets with their descriptions
/// as tuples of (Ruleset, String, String) /// as tuples of (Ruleset, String)
/// TODO: Replace when rulesets are implemented /// TODO: Replace when rulesets are implemented
List<(Ruleset, String, String)> rulesets = [ List<(Ruleset, String)> rulesets = [
( (
Ruleset.singleWinner, Ruleset.singleWinner,
'Single Winner',
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.', 'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.',
), ),
( (
Ruleset.singleLoser, Ruleset.singleLoser,
'Single Loser',
'Exactly one loser is determined; last place receives the penalty or consequence.', 'Exactly one loser is determined; last place receives the penalty or consequence.',
), ),
( (
Ruleset.mostPoints, Ruleset.mostPoints,
'Most Points',
'Traditional ruleset: the player with the most points wins.', 'Traditional ruleset: the player with the most points wins.',
), ),
( (
Ruleset.leastPoints, Ruleset.leastPoints,
'Least Points',
'Inverse scoring: the player with the fewest points wins.', 'Inverse scoring: the player with the fewest points wins.',
), ),
]; ];
@@ -129,9 +125,6 @@ class _CreateGameViewState extends State<CreateGameView> {
child: TextInputField( child: TextInputField(
controller: _gameNameController, controller: _gameNameController,
hintText: 'Game name', hintText: 'Game name',
onChanged: (value) {
setState(() {});
},
), ),
), ),
ChooseTile( ChooseTile(
@@ -148,11 +141,14 @@ class _CreateGameViewState extends State<CreateGameView> {
), ),
), ),
); );
if (selectedGameIndex != -1) {
setState(() {
selectedRuleset = games[selectedGameIndex].$3; selectedRuleset = games[selectedGameIndex].$3;
selectedRulesetIndex = rulesets.indexWhere( selectedRulesetIndex = rulesets.indexWhere(
(r) => r.$1 == selectedRuleset, (r) => r.$1 == selectedRuleset,
); );
setState(() {}); });
}
}, },
), ),
ChooseTile( ChooseTile(
@@ -186,13 +182,11 @@ class _CreateGameViewState extends State<CreateGameView> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => ChooseGroupView( builder: (context) => ChooseGroupView(
groups: groupsList, groups: groupsList,
initialGroupIndex: selectedGroupIndex, initialGroupId: selectedGroupId,
), ),
), ),
); );
selectedGroupIndex = groupsList.indexWhere( selectedGroupId = selectedGroup?.id ?? '';
(g) => g.id == selectedGroup?.id,
);
setState(() {}); setState(() {});
}, },
), ),
@@ -227,10 +221,6 @@ class _CreateGameViewState extends State<CreateGameView> {
group: selectedGroup!, group: selectedGroup!,
players: selectedPlayers, players: selectedPlayers,
); );
final db = Provider.of<AppDatabase>(
context,
listen: false,
);
await db.gameDao.addGame(game: game); await db.gameDao.addGame(game: game);
if (context.mounted) { if (context.mounted) {
Navigator.pushReplacement( Navigator.pushReplacement(
@@ -255,7 +245,7 @@ class _CreateGameViewState extends State<CreateGameView> {
bool _enableCreateGameButton() { bool _enableCreateGameButton() {
return _gameNameController.text.isNotEmpty && return _gameNameController.text.isNotEmpty &&
(selectedGroup != null || (selectedGroup != null ||
(selectedPlayers != null && selectedPlayers!.isNotEmpty)) && (selectedPlayers != null && selectedPlayers!.length > 1)) &&
selectedRuleset != null; selectedRuleset != null;
} }
} }

View File

@@ -38,17 +38,23 @@ class TitleDescriptionListTile extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text( SizedBox(
width: 230,
child: Text(
title, title,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 18, fontSize: 18,
), ),
), ),
),
if (badgeText != null) ...[ if (badgeText != null) ...[
const Spacer(), const Spacer(),
Container( Container(
constraints: const BoxConstraints(maxWidth: 100),
margin: const EdgeInsets.only(top: 4), margin: const EdgeInsets.only(top: 4),
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 2, vertical: 2,
@@ -60,6 +66,9 @@ class TitleDescriptionListTile extends StatelessWidget {
), ),
child: Text( child: Text(
badgeText!, badgeText!,
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 12, fontSize: 12,