Haptisches Feedback hinzufügen #216

Merged
flixcoo merged 14 commits from feature/215-haptisches-feedback-hinzufügen into development 2026-05-14 13:09:01 +00:00
20 changed files with 165 additions and 53 deletions
Showing only changes of commit bc59d1d91c - Show all commits

View File

@@ -7,6 +7,7 @@ import 'package:tallee/presentation/views/main_menu/group_view/group_view.dart';
import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart';
import 'package:tallee/presentation/views/main_menu/statistics_view.dart'; import 'package:tallee/presentation/views/main_menu/statistics_view.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/navbar_item.dart'; import 'package:tallee/presentation/widgets/navbar_item.dart';
class CustomNavigationBar extends StatefulWidget { class CustomNavigationBar extends StatefulWidget {
@@ -54,7 +55,7 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
actions: [ actions: [
IconButton( HapticIconButton(
onPressed: () async { onPressed: () async {
final navigator = Navigator.of(context); final navigator = Navigator.of(context);
await HapticFeedback.selectionClick(); await HapticFeedback.selectionClick();
sneeex marked this conversation as resolved
Review

Warum rufst du hier extra nochmal HapticFeedback.selectionClick() auf?

Warum rufst du hier extra nochmal `HapticFeedback.selectionClick()` auf?
Review

war wohl ausversehen, vmtl noch aus dem testing, habs entfernt

war wohl ausversehen, vmtl noch aus dem testing, habs entfernt

View File

@@ -135,9 +135,13 @@ class _CreateGroupViewState extends State<CreateGroupView> {
if (success) { if (success) {
await HapticFeedback.successNotification(); await HapticFeedback.successNotification();
if (mounted) {
Navigator.pop(context, updatedGroup); Navigator.pop(context, updatedGroup);
}
} else { } else {
if (mounted) {
await HapticFeedback.errorNotification(); await HapticFeedback.errorNotification();
}
showSnackbar( showSnackbar(
message: widget.groupToEdit == null message: widget.groupToEdit == null
? loc.error_creating_group ? loc.error_creating_group

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/adaptive_page_route.dart';
@@ -13,6 +12,7 @@ import 'package:tallee/data/models/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart'; import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart';
import 'package:tallee/presentation/widgets/app_skeleton.dart'; import 'package:tallee/presentation/widgets/app_skeleton.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart'; import 'package:tallee/presentation/widgets/colored_icon_container.dart';
import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart';
@@ -66,10 +66,9 @@ class _GroupDetailViewState extends State<GroupDetailView> {
appBar: AppBar( appBar: AppBar(
title: Text(loc.group_profile), title: Text(loc.group_profile),
actions: [ actions: [
IconButton( HapticIconButton(
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: () async { onPressed: () async {
await HapticFeedback.selectionClick();
showDialog<bool>( showDialog<bool>(
context: context, context: context,
builder: (context) => CustomAlertDialog( builder: (context) => CustomAlertDialog(
@@ -77,17 +76,11 @@ class _GroupDetailViewState extends State<GroupDetailView> {
content: Text(loc.this_cannot_be_undone), content: Text(loc.this_cannot_be_undone),
actions: [ actions: [
CustomDialogAction( CustomDialogAction(
onPressed: () async { onPressed: () => Navigator.of(context).pop(true),
await HapticFeedback.warningNotification();
Navigator.of(context).pop(true);
},
text: loc.delete, text: loc.delete,
), ),
CustomDialogAction( CustomDialogAction(
onPressed: () async { onPressed: () => Navigator.of(context).pop(false),
await HapticFeedback.selectionClick();
Navigator.of(context).pop(false);
},
buttonType: ButtonType.secondary, buttonType: ButtonType.secondary,
text: loc.cancel, text: loc.cancel,
), ),

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/constants.dart'; import 'package:tallee/core/constants.dart';
@@ -103,7 +102,6 @@ class _GroupViewState extends State<GroupView> {
text: loc.create_group, text: loc.create_group,
icon: Icons.group_add, icon: Icons.group_add,
onPressed: () async { onPressed: () async {
await HapticFeedback.selectionClick();
await Navigator.push( await Navigator.push(
context, context,
adaptivePageRoute( adaptivePageRoute(

View File

@@ -7,6 +7,7 @@ import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/game.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_game_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_game_view.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:tallee/presentation/widgets/tiles/game_tile.dart'; import 'package:tallee/presentation/widgets/tiles/game_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart'; import 'package:tallee/presentation/widgets/top_centered_message.dart';
@@ -70,7 +71,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: HapticIconButton(
icon: const Icon(Icons.arrow_back_ios), icon: const Icon(Icons.arrow_back_ios),
onPressed: () { onPressed: () {
Navigator.of(context).pop( Navigator.of(context).pop(
@@ -83,7 +84,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
}, },
), ),
actions: [ actions: [
IconButton( HapticIconButton(
icon: const Icon(Icons.add), icon: const Icon(Icons.add),
onPressed: () async { onPressed: () async {
final result = await Navigator.push( final result = await Navigator.push(

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/group.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:tallee/presentation/widgets/tiles/group_tile.dart'; import 'package:tallee/presentation/widgets/tiles/group_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart'; import 'package:tallee/presentation/widgets/top_centered_message.dart';
@@ -45,7 +46,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: HapticIconButton(
icon: const Icon(Icons.arrow_back_ios), icon: const Icon(Icons.arrow_back_ios),
onPressed: () { onPressed: () {
Navigator.of(context).pop( Navigator.of(context).pop(

View File

@@ -12,6 +12,7 @@ import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/group.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart'; import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart';
import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart'; import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart';
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart'; import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
@@ -120,7 +121,7 @@ class _CreateGameViewState extends State<CreateGameView> {
title: Text(isEditing ? loc.edit_game : loc.create_game), title: Text(isEditing ? loc.edit_game : loc.create_game),
actions: [ actions: [
if (isEditMode()) if (isEditMode())
IconButton( HapticIconButton(
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: () async { onPressed: () async {
if (!context.mounted) return; if (!context.mounted) return;

View File

@@ -11,6 +11,7 @@ import 'package:tallee/data/models/match.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_match_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart'; import 'package:tallee/presentation/widgets/colored_icon_container.dart';
import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart';
@@ -60,7 +61,7 @@ class _MatchDetailViewState extends State<MatchDetailView> {
appBar: AppBar( appBar: AppBar(
title: Text(loc.match_profile), title: Text(loc.match_profile),
actions: [ actions: [
IconButton( HapticIconButton(
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: () async { onPressed: () async {
showDialog<bool>( showDialog<bool>(

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
@@ -8,6 +9,7 @@ import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score_entry.dart'; import 'package:tallee/data/models/score_entry.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart'; import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart';
@@ -112,7 +114,7 @@ class _MatchResultViewState extends State<MatchResultView> {
return Scaffold( return Scaffold(
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: HapticIconButton(
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
onPressed: () { onPressed: () {
widget.onWinnerChanged?.call(); widget.onWinnerChanged?.call();
@@ -204,6 +206,7 @@ class _MatchResultViewState extends State<MatchResultView> {
: RadioGroup<Player>( : RadioGroup<Player>(
groupValue: _selectedPlayer, groupValue: _selectedPlayer,
onChanged: (Player? value) async { onChanged: (Player? value) async {
await HapticFeedback.selectionClick();
setState(() { setState(() {
_selectedPlayer = value; _selectedPlayer = value;
}); });
@@ -217,6 +220,7 @@ class _MatchResultViewState extends State<MatchResultView> {
text: allPlayers[index].name, text: allPlayers[index].name,
value: allPlayers[index], value: allPlayers[index],
onContainerTap: (value) async { onContainerTap: (value) async {
await HapticFeedback.selectionClick();
setState(() { setState(() {
// Check if the already selected player is the same as the newly tapped player. // Check if the already selected player is the same as the newly tapped player.
if (_selectedPlayer == value) { if (_selectedPlayer == value) {
@@ -338,6 +342,12 @@ class _MatchResultViewState extends State<MatchResultView> {
}, },
); );
}, },
onReorderStart: (int n) async {
await HapticFeedback.selectionClick();
},
onReorderEnd: (int n) async {
await HapticFeedback.selectionClick();
},
onReorder: (int oldIndex, int newIndex) { onReorder: (int oldIndex, int newIndex) {
setState(() { setState(() {
if (newIndex > oldIndex) { if (newIndex > oldIndex) {

View File

@@ -276,37 +276,40 @@ class _SettingsViewState extends State<SettingsView> {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
switch (result) { switch (result) {
case ImportResult.success: case ImportResult.success:
if (context.mounted) {
await HapticFeedback.successNotification(); await HapticFeedback.successNotification();
if (context.mounted) {
showSnackbar(
context: context,
message: loc.data_successfully_imported,
);
} }
showSnackbar(context: context, message: loc.data_successfully_imported);
case ImportResult.invalidSchema: case ImportResult.invalidSchema:
if (context.mounted) {
await HapticFeedback.errorNotification(); await HapticFeedback.errorNotification();
} if (context.mounted) {
showSnackbar(context: context, message: loc.invalid_schema); showSnackbar(context: context, message: loc.invalid_schema);
}
case ImportResult.fileReadError: case ImportResult.fileReadError:
if (context.mounted) {
await HapticFeedback.errorNotification(); await HapticFeedback.errorNotification();
} if (context.mounted) {
showSnackbar(context: context, message: loc.error_reading_file); showSnackbar(context: context, message: loc.error_reading_file);
}
case ImportResult.canceled: case ImportResult.canceled:
if (context.mounted) {
await HapticFeedback.errorNotification(); await HapticFeedback.errorNotification();
} if (context.mounted) {
showSnackbar(context: context, message: loc.import_canceled); showSnackbar(context: context, message: loc.import_canceled);
}
case ImportResult.formatException: case ImportResult.formatException:
if (context.mounted) {
await HapticFeedback.errorNotification(); await HapticFeedback.errorNotification();
} if (context.mounted) {
showSnackbar(context: context, message: loc.format_exception); showSnackbar(context: context, message: loc.format_exception);
case ImportResult.unknownException:
if (context.mounted) {
await HapticFeedback.errorNotification();
} }
case ImportResult.unknownException:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.unknown_exception); showSnackbar(context: context, message: loc.unknown_exception);
} }
} }
}
/// Displays a snackbar based on the export result. /// Displays a snackbar based on the export result.
/// ///
@@ -320,15 +323,24 @@ class _SettingsViewState extends State<SettingsView> {
switch (result) { switch (result) {
case ExportResult.success: case ExportResult.success:
await HapticFeedback.successNotification(); await HapticFeedback.successNotification();
showSnackbar(context: context, message: loc.data_successfully_exported); if (context.mounted) {
showSnackbar(
context: context,
message: loc.data_successfully_exported,
);
}
case ExportResult.canceled: case ExportResult.canceled:
await HapticFeedback.errorNotification(); await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.export_canceled); showSnackbar(context: context, message: loc.export_canceled);
await HapticFeedback.errorNotification(); }
case ExportResult.unknownException: case ExportResult.unknownException:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.unknown_exception); showSnackbar(context: context, message: loc.unknown_exception);
} }
} }
}
/// Displays a snackbar with the given message and optional action. /// Displays a snackbar with the given message and optional action.
/// ///

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
@@ -48,7 +49,12 @@ class CustomWidthButton extends StatelessWidget {
)!; )!;
return ElevatedButton( return ElevatedButton(
onPressed: onPressed, onPressed: onPressed == null
? null
: () async {
await HapticFeedback.selectionClick();
onPressed!.call();
},
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
foregroundColor: textcolor, foregroundColor: textcolor,
disabledForegroundColor: disabledTextColor, disabledForegroundColor: disabledTextColor,
@@ -78,7 +84,12 @@ class CustomWidthButton extends StatelessWidget {
: Color.lerp(CustomTheme.primaryColor, Colors.black, 0.5)!; : Color.lerp(CustomTheme.primaryColor, Colors.black, 0.5)!;
return OutlinedButton( return OutlinedButton(
onPressed: onPressed, onPressed: onPressed == null
? null
: () async {
await HapticFeedback.selectionClick();
onPressed!.call();
},
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
foregroundColor: textcolor, foregroundColor: textcolor,
disabledForegroundColor: disabledTextColor, disabledForegroundColor: disabledTextColor,
@@ -110,7 +121,12 @@ class CustomWidthButton extends StatelessWidget {
disabledBackgroundColor = Colors.transparent; disabledBackgroundColor = Colors.transparent;
return TextButton( return TextButton(
onPressed: onPressed, onPressed: onPressed == null
? null
: () async {
await HapticFeedback.selectionClick();
onPressed!.call();
},
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: textcolor, foregroundColor: textcolor,
disabledForegroundColor: disabledTextColor, disabledForegroundColor: disabledTextColor,

View File

@@ -1,6 +1,6 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
class HapticBackButton extends StatelessWidget { class HapticBackButton extends StatelessWidget {
const HapticBackButton({super.key}); const HapticBackButton({super.key});
@@ -13,10 +13,9 @@ class HapticBackButton extends StatelessWidget {
_ => Icons.arrow_back_rounded, _ => Icons.arrow_back_rounded,
}; };
return IconButton( return HapticIconButton(
icon: Icon(iconData), icon: Icon(iconData),
onPressed: () async { onPressed: () async {
await HapticFeedback.mediumImpact();
Navigator.of(context).maybePop(); Navigator.of(context).maybePop();
}, },
); );

View File

@@ -1,7 +1,7 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
class HapticCloseButton extends StatelessWidget { class HapticCloseButton extends StatelessWidget {
const HapticCloseButton({super.key}); const HapticCloseButton({super.key});
@@ -13,10 +13,9 @@ class HapticCloseButton extends StatelessWidget {
_ => Icons.close_rounded, _ => Icons.close_rounded,
}; };
return IconButton( return HapticIconButton(
icon: Icon(iconData), icon: Icon(iconData),
onPressed: () async { onPressed: () async {
await HapticFeedback.mediumImpact();
Navigator.of(context).maybePop(); Navigator.of(context).maybePop();
}, },
); );

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class HapticIconButton extends StatelessWidget {
sneeex marked this conversation as resolved
Review

Kannst du bitte noch bei den drei neuen Buttons den Splash-Effekt entfernen und eine Scale Animation hinzufügen, ähnlich wie bei main menu button? Ggf. ersetzt du dann IconButton() einfach durch einen Container(), vllt ist das sogar einfacher

Kannst du bitte noch bei den drei neuen Buttons den Splash-Effekt entfernen und eine Scale Animation hinzufügen, ähnlich wie bei main menu button? Ggf. ersetzt du dann `IconButton()` einfach durch einen `Container()`, vllt ist das sogar einfacher
Review

du meinst bei allen IconButtons? weil das ist ja dann nicht nur die drei buttons in den settings, sondern alle back buttons auch. Und was für ein scale meinst du? icon scale? button scale insgesamt? und wieso? finde das eigentlich glaube nicht so geil

du meinst bei allen IconButtons? weil das ist ja dann nicht nur die drei buttons in den settings, sondern alle back buttons auch. Und was für ein scale meinst du? icon scale? button scale insgesamt? und wieso? finde das eigentlich glaube nicht so geil
Review

Ja bei allen Icon Buttons.
Meinetwegen auch ohne Scale Effekt, aber auf jeden Fall ohne Splash. Und mit Scale meinte ich dass das Icon sich in der Größe verändert, wie beim MainMenuButton

Ja bei allen Icon Buttons. Meinetwegen auch ohne Scale Effekt, aber auf jeden Fall ohne Splash. Und mit Scale meinte ich dass das Icon sich in der Größe verändert, wie beim `MainMenuButton`
Review

Ja bei allen Icon Buttons.
Meinetwegen auch ohne Scale Effekt, aber auf jeden Fall ohne Splash. Und mit Scale meinte ich dass das Icon sich in der Größe verändert, wie beim MainMenuButton

Beim Main Menu Button ist aber der ganze Button mit Scale und nicht nur das Icon

> Ja bei allen Icon Buttons. > Meinetwegen auch ohne Scale Effekt, aber auf jeden Fall ohne Splash. Und mit Scale meinte ich dass das Icon sich in der Größe verändert, wie beim `MainMenuButton` Beim Main Menu Button ist aber der ganze Button mit Scale und nicht nur das Icon
Review

Der Icon Button ist ja aber nur n Icon, der hat ja keinen Hintergrund

Der Icon Button ist ja aber nur n Icon, der hat ja keinen Hintergrund
const HapticIconButton({
super.key,
required this.icon,
required this.onPressed,
this.tooltip,
this.iconSize,
this.color,
this.padding,
this.alignment,
this.constraints,
this.style,
this.isSelected,
this.selectedIcon,
});
final Widget icon;
final VoidCallback? onPressed;
final String? tooltip;
final double? iconSize;
final Color? color;
final EdgeInsetsGeometry? padding;
final AlignmentGeometry? alignment;
final BoxConstraints? constraints;
final ButtonStyle? style;
final bool? isSelected;
final Widget? selectedIcon;
@override
Widget build(BuildContext context) {
return IconButton(
tooltip: tooltip,
sneeex marked this conversation as resolved
Review

Nicht sinnvoll, wir sind ja auf mobile, da gibts ja keinen hover state

Nicht sinnvoll, wir sind ja auf mobile, da gibts ja keinen hover state
iconSize: iconSize,
color: color,
padding: padding,
alignment: alignment ?? Alignment.center,
constraints: constraints,
style: style,
isSelected: isSelected,
selectedIcon: selectedIcon,
icon: icon,
onPressed: onPressed == null
? null
: () async {
await HapticFeedback.selectionClick();
onPressed!.call();
},
);
}
}

View File

@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class MainMenuButton extends StatefulWidget { class MainMenuButton extends StatefulWidget {
/// A button for the main menu with an optional icon and a press animation. /// A button for the main menu with an optional icon and a press animation.
@@ -78,6 +79,7 @@ class _MainMenuButtonState extends State<MainMenuButton>
onTapUp: (_) async { onTapUp: (_) async {
_cancelTimers(); _cancelTimers();
if (mounted && !_isLongPressing) { if (mounted && !_isLongPressing) {
await HapticFeedback.selectionClick();
widget.onPressed(); widget.onPressed();
} }
_isLongPressing = false; _isLongPressing = false;

View File

@@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart'; import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
@@ -26,7 +27,10 @@ class CustomDialogAction extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedDialogButton( return AnimatedDialogButton(
onPressed: onPressed, onPressed: () async {
await HapticFeedback.selectionClick();
onPressed.call();
},
buttonText: text, buttonText: text,
buttonType: buttonType, buttonType: buttonType,
isDescructive: isDestructive, isDescructive: isDestructive,

View File

@@ -144,7 +144,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
text: player.name, text: player.name,
suffixText: getNameCountText(player), suffixText: getNameCountText(player),
onIconTap: () { onIconTap: () {
setState(() { setState(() async {
await HapticFeedback.selectionClick();
// Removes the player from the selection and notifies the parent. // Removes the player from the selection and notifies the parent.
selectedPlayers.remove(player); selectedPlayers.remove(player);
widget.onChanged([...selectedPlayers]); widget.onChanged([...selectedPlayers]);

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
class ChooseTile extends StatefulWidget { class ChooseTile extends StatefulWidget {
@@ -30,7 +31,12 @@ class _ChooseTileState extends State<ChooseTile> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: widget.onPressed, onTap: () async {
await HapticFeedback.vibrate();
if (widget.onPressed != null) {
widget.onPressed!.call();
}
},
child: Container( child: Container(
margin: CustomTheme.tileMargin, margin: CustomTheme.tileMargin,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
class CustomCheckboxListTile extends StatelessWidget { class CustomCheckboxListTile extends StatelessWidget {
@@ -16,7 +17,10 @@ class CustomCheckboxListTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () => onChanged(!value), onTap: () async {
await HapticFeedback.selectionClick();
onChanged(!value);
},
child: Container( child: Container(
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
padding: const EdgeInsets.symmetric(horizontal: 2), padding: const EdgeInsets.symmetric(horizontal: 2),
@@ -29,7 +33,8 @@ class CustomCheckboxListTile extends StatelessWidget {
children: [ children: [
Checkbox( Checkbox(
value: value, value: value,
onChanged: (bool? v) { onChanged: (bool? v) async {
await HapticFeedback.selectionClick();
if (v == null) return; if (v == null) return;
onChanged(v); onChanged(v);
}, },

View File

@@ -1,6 +1,7 @@
import 'dart:core' hide Match; import 'dart:core' hide Match;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
@@ -51,7 +52,10 @@ class _MatchTileState extends State<MatchTile> {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return GestureDetector( return GestureDetector(
onTap: widget.onTap, onTap: () async {
await HapticFeedback.selectionClick();
widget.onTap.call();
},
child: Container( child: Container(
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
width: widget.width, width: widget.width,