Merge pull request 'SnackBar Sichtbarkeit' (#144) from bug/130-Snackbar-Sichtbarkeit into development

Reviewed-on: #144
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
Reviewed-by: Felix Kirchner <felix.kirchner.fk@gmail.com>
This commit was merged in pull request #144.
This commit is contained in:
2026-01-10 13:54:57 +00:00
3 changed files with 285 additions and 279 deletions

View File

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

View File

@@ -119,140 +119,142 @@ class _CreateMatchViewState extends State<CreateMatchView> {
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar(title: Text(loc.create_new_match)),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
margin: CustomTheme.tileMargin,
child: TextInputField(
controller: _matchNameController,
hintText: hintText ?? '',
return ScaffoldMessenger(
child: Scaffold(
backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar(title: Text(loc.create_new_match)),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
margin: CustomTheme.tileMargin,
child: TextInputField(
controller: _matchNameController,
hintText: hintText ?? '',
),
),
),
ChooseTile(
title: loc.game,
trailingText: selectedGameIndex == -1
? loc.none
: games[selectedGameIndex].$1,
onPressed: () async {
selectedGameIndex = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseGameView(
games: games,
initialGameIndex: selectedGameIndex,
ChooseTile(
title: loc.game,
trailingText: selectedGameIndex == -1
? loc.none
: games[selectedGameIndex].$1,
onPressed: () async {
selectedGameIndex = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ChooseGameView(
games: games,
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(
MaterialPageRoute(
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(
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;
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;
}
});
},
),
),
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,
),
),
ChooseTile(
title: loc.ruleset,
trailingText: selectedRuleset == null
? loc.none
: translateRulesetToString(selectedRuleset!, context),
onPressed: () async {
selectedRuleset = await Navigator.of(context).push(
MaterialPageRoute(
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(
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
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(backgroundColor: CustomTheme.backgroundColor),
backgroundColor: CustomTheme.backgroundColor,
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) =>
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 10),
child: Text(
textAlign: TextAlign.start,
loc.menu,
style: const TextStyle(
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),
),
],
return ScaffoldMessenger(
child: Scaffold(
appBar: AppBar(backgroundColor: CustomTheme.backgroundColor),
backgroundColor: CustomTheme.backgroundColor,
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) =>
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 10),
child: Text(
textAlign: TextAlign.start,
loc.menu,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
).then((confirmed) {
if (confirmed == true && context.mounted) {
DataTransferService.deleteAllData(context);
showSnackbar(
context: context,
message: AppLocalizations.of(
context,
).data_successfully_deleted,
);
}
});
},
),
],
),
),
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);
showSnackbar(
context: context,
message: AppLocalizations.of(
context,
).data_successfully_deleted,
);
}
});
},
),
],
),
),
),
),
),
);
}