From f658a88849caee39d07de784807722905308bd08 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Sat, 22 Nov 2025 23:52:00 +0100 Subject: [PATCH 1/5] Implemented displaying real recent games in home view --- .../views/main_menu/home_view.dart | 158 +++++++++++++----- 1 file changed, 115 insertions(+), 43 deletions(-) diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index 34e4be3..194849e 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -1,9 +1,13 @@ import 'package:flutter/material.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/widgets/buttons/quick_create_button.dart'; import 'package:game_tracker/presentation/widgets/tiles/game_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/quick_info_tile.dart'; +import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:provider/provider.dart'; import 'package:skeletonizer/skeletonizer.dart'; @@ -17,23 +21,43 @@ class HomeView extends StatefulWidget { class _HomeViewState extends State { late Future _gameCountFuture; late Future _groupCountFuture; + late Future> _recentGamesFuture; bool isLoading = true; + late final List skeletonData = List.filled( + 2, + Game( + name: 'Skeleton Game', + group: Group( + name: 'Skeleton Group', + members: [ + Player(name: 'Skeleton Player 1'), + Player(name: 'Skeleton Player 2'), + ], + ), + winner: + "Winner ID", //TODO: Should be player object, but isnt yet, waiting for pr + ), + ); + @override initState() { super.initState(); final db = Provider.of(context, listen: false); _gameCountFuture = db.gameDao.getGameCount(); _groupCountFuture = db.groupDao.getGroupCount(); + _recentGamesFuture = db.gameDao.getAllGames(); - Future.wait([_gameCountFuture, _groupCountFuture]).then((_) async { - await Future.delayed(const Duration(milliseconds: 50)); - if (mounted) { - setState(() { - isLoading = false; - }); - } - }); + Future.wait([_gameCountFuture, _groupCountFuture, _recentGamesFuture]).then( + (_) async { + await Future.delayed(const Duration(milliseconds: 50)); + if (mounted) { + setState(() { + isLoading = false; + }); + } + }, + ); } @override @@ -48,12 +72,21 @@ class _HomeViewState extends State { ), enabled: isLoading, enableSwitchAnimation: true, - switchAnimationConfig: const SwitchAnimationConfig( + switchAnimationConfig: SwitchAnimationConfig( duration: Duration(milliseconds: 200), switchInCurve: Curves.linear, switchOutCurve: Curves.linear, transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder, - layoutBuilder: AnimatedSwitcher.defaultLayoutBuilder, + layoutBuilder: + (Widget? currentChild, List previousChildren) { + return Stack( + alignment: Alignment.topCenter, + children: [ + ...previousChildren, + if (currentChild != null) currentChild, + ], + ); + }, ), child: SingleChildScrollView( child: Column( @@ -97,41 +130,70 @@ class _HomeViewState extends State { ), ], ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: InfoTile( - width: constraints.maxWidth * 0.95, - title: 'Recent Games', - icon: Icons.timer, - content: const Padding( - padding: EdgeInsets.symmetric(horizontal: 40.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GameTile( - gameTitle: 'Gamenight', - gameType: 'Cabo', - ruleset: 'Lowest Points', - players: '5 Players', - winner: 'Leonard', + FutureBuilder( + future: _recentGamesFuture, + builder: (context, snapshot) { + if (snapshot.hasError) { + return const Center( + child: TopCenteredMessage( + icon: Icons.report, + title: 'Error', + message: 'Group data couldn\'t\nbe loaded.', + ), + ); + } + if (snapshot.connectionState == ConnectionState.done && + (!snapshot.hasData || snapshot.data!.isEmpty)) { + return const Center( + child: TopCenteredMessage( + icon: Icons.info, + title: 'Info', + message: 'No games created yet.', + ), + ); + } + final List games = + isLoading ? skeletonData : (snapshot.data ?? []) + ..sort((a, b) => b.createdAt.compareTo(a.createdAt)); + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: InfoTile( + width: constraints.maxWidth * 0.95, + title: 'Recent Games', + icon: Icons.timer, + content: Padding( + padding: EdgeInsets.symmetric(horizontal: 40.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GameTile( + gameTitle: games[0].name, + gameType: "Gametype", + ruleset: 'Ruleset', + players: _getPlayerText(games[0]), + winner: + 'Leonard', //TODO: Replace Winner with real Winner + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Divider(), + ), + GameTile( + gameTitle: games[1].name, + gameType: 'Gametype', + ruleset: 'Ruleset', + players: _getPlayerText(games[1]), + winner: + 'Lina', //TODO: Replace Winner with real Winner + ), + SizedBox(height: 8), + ], ), - Padding( - padding: EdgeInsets.symmetric(vertical: 8.0), - child: Divider(), - ), - GameTile( - gameTitle: 'Schoolbreak', - gameType: 'Uno', - ruleset: 'Highest Points', - players: 'The Gang', - winner: 'Lina', - ), - SizedBox(height: 8), - ], + ), ), - ), - ), + ); + }, ), InfoTile( width: constraints.maxWidth * 0.95, @@ -189,4 +251,14 @@ class _HomeViewState extends State { }, ); } + + String _getPlayerText(Game game) { + if (game.group == null) { + return game.players?.map((p) => p.name).join(', ') ?? 'No Players'; + } + if (game.players == null || game.players!.isEmpty) { + return game.group!.name; + } + return '${game.group!.name} + ${game.players!.length}'; + } } From def37aa6402b8054472da849755d3a709034484f Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Sun, 23 Nov 2025 14:56:53 +0100 Subject: [PATCH 2/5] Refactor Recent Games tile in HomeView - Move FutureBuilder inside InfoTile content - Replace hardcoded winner and game type strings with actual game data - Limit displayed recent games to 2 items and handle cases with fewer games - Update player text generation to show player count instead of names - Remove TopCenteredMessage usage and replace with simple text for empty/error states - Update skeleton data to use Player object for winner --- .../views/main_menu/home_view.dart | 120 ++++++++++-------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index 194849e..f0f1483 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -7,7 +7,6 @@ import 'package:game_tracker/presentation/widgets/buttons/quick_create_button.da import 'package:game_tracker/presentation/widgets/tiles/game_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/quick_info_tile.dart'; -import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:provider/provider.dart'; import 'package:skeletonizer/skeletonizer.dart'; @@ -35,8 +34,7 @@ class _HomeViewState extends State { Player(name: 'Skeleton Player 2'), ], ), - winner: - "Winner ID", //TODO: Should be player object, but isnt yet, waiting for pr + winner: Player(name: 'Skeleton Player 1'), ), ); @@ -73,7 +71,7 @@ class _HomeViewState extends State { enabled: isLoading, enableSwitchAnimation: true, switchAnimationConfig: SwitchAnimationConfig( - duration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 200), switchInCurve: Curves.linear, switchOutCurve: Curves.linear, transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder, @@ -130,70 +128,79 @@ class _HomeViewState extends State { ), ], ), - FutureBuilder( - future: _recentGamesFuture, - builder: (context, snapshot) { - if (snapshot.hasError) { - return const Center( - child: TopCenteredMessage( - icon: Icons.report, - title: 'Error', - message: 'Group data couldn\'t\nbe loaded.', - ), - ); - } - if (snapshot.connectionState == ConnectionState.done && - (!snapshot.hasData || snapshot.data!.isEmpty)) { - return const Center( - child: TopCenteredMessage( - icon: Icons.info, - title: 'Info', - message: 'No games created yet.', - ), - ); - } - final List games = - isLoading ? skeletonData : (snapshot.data ?? []) - ..sort((a, b) => b.createdAt.compareTo(a.createdAt)); - return Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: InfoTile( - width: constraints.maxWidth * 0.95, - title: 'Recent Games', - icon: Icons.timer, - content: Padding( - padding: EdgeInsets.symmetric(horizontal: 40.0), - child: Column( + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: InfoTile( + width: constraints.maxWidth * 0.95, + title: 'Recent Games', + icon: Icons.timer, + content: Padding( + padding: const EdgeInsets.symmetric(horizontal: 40.0), + child: FutureBuilder( + future: _recentGamesFuture, + builder: (context, snapshot) { + if (snapshot.hasError) { + return const Center( + heightFactor: 4, + child: Text('Error while loading recent games.'), + ); + } + if (snapshot.connectionState == + ConnectionState.done && + (!snapshot.hasData || snapshot.data!.isEmpty)) { + return const Center( + heightFactor: 4, + child: Text('No recent games available.'), + ); + } + final List games = + (isLoading ? skeletonData : (snapshot.data ?? []) + ..sort( + (a, b) => + b.createdAt.compareTo(a.createdAt), + )) + .take(2) + .toList(); + return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ GameTile( gameTitle: games[0].name, - gameType: "Gametype", + gameType: 'Winner', ruleset: 'Ruleset', players: _getPlayerText(games[0]), - winner: - 'Leonard', //TODO: Replace Winner with real Winner + winner: games[0].winner == null + ? 'No winner set.' + : games[0].winner!.name, ), - Padding( + const Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Divider(), ), - GameTile( - gameTitle: games[1].name, - gameType: 'Gametype', - ruleset: 'Ruleset', - players: _getPlayerText(games[1]), - winner: - 'Lina', //TODO: Replace Winner with real Winner - ), - SizedBox(height: 8), + if (games.length >= 2) ...[ + GameTile( + gameTitle: games[1].name, + gameType: 'Winner', + ruleset: 'Ruleset', + players: _getPlayerText(games[1]), + winner: games[1].winner == null + ? 'No winner set.' + : games[1].winner!.name, + ), + const SizedBox(height: 8), + ] else ...[ + const Center( + heightFactor: 4, + child: Text('No second game available.'), + ), + ], ], - ), - ), + ); + }, ), - ); - }, + ), + ), ), InfoTile( width: constraints.maxWidth * 0.95, @@ -254,7 +261,8 @@ class _HomeViewState extends State { String _getPlayerText(Game game) { if (game.group == null) { - return game.players?.map((p) => p.name).join(', ') ?? 'No Players'; + final playerCount = game.players?.length ?? 0; + return '$playerCount Player(s)'; } if (game.players == null || game.players!.isEmpty) { return game.group!.name; From 963edaf1d1be0444de244399d7561648b0809510 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Sun, 23 Nov 2025 17:51:55 +0100 Subject: [PATCH 3/5] Update game status text and player count label in HomeView --- lib/presentation/views/main_menu/home_view.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index f0f1483..aa21f76 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -171,7 +171,7 @@ class _HomeViewState extends State { ruleset: 'Ruleset', players: _getPlayerText(games[0]), winner: games[0].winner == null - ? 'No winner set.' + ? 'Game in progress...' : games[0].winner!.name, ), const Padding( @@ -185,7 +185,7 @@ class _HomeViewState extends State { ruleset: 'Ruleset', players: _getPlayerText(games[1]), winner: games[1].winner == null - ? 'No winner set.' + ? 'Game in progress...' : games[1].winner!.name, ), const SizedBox(height: 8), @@ -262,7 +262,7 @@ class _HomeViewState extends State { String _getPlayerText(Game game) { if (game.group == null) { final playerCount = game.players?.length ?? 0; - return '$playerCount Player(s)'; + return '$playerCount Players'; } if (game.players == null || game.players!.isEmpty) { return game.group!.name; From 2616f7c113abdad9a1d732c1034b912ed5b46f92 Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Sun, 23 Nov 2025 18:08:03 +0100 Subject: [PATCH 4/5] Refactor FutureBuilder logic in HomeView to handle empty game lists and improve code formatting --- .../views/main_menu/home_view.dart | 138 ++++++++++-------- 1 file changed, 79 insertions(+), 59 deletions(-) diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index aa21f76..a4184bf 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -138,66 +138,86 @@ class _HomeViewState extends State { padding: const EdgeInsets.symmetric(horizontal: 40.0), child: FutureBuilder( future: _recentGamesFuture, - builder: (context, snapshot) { - if (snapshot.hasError) { - return const Center( - heightFactor: 4, - child: Text('Error while loading recent games.'), - ); - } - if (snapshot.connectionState == - ConnectionState.done && - (!snapshot.hasData || snapshot.data!.isEmpty)) { - return const Center( - heightFactor: 4, - child: Text('No recent games available.'), - ); - } - final List games = - (isLoading ? skeletonData : (snapshot.data ?? []) - ..sort( - (a, b) => - b.createdAt.compareTo(a.createdAt), - )) - .take(2) - .toList(); - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GameTile( - gameTitle: games[0].name, - gameType: 'Winner', - ruleset: 'Ruleset', - players: _getPlayerText(games[0]), - winner: games[0].winner == null - ? 'Game in progress...' - : games[0].winner!.name, - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 8.0), - child: Divider(), - ), - if (games.length >= 2) ...[ - GameTile( - gameTitle: games[1].name, - gameType: 'Winner', - ruleset: 'Ruleset', - players: _getPlayerText(games[1]), - winner: games[1].winner == null - ? 'Game in progress...' - : games[1].winner!.name, - ), - const SizedBox(height: 8), - ] else ...[ - const Center( + builder: + ( + BuildContext context, + AsyncSnapshot> snapshot, + ) { + if (snapshot.hasError) { + return const Center( heightFactor: 4, - child: Text('No second game available.'), - ), - ], - ], - ); - }, + child: Text( + 'Error while loading recent games.', + ), + ); + } + if (snapshot.connectionState == + ConnectionState.done && + (!snapshot.hasData || + snapshot.data!.isEmpty)) { + return const Center( + heightFactor: 4, + child: Text('No recent games available.'), + ); + } + final List games = + (isLoading + ? skeletonData + : (snapshot.data ?? []) + ..sort( + (a, b) => b.createdAt.compareTo( + a.createdAt, + ), + )) + .take(2) + .toList(); + if (games.length > 0) + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GameTile( + gameTitle: games[0].name, + gameType: 'Winner', + ruleset: 'Ruleset', + players: _getPlayerText(games[0]), + winner: games[0].winner == null + ? 'Game in progress...' + : games[0].winner!.name, + ), + const Padding( + padding: EdgeInsets.symmetric( + vertical: 8.0, + ), + child: Divider(), + ), + if (games.length > 1) ...[ + GameTile( + gameTitle: games[1].name, + gameType: 'Winner', + ruleset: 'Ruleset', + players: _getPlayerText(games[1]), + winner: games[1].winner == null + ? 'Game in progress...' + : games[1].winner!.name, + ), + const SizedBox(height: 8), + ] else ...[ + const Center( + heightFactor: 4, + child: Text( + 'No second game available.', + ), + ), + ], + ], + ); + else + return const Center( + heightFactor: 4, + child: Text('No recent games available.'), + ); + }, ), ), ), From e4abf53f66c3fca02c0f31f9a6305258b4bc74ce Mon Sep 17 00:00:00 2001 From: mathiskirchner Date: Sun, 23 Nov 2025 18:09:45 +0100 Subject: [PATCH 5/5] fix linter errors --- lib/presentation/views/main_menu/home_view.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index a4184bf..a8ff0d3 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -171,7 +171,7 @@ class _HomeViewState extends State { )) .take(2) .toList(); - if (games.length > 0) + if (games.isNotEmpty) { return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -212,11 +212,12 @@ class _HomeViewState extends State { ], ], ); - else + } else { return const Center( heightFactor: 4, child: Text('No recent games available.'), ); + } }, ), ),