7 Commits

Author SHA1 Message Date
4f0a1eec6d refactor
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Failing after 2m6s
2025-12-21 19:50:34 +01:00
24b60bb18b added minimumSkeletonDuration constant and changed future logic 2025-12-21 19:46:59 +01:00
c8532adfde changed skeleton duration logic in statistics view and refactored 2025-12-21 15:52:02 +01:00
76186787e7 changed futurebuilder logic in player selection 2025-12-21 15:51:24 +01:00
f05114a99e removed futurebuilder logic in groups_view 2025-12-21 15:50:39 +01:00
d96494f608 Changed futurebuilder logic in groups_view 2025-12-21 15:50:24 +01:00
0eaf3d251b added constant minimumSkeletonDuration 2025-12-21 15:49:58 +01:00
7 changed files with 99 additions and 147 deletions

2
lib/core/constants.dart Normal file
View File

@@ -0,0 +1,2 @@
/// Minimum duration of all app skeletons
Duration minimumSkeletonDuration = Duration(milliseconds: 250);

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/group.dart';
@@ -34,10 +35,10 @@ class _GroupsViewState extends State<GroupsView> {
void initState() { void initState() {
super.initState(); super.initState();
db = Provider.of<AppDatabase>(context, listen: false); db = Provider.of<AppDatabase>(context, listen: false);
_allGroupsFuture = Future.delayed( _allGroupsFuture = Future.wait([
const Duration(milliseconds: 250), db.groupDao.getAllGroups(),
() => db.groupDao.getAllGroups(), Future.delayed(minimumSkeletonDuration),
); ]).then((results) => results[0] as List<Group>);
} }
@override @override

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/data/dto/match.dart'; import 'package:game_tracker/data/dto/match.dart';
@@ -18,12 +19,10 @@ class HomeView extends StatefulWidget {
} }
class _HomeViewState extends State<HomeView> { class _HomeViewState extends State<HomeView> {
late Future<int> _matchCountFuture;
late Future<int> _groupCountFuture;
late Future<List<Match>> _recentMatchesFuture;
bool isLoading = true; bool isLoading = true;
int matchCount = 0;
late final List<Match> skeletonData = List.filled( int groupCount = 0;
List<Match> recentMatches = List.filled(
2, 2,
Match( Match(
name: 'Skeleton Match', name: 'Skeleton Match',
@@ -39,19 +38,24 @@ class _HomeViewState extends State<HomeView> {
); );
@override @override
initState() { void initState() {
super.initState(); super.initState();
final db = Provider.of<AppDatabase>(context, listen: false); final db = Provider.of<AppDatabase>(context, listen: false);
_matchCountFuture = db.matchDao.getMatchCount();
_groupCountFuture = db.groupDao.getGroupCount();
_recentMatchesFuture = db.matchDao.getAllMatches();
Future.wait([ Future.wait([
_matchCountFuture, db.matchDao.getMatchCount(),
_groupCountFuture, db.groupDao.getGroupCount(),
_recentMatchesFuture, db.matchDao.getAllMatches(),
]).then((_) async { Future.delayed(minimumSkeletonDuration),
await Future.delayed(const Duration(milliseconds: 250)); ]).then((results) {
matchCount = results[0] as int;
groupCount = results[1] as int;
recentMatches = results[2] as List<Match>;
recentMatches =
(recentMatches..sort((a, b) => b.createdAt.compareTo(a.createdAt)))
.take(2)
.toList();
if (mounted) { if (mounted) {
setState(() { setState(() {
isLoading = false; isLoading = false;
@@ -73,38 +77,20 @@ class _HomeViewState extends State<HomeView> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
FutureBuilder<int>( QuickInfoTile(
future: _matchCountFuture,
builder: (context, snapshot) {
final int count = (snapshot.hasData)
? snapshot.data!
: 0;
return QuickInfoTile(
width: constraints.maxWidth * 0.45, width: constraints.maxWidth * 0.45,
height: constraints.maxHeight * 0.15, height: constraints.maxHeight * 0.15,
title: 'Matches', title: 'Matches',
icon: Icons.groups_rounded, icon: Icons.groups_rounded,
value: count, value: matchCount,
);
},
), ),
SizedBox(width: constraints.maxWidth * 0.05), SizedBox(width: constraints.maxWidth * 0.05),
FutureBuilder<int>( QuickInfoTile(
future: _groupCountFuture,
builder: (context, snapshot) {
final int count =
(snapshot.connectionState == ConnectionState.done &&
snapshot.hasData)
? snapshot.data!
: 0;
return QuickInfoTile(
width: constraints.maxWidth * 0.45, width: constraints.maxWidth * 0.45,
height: constraints.maxHeight * 0.15, height: constraints.maxHeight * 0.15,
title: 'Groups', title: 'Groups',
icon: Icons.groups_rounded, icon: Icons.groups_rounded,
value: count, value: groupCount,
);
},
), ),
], ],
), ),
@@ -116,80 +102,48 @@ class _HomeViewState extends State<HomeView> {
icon: Icons.timer, icon: Icons.timer,
content: Padding( content: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0), padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: FutureBuilder( child: Visibility(
future: _recentMatchesFuture, visible: !isLoading,
builder: replacement: const Center(
( heightFactor: 12,
BuildContext context, child: Text('No recent games available.'),
AsyncSnapshot<List<Match>> snapshot,
) {
if (snapshot.hasError) {
return const Center(
heightFactor: 4,
child: Text(
'Error while loading recent matches.',
), ),
); child: Column(
}
final List<Match> matches =
(isLoading
? skeletonData
: (snapshot.data ?? [])
..sort(
(a, b) => b.createdAt.compareTo(
a.createdAt,
),
))
.take(2)
.toList();
if (matches.isNotEmpty) {
return Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
MatchTile( MatchTile(
matchTitle: matches[0].name, matchTitle: recentMatches[0].name,
game: 'Winner', game: 'Winner',
ruleset: 'Ruleset', ruleset: 'Ruleset',
players: _getPlayerText(matches[0]), players: _getPlayerText(recentMatches[0]),
winner: matches[0].winner == null winner: recentMatches[0].winner == null
? 'Match in progress...' ? 'Match in progress...'
: matches[0].winner!.name, : recentMatches[0].winner!.name,
), ),
const Padding( const Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(vertical: 8.0),
vertical: 8.0,
),
child: Divider(), child: Divider(),
), ),
if (matches.length > 1) ...[ if (recentMatches.length > 1) ...[
MatchTile( MatchTile(
matchTitle: matches[1].name, matchTitle: recentMatches[1].name,
game: 'Winner', game: 'Winner',
ruleset: 'Ruleset', ruleset: 'Ruleset',
players: _getPlayerText(matches[1]), players: _getPlayerText(recentMatches[1]),
winner: matches[1].winner == null winner: recentMatches[1].winner == null
? 'Game in progress...' ? 'Game in progress...'
: matches[1].winner!.name, : recentMatches[1].winner!.name,
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
] else ...[ ] else ...[
const Center( const Center(
heightFactor: 4, heightFactor: 4,
child: Text( child: Text('No second game available.'),
'No second game available.',
),
), ),
], ],
], ],
); ),
} else {
return const Center(
heightFactor: 12,
child: Text('No recent games available.'),
);
}
},
), ),
), ),
), ),
@@ -199,7 +153,6 @@ class _HomeViewState extends State<HomeView> {
title: 'Quick Create', title: 'Quick Create',
icon: Icons.add_box_rounded, icon: Icons.add_box_rounded,
content: Column( content: Column(
spacing: 8,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,

View File

@@ -28,12 +28,6 @@ class _CreateMatchViewState extends State<CreateMatchView> {
/// Reference to the app database /// Reference to the app database
late final AppDatabase db; late final AppDatabase db;
/// Futures to load all groups and players from the database
late Future<List<Group>> _allGroupsFuture;
/// Future to load all players from the database
late Future<List<Player>> _allPlayersFuture;
/// Controller for the game name input field /// Controller for the game name input field
final TextEditingController _gameNameController = TextEditingController(); final TextEditingController _gameNameController = TextEditingController();
@@ -107,10 +101,10 @@ class _CreateMatchViewState extends State<CreateMatchView> {
db = Provider.of<AppDatabase>(context, listen: false); db = Provider.of<AppDatabase>(context, listen: false);
_allGroupsFuture = db.groupDao.getAllGroups(); Future.wait([
_allPlayersFuture = db.playerDao.getAllPlayers(); db.groupDao.getAllGroups(),
db.playerDao.getAllPlayers(),
Future.wait([_allGroupsFuture, _allPlayersFuture]).then((result) async { ]).then((result) async {
groupsList = result[0] as List<Group>; groupsList = result[0] as List<Group>;
playerList = result[1] as List<Player>; playerList = result[1] as List<Player>;
}); });

View File

@@ -2,6 +2,7 @@ import 'dart:core' hide Match;
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/group.dart';
@@ -43,10 +44,10 @@ class _MatchViewState extends State<MatchView> {
void initState() { void initState() {
super.initState(); super.initState();
db = Provider.of<AppDatabase>(context, listen: false); db = Provider.of<AppDatabase>(context, listen: false);
_gameListFuture = Future.delayed( _gameListFuture = Future.wait([
const Duration(milliseconds: 250), db.matchDao.getAllMatches(),
() => db.matchDao.getAllMatches(), Future.delayed(minimumSkeletonDuration),
); ]).then((results) => results[0] as List<Match>);
} }
@override @override

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/match.dart'; import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/data/dto/player.dart';
@@ -14,8 +15,6 @@ class StatisticsView extends StatefulWidget {
} }
class _StatisticsViewState extends State<StatisticsView> { class _StatisticsViewState extends State<StatisticsView> {
late Future<List<Match>> _matchesFuture;
late Future<List<Player>> _playersFuture;
List<(String, int)> winCounts = List.filled(6, ('Skeleton Player', 1)); List<(String, int)> winCounts = List.filled(6, ('Skeleton Player', 1));
List<(String, int)> matchCounts = List.filled(6, ('Skeleton Player', 1)); List<(String, int)> matchCounts = List.filled(6, ('Skeleton Player', 1));
List<(String, double)> winRates = List.filled(6, ('Skeleton Player', 1)); List<(String, double)> winRates = List.filled(6, ('Skeleton Player', 1));
@@ -25,11 +24,12 @@ class _StatisticsViewState extends State<StatisticsView> {
void initState() { void initState() {
super.initState(); super.initState();
final db = Provider.of<AppDatabase>(context, listen: false); final db = Provider.of<AppDatabase>(context, listen: false);
_matchesFuture = db.matchDao.getAllMatches();
_playersFuture = db.playerDao.getAllPlayers();
Future.wait([_matchesFuture, _playersFuture]).then((results) async { Future.wait([
await Future.delayed(const Duration(milliseconds: 250)); db.matchDao.getAllMatches(),
db.playerDao.getAllPlayers(),
Future.delayed(minimumSkeletonDuration),
]).then((results) async {
final matches = results[0] as List<Match>; final matches = results[0] as List<Match>;
final players = results[1] as List<Player>; final players = results[1] as List<Player>;
winCounts = _calculateWinsForAllPlayers(matches, players); winCounts = _calculateWinsForAllPlayers(matches, players);

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/data/dto/player.dart';
@@ -46,10 +47,10 @@ class _PlayerSelectionState extends State<PlayerSelection> {
} }
void loadPlayerList() { void loadPlayerList() {
_allPlayersFuture = Future.delayed( _allPlayersFuture = Future.wait([
const Duration(milliseconds: 250), db.playerDao.getAllPlayers(),
() => db.playerDao.getAllPlayers(), Future.delayed(minimumSkeletonDuration),
); ]).then((results) => results[0] as List<Player>);
suggestedPlayers = skeletonData; suggestedPlayers = skeletonData;
_allPlayersFuture.then((loadedPlayers) { _allPlayersFuture.then((loadedPlayers) {
setState(() { setState(() {