Compare commits
22 Commits
67bbb15845
...
74402f2e04
| Author | SHA1 | Date | |
|---|---|---|---|
| 74402f2e04 | |||
| 5c09dfb47d | |||
| 02735b5b1d | |||
| a6f40456d8 | |||
| d931e85e9f | |||
| a707990356 | |||
| 3bd522df6e | |||
| 2dd4f52336 | |||
| 2dad822d79 | |||
| 640830d8ab | |||
| 168d7748a9 | |||
| b475237b9e | |||
| 2a7d409df0 | |||
| 7c34e51779 | |||
| ac6f3d0f92 | |||
| 511124e323 | |||
| d07bc6e8bb | |||
| 352fdc13f1 | |||
| d36348c59c | |||
| d08c79fc26 | |||
| 1081fb8be7 | |||
| 2ee4d8e6fa |
@@ -5,6 +5,7 @@ class CustomTheme {
|
||||
static Color secondaryColor = const Color(0xFFAFA2FF);
|
||||
static Color backgroundColor = const Color(0xFF0B0B0B);
|
||||
static Color boxColor = const Color(0xFF101010);
|
||||
static Color onBoxColor = const Color(0xFF181818);
|
||||
static Color boxBorder = const Color(0xFF272727);
|
||||
|
||||
static AppBarTheme appBarTheme = AppBarTheme(
|
||||
|
||||
@@ -14,9 +14,14 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
|
||||
Future<List<Group>> getAllGroups() async {
|
||||
final query = select(groupTable);
|
||||
final result = await query.get();
|
||||
return result
|
||||
.map((row) => Group(id: row.id, name: row.name, members: []))
|
||||
.toList();
|
||||
return Future.wait(
|
||||
result.map((groupData) async {
|
||||
final members = await db.playerGroupDao.getPlayersOfGroupById(
|
||||
groupId: groupData.id,
|
||||
);
|
||||
return Group(id: groupData.id, name: groupData.name, members: members);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/// Retrieves a [Group] by its [groupId], including its members.
|
||||
@@ -33,28 +38,32 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
|
||||
|
||||
/// Adds a new group with the given [id] and [name] to the database.
|
||||
/// This method also adds the group's members to the [PlayerGroupTable].
|
||||
Future<void> addGroup({required Group group}) async {
|
||||
await db.transaction(() async {
|
||||
await into(
|
||||
groupTable,
|
||||
).insert(GroupTableCompanion.insert(id: group.id, name: group.name));
|
||||
await db.batch(
|
||||
(b) => b.insertAll(
|
||||
db.playerGroupTable,
|
||||
group.members
|
||||
.map(
|
||||
(member) => PlayerGroupTableCompanion.insert(
|
||||
playerId: member.id,
|
||||
groupId: group.id,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
await Future.wait(
|
||||
group.members.map((player) => db.playerDao.addPlayer(player: player)),
|
||||
);
|
||||
});
|
||||
Future<bool> addGroup({required Group group}) async {
|
||||
if (!await groupExists(groupId: group.id)) {
|
||||
await db.transaction(() async {
|
||||
await into(
|
||||
groupTable,
|
||||
).insert(GroupTableCompanion.insert(id: group.id, name: group.name));
|
||||
await db.batch(
|
||||
(b) => b.insertAll(
|
||||
db.playerGroupTable,
|
||||
group.members
|
||||
.map(
|
||||
(member) => PlayerGroupTableCompanion.insert(
|
||||
playerId: member.id,
|
||||
groupId: group.id,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
await Future.wait(
|
||||
group.members.map((player) => db.playerDao.addPlayer(player: player)),
|
||||
);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Deletes the group with the given [id] from the database.
|
||||
|
||||
@@ -26,14 +26,14 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
|
||||
/// Adds a new [player] to the database.
|
||||
/// If a player with the same ID already exists, updates their name to
|
||||
/// the new one.
|
||||
Future<void> addPlayer({required Player player}) async {
|
||||
Future<bool> addPlayer({required Player player}) async {
|
||||
if (!await playerExists(playerId: player.id)) {
|
||||
await into(
|
||||
playerTable,
|
||||
).insert(PlayerTableCompanion.insert(id: player.id, name: player.name));
|
||||
} else {
|
||||
await updatePlayername(playerId: player.id, newName: player.name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Deletes the player with the given [id] from the database.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/db/database.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/custom_navigation_bar.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@@ -19,6 +21,7 @@ class GameTracker extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
addSampleGroupData(context);
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Game Tracker',
|
||||
@@ -39,4 +42,74 @@ class GameTracker extends StatelessWidget {
|
||||
home: const CustomNavigationBar(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> addSampleGroupData(BuildContext context) async {
|
||||
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||
if (await db.groupDao.getGroupCount() == 0) {
|
||||
final List<Player> allPlayers = [
|
||||
Player(id: '1', name: 'Alex'),
|
||||
Player(id: '2', name: 'Ben'),
|
||||
Player(id: '3', name: 'Chris'),
|
||||
Player(id: '4', name: 'Daniel'),
|
||||
Player(id: '5', name: 'Max Mustermann'),
|
||||
Player(id: '6', name: 'Sebastian'),
|
||||
Player(id: '7', name: 'Jonathan'),
|
||||
Player(id: '8', name: 'Alexander'),
|
||||
];
|
||||
|
||||
// 2. Erstelle und füge 8 Gruppen mit unterschiedlicher Spieleranzahl hinzu
|
||||
await db.groupDao.addGroup(
|
||||
group: Group(
|
||||
id: '1',
|
||||
name: 'Anfänger',
|
||||
members: allPlayers.sublist(0, 3),
|
||||
),
|
||||
); // 3 Spieler
|
||||
await db.groupDao.addGroup(
|
||||
group: Group(
|
||||
id: '2',
|
||||
name: 'Die glorreichen Sieben',
|
||||
members: allPlayers.sublist(0, 7),
|
||||
),
|
||||
); // 7 Spieler
|
||||
await db.groupDao.addGroup(
|
||||
group: Group(
|
||||
id: '3',
|
||||
name: 'Profis',
|
||||
members: allPlayers.sublist(4, 8),
|
||||
),
|
||||
); // 4 Spieler
|
||||
await db.groupDao.addGroup(
|
||||
group: Group(
|
||||
id: '4',
|
||||
name: 'Duo Infernale',
|
||||
members: [allPlayers[0], allPlayers[7]],
|
||||
),
|
||||
); // 2 Spieler
|
||||
await db.groupDao.addGroup(
|
||||
group: Group(
|
||||
id: '5',
|
||||
name: 'Die fantastischen Fünf',
|
||||
members: allPlayers.sublist(1, 6),
|
||||
),
|
||||
); // 5 Spieler
|
||||
await db.groupDao.addGroup(
|
||||
group: Group(
|
||||
id: '6',
|
||||
name: 'Feierabend-Zocker',
|
||||
members: allPlayers.sublist(0, 6),
|
||||
),
|
||||
); // 6 Spieler
|
||||
await db.groupDao.addGroup(
|
||||
group: Group(id: '7', name: 'Alle Mann an Bord!', members: allPlayers),
|
||||
); // 8 Spieler
|
||||
await db.groupDao.addGroup(
|
||||
group: Group(
|
||||
id: '8',
|
||||
name: 'Testgruppe Alpha',
|
||||
members: [allPlayers[1], allPlayers[3]],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||
import 'package:game_tracker/presentation/widgets/double_row_info_tile.dart';
|
||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||
|
||||
class GameHistoryView extends StatefulWidget {
|
||||
const GameHistoryView({super.key});
|
||||
@@ -178,9 +178,11 @@ class _GameHistoryViewState extends State<GameHistoryView> {
|
||||
|
||||
Widget gameHistoryListView(allGameData, suggestedGameData) {
|
||||
if (suggestedGameData.isEmpty && allGameData.isEmpty) {
|
||||
return TopCenteredMessage("Keine Spiele erstellt");
|
||||
return TopCenteredMessage(message: "Keine Spiele erstellt");
|
||||
} else if (suggestedGameData.isEmpty) {
|
||||
return TopCenteredMessage("Kein Spiel mit den Suchparametern gefunden.");
|
||||
return TopCenteredMessage(
|
||||
message: "Kein Spiel mit den Suchparametern gefunden.",
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: suggestedGameData.length,
|
||||
|
||||
@@ -1,10 +1,108 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/presentation/widgets/full_width_button.dart';
|
||||
import 'package:game_tracker/presentation/widgets/group_tile.dart';
|
||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
|
||||
class GroupsView extends StatelessWidget {
|
||||
class GroupsView extends StatefulWidget {
|
||||
const GroupsView({super.key});
|
||||
|
||||
@override
|
||||
State<GroupsView> createState() => _GroupsViewState();
|
||||
}
|
||||
|
||||
class _GroupsViewState extends State<GroupsView> {
|
||||
late Future<List<Group>> _allGroupsFuture;
|
||||
|
||||
final player = Player(id: 'p1', name: 'Sample');
|
||||
late final List<Group> skeletonData = List.filled(
|
||||
8,
|
||||
Group(
|
||||
id: '0',
|
||||
name: 'Sample Game',
|
||||
members: [player, player, player, player],
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||
_allGroupsFuture = db.groupDao.getAllGroups();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(child: Text('Groups View'));
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
body: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
FutureBuilder<List<Group>>(
|
||||
future: _allGroupsFuture,
|
||||
builder:
|
||||
(BuildContext context, AsyncSnapshot<List<Group>> snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return const Center(
|
||||
child: TopCenteredMessage(
|
||||
message: 'Error while loading group data.',
|
||||
),
|
||||
);
|
||||
}
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
(!snapshot.hasData || snapshot.data!.isEmpty)) {
|
||||
return const Center(
|
||||
child: TopCenteredMessage(
|
||||
message: 'No groups created yet.',
|
||||
),
|
||||
);
|
||||
}
|
||||
final bool isLoading =
|
||||
snapshot.connectionState == ConnectionState.waiting;
|
||||
final List<Group> groups = isLoading
|
||||
? skeletonData
|
||||
: (snapshot.data ?? []);
|
||||
return Skeletonizer(
|
||||
effect: PulseEffect(
|
||||
from: Colors.grey[800]!,
|
||||
to: Colors.grey[600]!,
|
||||
duration: const Duration(milliseconds: 800),
|
||||
),
|
||||
enabled: isLoading,
|
||||
enableSwitchAnimation: true,
|
||||
switchAnimationConfig: const SwitchAnimationConfig(
|
||||
duration: Duration(milliseconds: 200),
|
||||
switchInCurve: Curves.linear,
|
||||
switchOutCurve: Curves.linear,
|
||||
transitionBuilder:
|
||||
AnimatedSwitcher.defaultTransitionBuilder,
|
||||
layoutBuilder: AnimatedSwitcher.defaultLayoutBuilder,
|
||||
),
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 85),
|
||||
itemCount: groups.length + 1,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index == groups.length) {
|
||||
return const SizedBox(height: 60);
|
||||
}
|
||||
return GroupTile(group: groups[index]);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 80,
|
||||
child: FullWidthButton(text: 'Create Group', onPressed: () {}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
29
lib/presentation/widgets/full_width_button.dart
Normal file
29
lib/presentation/widgets/full_width_button.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
class FullWidthButton extends StatelessWidget {
|
||||
const FullWidthButton({super.key, required this.text, this.onPressed});
|
||||
|
||||
final String text;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
onPressed: onPressed,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: Size(MediaQuery.of(context).size.width * 0.90, 60),
|
||||
backgroundColor: CustomTheme.primaryColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 22,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
82
lib/presentation/widgets/group_tile.dart
Normal file
82
lib/presentation/widgets/group_tile.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
|
||||
class GroupTile extends StatelessWidget {
|
||||
const GroupTile({super.key, required this.group});
|
||||
|
||||
final Group group;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: MediaQuery.of(context).size.width * 0.90,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20, 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),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
group.name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'${group.members.length}',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
const Icon(Icons.group),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Wrap(
|
||||
alignment: WrapAlignment.start,
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
spacing: 12.0,
|
||||
runSpacing: 8.0,
|
||||
children: <Widget>[
|
||||
for (var member in group.members)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.onBoxColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Skeleton.ignore(
|
||||
child: Text(
|
||||
member.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 2.5),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Widget TopCenteredMessage(String message) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 100),
|
||||
margin: EdgeInsets.only(left: 10, right: 10),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Text(
|
||||
"$message",
|
||||
style: TextStyle(fontSize: 20),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
class TopCenteredMessage extends StatelessWidget {
|
||||
const TopCenteredMessage({super.key, required this.message});
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 100),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Text(
|
||||
message,
|
||||
style: const TextStyle(fontSize: 20),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ dependencies:
|
||||
drift_flutter: ^0.2.4
|
||||
path_provider: ^2.1.5
|
||||
provider: ^6.1.5
|
||||
skeletonizer: ^2.1.0+1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -36,6 +36,29 @@ void main() {
|
||||
await database.close();
|
||||
});
|
||||
|
||||
test('all groups get fetched correclty', () async {
|
||||
final testgroup2 = Group(
|
||||
id: 'gr2',
|
||||
name: 'Second Group',
|
||||
members: [player2, player3, player4],
|
||||
);
|
||||
await database.groupDao.addGroup(group: testgroup);
|
||||
await database.groupDao.addGroup(group: testgroup2);
|
||||
|
||||
final allGroups = await database.groupDao.getAllGroups();
|
||||
expect(allGroups.length, 2);
|
||||
|
||||
final fetchedGroup1 = allGroups.firstWhere((g) => g.id == testgroup.id);
|
||||
expect(fetchedGroup1.name, testgroup.name);
|
||||
expect(fetchedGroup1.members.length, testgroup.members.length);
|
||||
expect(fetchedGroup1.members.elementAt(0).id, player1.id);
|
||||
|
||||
final fetchedGroup2 = allGroups.firstWhere((g) => g.id == testgroup2.id);
|
||||
expect(fetchedGroup2.name, testgroup2.name);
|
||||
expect(fetchedGroup2.members.length, testgroup2.members.length);
|
||||
expect(fetchedGroup2.members.elementAt(0).id, player2.id);
|
||||
});
|
||||
|
||||
test('group and group members gets added correctly', () async {
|
||||
await database.groupDao.addGroup(group: testgroup);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user