Compare commits
5 Commits
e71cb11295
...
2ba710ca2d
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ba710ca2d | |||
| 9054b163ce | |||
| e182c815a1 | |||
| c284d10943 | |||
| 72e48ada94 |
@@ -8,6 +8,19 @@ class CustomTheme {
|
||||
static Color onBoxColor = const Color(0xFF181818);
|
||||
static Color boxBorder = const Color(0xFF272727);
|
||||
|
||||
static BoxDecoration standardBoxDecoration = BoxDecoration(
|
||||
color: boxColor,
|
||||
border: Border.all(color: boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
);
|
||||
|
||||
static BoxDecoration highlightedBoxDecoration = BoxDecoration(
|
||||
color: boxColor,
|
||||
border: Border.all(color: Colors.blue),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [BoxShadow(color: Colors.blue.withAlpha(120), blurRadius: 12)],
|
||||
);
|
||||
|
||||
static AppBarTheme appBarTheme = AppBarTheme(
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: Colors.white,
|
||||
|
||||
@@ -5,14 +5,27 @@ import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart';
|
||||
|
||||
class ChooseGroupView extends StatefulWidget {
|
||||
final List<Group> groups;
|
||||
final int initialGroupIndex;
|
||||
|
||||
const ChooseGroupView({super.key, required this.groups});
|
||||
const ChooseGroupView({
|
||||
super.key,
|
||||
required this.groups,
|
||||
required this.initialGroupIndex,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChooseGroupView> createState() => _ChooseGroupViewState();
|
||||
}
|
||||
|
||||
class _ChooseGroupViewState extends State<ChooseGroupView> {
|
||||
late int selectedGroupIndex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
selectedGroupIndex = widget.initialGroupIndex;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -31,8 +44,20 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
|
||||
itemCount: widget.groups.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(widget.groups[index]),
|
||||
child: GroupTile(group: widget.groups[index]),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedGroupIndex = index;
|
||||
});
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(context).pop(widget.groups[index]);
|
||||
});
|
||||
},
|
||||
child: GroupTile(
|
||||
group: widget.groups[index],
|
||||
isHighlighted: selectedGroupIndex == index,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,37 +1,29 @@
|
||||
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/ruleset_list_tile.dart';
|
||||
|
||||
class ChooseRulesetView extends StatefulWidget {
|
||||
const ChooseRulesetView({super.key});
|
||||
final List<(Ruleset, String, String)> rulesets;
|
||||
final int initialRulesetIndex;
|
||||
const ChooseRulesetView({
|
||||
super.key,
|
||||
required this.rulesets,
|
||||
required this.initialRulesetIndex,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChooseRulesetView> createState() => _ChooseRulesetViewState();
|
||||
}
|
||||
|
||||
class _ChooseRulesetViewState extends State<ChooseRulesetView> {
|
||||
List<(Ruleset, String, String)> rulesets = [
|
||||
(
|
||||
Ruleset.singleWinner,
|
||||
'Single Winner',
|
||||
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.',
|
||||
),
|
||||
(
|
||||
Ruleset.singleLoser,
|
||||
'Single Loser',
|
||||
'Exactly one loser is determined; last place receives the penalty or consequence.',
|
||||
),
|
||||
(
|
||||
Ruleset.mostPoints,
|
||||
'Most Points',
|
||||
'Traditional ruleset: the player with the most points wins.',
|
||||
),
|
||||
(
|
||||
Ruleset.lastPoints,
|
||||
'Least Points',
|
||||
'Inverse scoring: the player with the fewest points wins.',
|
||||
),
|
||||
];
|
||||
late int selectedRulesetIndex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
selectedRulesetIndex = widget.initialRulesetIndex;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -48,45 +40,22 @@ class _ChooseRulesetViewState extends State<ChooseRulesetView> {
|
||||
),
|
||||
body: ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 85),
|
||||
itemCount: rulesets.length,
|
||||
itemCount: widget.rulesets.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(rulesets[index].$1),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
rulesets[index].$2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
rulesets[index].$3,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 2.5),
|
||||
],
|
||||
),
|
||||
),
|
||||
return RulesetListTile(
|
||||
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,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/core/enums.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/game.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/create_game/choose_group_view.dart';
|
||||
@@ -25,10 +26,35 @@ class _CreateGameViewState extends State<CreateGameView> {
|
||||
late final List<Group> groupsList;
|
||||
|
||||
Group? selectedGroup;
|
||||
int selectedGroupIndex = -1;
|
||||
Ruleset? selectedRuleset;
|
||||
int selectedRulesetIndex = -1;
|
||||
|
||||
bool isLoading = true;
|
||||
|
||||
List<(Ruleset, String, String)> rulesets = [
|
||||
(
|
||||
Ruleset.singleWinner,
|
||||
'Single Winner',
|
||||
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.',
|
||||
),
|
||||
(
|
||||
Ruleset.singleLoser,
|
||||
'Single Loser',
|
||||
'Exactly one loser is determined; last place receives the penalty or consequence.',
|
||||
),
|
||||
(
|
||||
Ruleset.mostPoints,
|
||||
'Most Points',
|
||||
'Traditional ruleset: the player with the most points wins.',
|
||||
),
|
||||
(
|
||||
Ruleset.lastPoints,
|
||||
'Least Points',
|
||||
'Inverse scoring: the player with the fewest points wins.',
|
||||
),
|
||||
];
|
||||
|
||||
late final List<Player> skeletonData = List.filled(
|
||||
7,
|
||||
Player(name: 'Player 0'),
|
||||
@@ -88,9 +114,15 @@ class _CreateGameViewState extends State<CreateGameView> {
|
||||
onTap: () async {
|
||||
selectedRuleset = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ChooseRulesetView(),
|
||||
builder: (context) => ChooseRulesetView(
|
||||
rulesets: rulesets,
|
||||
initialRulesetIndex: selectedRulesetIndex,
|
||||
),
|
||||
),
|
||||
);
|
||||
selectedRulesetIndex = rulesets.indexWhere(
|
||||
(r) => r.$1 == selectedRuleset,
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
@@ -102,11 +134,7 @@ class _CreateGameViewState extends State<CreateGameView> {
|
||||
vertical: 10,
|
||||
horizontal: 15,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Row(
|
||||
children: [
|
||||
const Text(
|
||||
@@ -128,9 +156,15 @@ class _CreateGameViewState extends State<CreateGameView> {
|
||||
onTap: () async {
|
||||
selectedGroup = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ChooseGroupView(groups: groupsList),
|
||||
builder: (context) => ChooseGroupView(
|
||||
groups: groupsList,
|
||||
initialGroupIndex: selectedGroupIndex,
|
||||
),
|
||||
),
|
||||
);
|
||||
selectedGroupIndex = groupsList.indexWhere(
|
||||
(g) => g.id == selectedGroup?.id,
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
@@ -142,11 +176,7 @@ class _CreateGameViewState extends State<CreateGameView> {
|
||||
vertical: 10,
|
||||
horizontal: 15,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Row(
|
||||
children: [
|
||||
const Text(
|
||||
@@ -175,7 +205,12 @@ class _CreateGameViewState extends State<CreateGameView> {
|
||||
selectedRuleset == null)
|
||||
? null
|
||||
: () async {
|
||||
print('Create game pressed');
|
||||
Game game = Game(
|
||||
name: _gameNameController.text.trim(),
|
||||
createdAt: DateTime.now(),
|
||||
group: selectedGroup!,
|
||||
);
|
||||
print('Creating game: ${game.name}');
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
@@ -105,11 +105,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
vertical: 10,
|
||||
horizontal: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
@@ -4,20 +4,20 @@ import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
|
||||
|
||||
class GroupTile extends StatelessWidget {
|
||||
const GroupTile({super.key, required this.group});
|
||||
const GroupTile({super.key, required this.group, this.isHighlighted = false});
|
||||
|
||||
final Group group;
|
||||
final bool isHighlighted;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
return AnimatedContainer(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: isHighlighted
|
||||
? CustomTheme.highlightedBoxDecoration
|
||||
: CustomTheme.standardBoxDecoration,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
@@ -29,11 +29,7 @@ class _InfoTileState extends State<InfoTile> {
|
||||
padding: widget.padding ?? const EdgeInsets.all(12),
|
||||
height: widget.height,
|
||||
width: widget.width ?? 380,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
||||
@@ -29,11 +29,7 @@ class _QuickInfoTileState extends State<QuickInfoTile> {
|
||||
padding: widget.padding ?? const EdgeInsets.all(12),
|
||||
height: widget.height ?? 110,
|
||||
width: widget.width ?? 180,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
56
lib/presentation/widgets/tiles/ruleset_list_tile.dart
Normal file
56
lib/presentation/widgets/tiles/ruleset_list_tile.dart
Normal file
@@ -0,0 +1,56 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
class RulesetListTile extends StatelessWidget {
|
||||
final String title;
|
||||
final String description;
|
||||
final VoidCallback? onPressed;
|
||||
final bool isHighlighted;
|
||||
|
||||
const RulesetListTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
this.onPressed,
|
||||
this.isHighlighted = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Use the callback directly so a null onPressed disables taps
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: AnimatedContainer(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||||
decoration: isHighlighted
|
||||
? CustomTheme.highlightedBoxDecoration
|
||||
: CustomTheme.standardBoxDecoration,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(description, style: const TextStyle(fontSize: 14)),
|
||||
const SizedBox(height: 2.5),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,7 @@ class SettingsListTile extends StatelessWidget {
|
||||
child: Container(
|
||||
margin: EdgeInsets.zero,
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
||||
@@ -18,11 +18,7 @@ class TextIconListTile extends StatelessWidget {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
|
||||
Reference in New Issue
Block a user