Merge remote-tracking branch 'origin/development' into enhancement/128-PageRoutes-adaptable-machen

# Conflicts:
#	lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart
This commit is contained in:
2026-01-10 14:57:33 +01:00
3 changed files with 286 additions and 279 deletions

View File

@@ -44,66 +44,68 @@ class _CreateGroupViewState extends State<CreateGroupView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return Scaffold( return ScaffoldMessenger(
backgroundColor: CustomTheme.backgroundColor, child: Scaffold(
appBar: AppBar(title: Text(loc.create_new_group)), backgroundColor: CustomTheme.backgroundColor,
body: SafeArea( appBar: AppBar(title: Text(loc.create_new_group)),
child: Column( body: SafeArea(
mainAxisAlignment: MainAxisAlignment.start, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.start,
Container( children: [
margin: CustomTheme.standardMargin, Container(
child: TextInputField( margin: CustomTheme.standardMargin,
controller: _groupNameController, child: TextInputField(
hintText: loc.group_name, controller: _groupNameController,
hintText: loc.group_name,
),
), ),
), Expanded(
Expanded( child: PlayerSelection(
child: PlayerSelection( onChanged: (value) {
onChanged: (value) { setState(() {
setState(() { selectedPlayers = [...value];
selectedPlayers = [...value]; });
}); },
}, ),
), ),
), CustomWidthButton(
CustomWidthButton( text: loc.create_group,
text: loc.create_group, sizeRelativeToWidth: 0.95,
sizeRelativeToWidth: 0.95, buttonType: ButtonType.primary,
buttonType: ButtonType.primary, onPressed:
onPressed: (_groupNameController.text.isEmpty ||
(_groupNameController.text.isEmpty || (selectedPlayers.length < 2))
(selectedPlayers.length < 2)) ? null
? null : () async {
: () async { bool success = await db.groupDao.addGroup(
bool success = await db.groupDao.addGroup( group: Group(
group: Group( name: _groupNameController.text.trim(),
name: _groupNameController.text.trim(), members: selectedPlayers,
members: selectedPlayers,
),
);
if (!context.mounted) return;
if (success) {
Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: CustomTheme.boxColor,
content: Center(
child: Text(
AppLocalizations.of(
context,
).error_creating_group,
style: const TextStyle(color: Colors.white),
),
),
), ),
); );
} if (!context.mounted) return;
}, if (success) {
), Navigator.pop(context);
const SizedBox(height: 20), } else {
], ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: CustomTheme.boxColor,
content: Center(
child: Text(
AppLocalizations.of(
context,
).error_creating_group,
style: const TextStyle(color: Colors.white),
),
),
),
);
}
},
),
const SizedBox(height: 20),
],
),
), ),
), ),
); );

View File

@@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/adaptive_page_route.dart'; import 'package:game_tracker/core/adaptive_page_route.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/custom_theme.dart';
@@ -119,140 +120,142 @@ class _CreateMatchViewState extends State<CreateMatchView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return Scaffold( return ScaffoldMessenger(
backgroundColor: CustomTheme.backgroundColor, child: Scaffold(
appBar: AppBar(title: Text(loc.create_new_match)), backgroundColor: CustomTheme.backgroundColor,
body: SafeArea( appBar: AppBar(title: Text(loc.create_new_match)),
child: Column( body: SafeArea(
mainAxisAlignment: MainAxisAlignment.start, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.start,
Container( children: [
margin: CustomTheme.tileMargin, Container(
child: TextInputField( margin: CustomTheme.tileMargin,
controller: _matchNameController, child: TextInputField(
hintText: hintText ?? '', controller: _matchNameController,
hintText: hintText ?? '',
),
), ),
), ChooseTile(
ChooseTile( title: loc.game,
title: loc.game, trailingText: selectedGameIndex == -1
trailingText: selectedGameIndex == -1 ? loc.none
? loc.none : games[selectedGameIndex].$1,
: games[selectedGameIndex].$1, onPressed: () async {
onPressed: () async { selectedGameIndex = await Navigator.of(context).push(
selectedGameIndex = await Navigator.of(context).push( MaterialPageRoute(
adaptivePageRoute( builder: (context) => ChooseGameView(
builder: (context) => ChooseGameView( games: games,
games: games, initialGameIndex: selectedGameIndex,
initialGameIndex: selectedGameIndex, ),
), ),
), );
);
setState(() {
if (selectedGameIndex != -1) {
hintText = games[selectedGameIndex].$1;
selectedRuleset = games[selectedGameIndex].$3;
selectedRulesetIndex = _rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
} else {
hintText = loc.match_name;
selectedRuleset = null;
}
});
},
),
ChooseTile(
title: loc.ruleset,
trailingText: selectedRuleset == null
? loc.none
: translateRulesetToString(selectedRuleset!, context),
onPressed: () async {
selectedRuleset = await Navigator.of(context).push(
adaptivePageRoute(
builder: (context) => ChooseRulesetView(
rulesets: _rulesets,
initialRulesetIndex: selectedRulesetIndex,
),
),
);
if (!mounted) return;
selectedRulesetIndex = _rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
selectedGameIndex = -1;
setState(() {});
},
),
ChooseTile(
title: loc.group,
trailingText: selectedGroup == null
? loc.none_group
: selectedGroup!.name,
onPressed: () async {
selectedGroup = await Navigator.of(context).push(
adaptivePageRoute(
builder: (context) => ChooseGroupView(
groups: groupsList,
initialGroupId: selectedGroupId,
),
),
);
selectedGroupId = selectedGroup?.id ?? '';
if (selectedGroup != null) {
filteredPlayerList = playerList
.where(
(p) => !selectedGroup!.members.any((m) => m.id == p.id),
)
.toList();
} else {
filteredPlayerList = List.from(playerList);
}
setState(() {});
},
),
Expanded(
child: PlayerSelection(
key: ValueKey(selectedGroup?.id ?? 'no_group'),
initialSelectedPlayers: selectedPlayers ?? [],
availablePlayers: filteredPlayerList,
onChanged: (value) {
setState(() { setState(() {
selectedPlayers = value; if (selectedGameIndex != -1) {
hintText = games[selectedGameIndex].$1;
selectedRuleset = games[selectedGameIndex].$3;
selectedRulesetIndex = _rulesets.indexWhere(
(r) => r.$1 == selectedRuleset,
);
} else {
hintText = loc.match_name;
selectedRuleset = null;
}
}); });
}, },
), ),
), ChooseTile(
CustomWidthButton( title: loc.ruleset,
text: loc.create_match, trailingText: selectedRuleset == null
sizeRelativeToWidth: 0.95, ? loc.none
buttonType: ButtonType.primary, : translateRulesetToString(selectedRuleset!, context),
onPressed: _enableCreateGameButton() onPressed: () async {
? () async { selectedRuleset = await Navigator.of(context).push(
Match match = Match( MaterialPageRoute(
name: _matchNameController.text.isEmpty builder: (context) => ChooseRulesetView(
? (hintText ?? '') rulesets: _rulesets,
: _matchNameController.text.trim(), initialRulesetIndex: selectedRulesetIndex,
createdAt: DateTime.now(), ),
group: selectedGroup, ),
players: selectedPlayers, );
); if (!mounted) return;
await db.matchDao.addMatch(match: match); selectedRulesetIndex = _rulesets.indexWhere(
if (context.mounted) { (r) => r.$1 == selectedRuleset,
Navigator.pushReplacement( );
context, selectedGameIndex = -1;
adaptivePageRoute( setState(() {});
fullscreenDialog: true, },
builder: (context) => MatchResultView( ),
match: match, ChooseTile(
onWinnerChanged: widget.onWinnerChanged, title: loc.group,
), trailingText: selectedGroup == null
), ? loc.none_group
: selectedGroup!.name,
onPressed: () async {
selectedGroup = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseGroupView(
groups: groupsList,
initialGroupId: selectedGroupId,
),
),
);
selectedGroupId = selectedGroup?.id ?? '';
if (selectedGroup != null) {
filteredPlayerList = playerList
.where(
(p) => !selectedGroup!.members.any((m) => m.id == p.id),
)
.toList();
} else {
filteredPlayerList = List.from(playerList);
}
setState(() {});
},
),
Expanded(
child: PlayerSelection(
key: ValueKey(selectedGroup?.id ?? 'no_group'),
initialSelectedPlayers: selectedPlayers ?? [],
availablePlayers: filteredPlayerList,
onChanged: (value) {
setState(() {
selectedPlayers = value;
});
},
),
),
CustomWidthButton(
text: loc.create_match,
sizeRelativeToWidth: 0.95,
buttonType: ButtonType.primary,
onPressed: _enableCreateGameButton()
? () async {
Match match = Match(
name: _matchNameController.text.isEmpty
? (hintText ?? '')
: _matchNameController.text.trim(),
createdAt: DateTime.now(),
group: selectedGroup,
players: selectedPlayers,
); );
await db.matchDao.addMatch(match: match);
if (context.mounted) {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
fullscreenDialog: true,
builder: (context) => MatchResultView(
match: match,
onWinnerChanged: widget.onWinnerChanged,
),
),
);
}
} }
} : null,
: null, ),
), ],
], ),
), ),
), ),
); );

View File

@@ -21,105 +21,107 @@ class _SettingsViewState extends State<SettingsView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return Scaffold( return ScaffoldMessenger(
appBar: AppBar(backgroundColor: CustomTheme.backgroundColor), child: Scaffold(
backgroundColor: CustomTheme.backgroundColor, appBar: AppBar(backgroundColor: CustomTheme.backgroundColor),
body: LayoutBuilder( backgroundColor: CustomTheme.backgroundColor,
builder: (BuildContext context, BoxConstraints constraints) => body: LayoutBuilder(
SingleChildScrollView( builder: (BuildContext context, BoxConstraints constraints) =>
child: Column( SingleChildScrollView(
mainAxisAlignment: MainAxisAlignment.start, child: Column(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Padding( children: [
padding: const EdgeInsets.fromLTRB(24, 0, 24, 10), Padding(
child: Text( padding: const EdgeInsets.fromLTRB(24, 0, 24, 10),
textAlign: TextAlign.start, child: Text(
loc.menu, textAlign: TextAlign.start,
style: const TextStyle( loc.menu,
fontSize: 28, style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 28,
), fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 10,
),
child: Text(
textAlign: TextAlign.start,
loc.settings,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
),
SettingsListTile(
title: loc.export_data,
icon: Icons.upload_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final String json =
await DataTransferService.getAppDataAsJson(context);
final result = await DataTransferService.exportData(
json,
'game_tracker-data',
);
if (!context.mounted) return;
showExportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: loc.import_data,
icon: Icons.download_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final result = await DataTransferService.importData(
context,
);
if (!context.mounted) return;
showImportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: loc.delete_all_data,
icon: Icons.delete_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () {
showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(loc.delete_all_data),
content: Text(loc.this_cannot_be_undone),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(loc.cancel),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(loc.delete),
),
],
), ),
).then((confirmed) { ),
if (confirmed == true && context.mounted) { ),
DataTransferService.deleteAllData(context); Padding(
showSnackbar( padding: const EdgeInsets.symmetric(
context: context, horizontal: 24,
message: AppLocalizations.of( vertical: 10,
context, ),
).data_successfully_deleted, child: Text(
); textAlign: TextAlign.start,
} loc.settings,
}); style: const TextStyle(
}, fontSize: 22,
), fontWeight: FontWeight.bold,
], ),
),
),
SettingsListTile(
title: loc.export_data,
icon: Icons.upload_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final String json =
await DataTransferService.getAppDataAsJson(context);
final result = await DataTransferService.exportData(
json,
'game_tracker-data',
);
if (!context.mounted) return;
showExportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: loc.import_data,
icon: Icons.download_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () async {
final result = await DataTransferService.importData(
context,
);
if (!context.mounted) return;
showImportSnackBar(context: context, result: result);
},
),
SettingsListTile(
title: loc.delete_all_data,
icon: Icons.delete_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () {
showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(loc.delete_all_data),
content: Text(loc.this_cannot_be_undone),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(loc.cancel),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(loc.delete),
),
],
),
).then((confirmed) {
if (confirmed == true && context.mounted) {
DataTransferService.deleteAllData(context);
showSnackbar(
context: context,
message: AppLocalizations.of(
context,
).data_successfully_deleted,
);
}
});
},
),
],
),
), ),
), ),
), ),
); );
} }