diff --git a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart index 5939405..0055761 100644 --- a/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart +++ b/lib/presentation/views/main_menu/create_game/choose_ruleset_view.dart @@ -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 createState() => _ChooseRulesetViewState(); } class _ChooseRulesetViewState extends State { - 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 { ), 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, ); }, ), diff --git a/lib/presentation/views/main_menu/create_game/create_game_view.dart b/lib/presentation/views/main_menu/create_game/create_game_view.dart index 4c776f9..2626961 100644 --- a/lib/presentation/views/main_menu/create_game/create_game_view.dart +++ b/lib/presentation/views/main_menu/create_game/create_game_view.dart @@ -28,9 +28,33 @@ class _CreateGameViewState extends State { 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 skeletonData = List.filled( 7, Player(name: 'Player 0'), @@ -90,9 +114,15 @@ class _CreateGameViewState extends State { 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( @@ -132,7 +162,7 @@ class _CreateGameViewState extends State { MaterialPageRoute( builder: (context) => ChooseGroupView( groups: groupsList, - selectedGroupIndex: selectedGroupIndex, + initialGroupIndex: selectedGroupIndex, ), ), ); diff --git a/lib/presentation/widgets/tiles/ruleset_list_tile.dart b/lib/presentation/widgets/tiles/ruleset_list_tile.dart new file mode 100644 index 0000000..0cc6071 --- /dev/null +++ b/lib/presentation/widgets/tiles/ruleset_list_tile.dart @@ -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), + ], + ), + ), + ); + } +}