diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index 366ecbd..6e9e6d0 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -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/settings_view/settings_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'; class CustomNavigationBar extends StatefulWidget { @@ -54,7 +55,7 @@ class _CustomNavigationBarState extends State backgroundColor: CustomTheme.backgroundColor, scrolledUnderElevation: 0, actions: [ - IconButton( + HapticIconButton( onPressed: () async { final navigator = Navigator.of(context); await HapticFeedback.selectionClick(); diff --git a/lib/presentation/views/main_menu/group_view/create_group_view.dart b/lib/presentation/views/main_menu/group_view/create_group_view.dart index 55f59aa..84efbe1 100644 --- a/lib/presentation/views/main_menu/group_view/create_group_view.dart +++ b/lib/presentation/views/main_menu/group_view/create_group_view.dart @@ -135,9 +135,13 @@ class _CreateGroupViewState extends State { if (success) { await HapticFeedback.successNotification(); - Navigator.pop(context, updatedGroup); + if (mounted) { + Navigator.pop(context, updatedGroup); + } } else { - await HapticFeedback.errorNotification(); + if (mounted) { + await HapticFeedback.errorNotification(); + } showSnackbar( message: widget.groupToEdit == null ? loc.error_creating_group diff --git a/lib/presentation/views/main_menu/group_view/group_detail_view.dart b/lib/presentation/views/main_menu/group_view/group_detail_view.dart index 746d0ee..c417ec4 100644 --- a/lib/presentation/views/main_menu/group_view/group_detail_view.dart +++ b/lib/presentation/views/main_menu/group_view/group_detail_view.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.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/presentation/views/main_menu/group_view/create_group_view.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/colored_icon_container.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; @@ -66,10 +66,9 @@ class _GroupDetailViewState extends State { appBar: AppBar( title: Text(loc.group_profile), actions: [ - IconButton( + HapticIconButton( icon: const Icon(Icons.delete), onPressed: () async { - await HapticFeedback.selectionClick(); showDialog( context: context, builder: (context) => CustomAlertDialog( @@ -77,17 +76,11 @@ class _GroupDetailViewState extends State { content: Text(loc.this_cannot_be_undone), actions: [ CustomDialogAction( - onPressed: () async { - await HapticFeedback.warningNotification(); - Navigator.of(context).pop(true); - }, + onPressed: () => Navigator.of(context).pop(true), text: loc.delete, ), CustomDialogAction( - onPressed: () async { - await HapticFeedback.selectionClick(); - Navigator.of(context).pop(false); - }, + onPressed: () => Navigator.of(context).pop(false), buttonType: ButtonType.secondary, text: loc.cancel, ), diff --git a/lib/presentation/views/main_menu/group_view/group_view.dart b/lib/presentation/views/main_menu/group_view/group_view.dart index 923344e..c8a9398 100644 --- a/lib/presentation/views/main_menu/group_view/group_view.dart +++ b/lib/presentation/views/main_menu/group_view/group_view.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/constants.dart'; @@ -103,7 +102,6 @@ class _GroupViewState extends State { text: loc.create_group, icon: Icons.group_add, onPressed: () async { - await HapticFeedback.selectionClick(); await Navigator.push( context, adaptivePageRoute( diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart index c019213..3c51cab 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart @@ -7,6 +7,7 @@ import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/models/game.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/widgets/buttons/haptic_icon_button.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/top_centered_message.dart'; @@ -70,7 +71,7 @@ class _ChooseGameViewState extends State { backgroundColor: CustomTheme.backgroundColor, resizeToAvoidBottomInset: false, appBar: AppBar( - leading: IconButton( + leading: HapticIconButton( icon: const Icon(Icons.arrow_back_ios), onPressed: () { Navigator.of(context).pop( @@ -83,7 +84,7 @@ class _ChooseGameViewState extends State { }, ), actions: [ - IconButton( + HapticIconButton( icon: const Icon(Icons.add), onPressed: () async { final result = await Navigator.push( diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart index c7471d8..1caa101 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_group_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/data/models/group.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/tiles/group_tile.dart'; import 'package:tallee/presentation/widgets/top_centered_message.dart'; @@ -45,7 +46,7 @@ class _ChooseGroupViewState extends State { backgroundColor: CustomTheme.backgroundColor, resizeToAvoidBottomInset: false, appBar: AppBar( - leading: IconButton( + leading: HapticIconButton( icon: const Icon(Icons.arrow_back_ios), onPressed: () { Navigator.of(context).pop( diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart index 3156476..4b4a977 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart @@ -12,6 +12,7 @@ import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/group.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/haptic_icon_button.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/text_input/text_input_field.dart'; @@ -120,7 +121,7 @@ class _CreateGameViewState extends State { title: Text(isEditing ? loc.edit_game : loc.create_game), actions: [ if (isEditMode()) - IconButton( + HapticIconButton( icon: const Icon(Icons.delete), onPressed: () async { if (!context.mounted) return; diff --git a/lib/presentation/views/main_menu/match_view/match_detail_view.dart b/lib/presentation/views/main_menu/match_view/match_detail_view.dart index 73d534d..b34f9df 100644 --- a/lib/presentation/views/main_menu/match_view/match_detail_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_detail_view.dart @@ -11,6 +11,7 @@ import 'package:tallee/data/models/match.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/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/colored_icon_container.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; @@ -60,7 +61,7 @@ class _MatchDetailViewState extends State { appBar: AppBar( title: Text(loc.match_profile), actions: [ - IconButton( + HapticIconButton( icon: const Icon(Icons.delete), onPressed: () async { showDialog( diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index ba138d6..47f91c5 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:tallee/core/custom_theme.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/l10n/generated/app_localizations.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_radio_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 { return Scaffold( backgroundColor: CustomTheme.backgroundColor, appBar: AppBar( - leading: IconButton( + leading: HapticIconButton( icon: const Icon(Icons.close), onPressed: () { widget.onWinnerChanged?.call(); @@ -204,6 +206,7 @@ class _MatchResultViewState extends State { : RadioGroup( groupValue: _selectedPlayer, onChanged: (Player? value) async { + await HapticFeedback.selectionClick(); setState(() { _selectedPlayer = value; }); @@ -217,6 +220,7 @@ class _MatchResultViewState extends State { text: allPlayers[index].name, value: allPlayers[index], onContainerTap: (value) async { + await HapticFeedback.selectionClick(); setState(() { // Check if the already selected player is the same as the newly tapped player. if (_selectedPlayer == value) { @@ -338,6 +342,12 @@ class _MatchResultViewState extends State { }, ); }, + onReorderStart: (int n) async { + await HapticFeedback.selectionClick(); + }, + onReorderEnd: (int n) async { + await HapticFeedback.selectionClick(); + }, onReorder: (int oldIndex, int newIndex) { setState(() { if (newIndex > oldIndex) { diff --git a/lib/presentation/views/main_menu/settings_view/settings_view.dart b/lib/presentation/views/main_menu/settings_view/settings_view.dart index c393ae5..d873b7a 100644 --- a/lib/presentation/views/main_menu/settings_view/settings_view.dart +++ b/lib/presentation/views/main_menu/settings_view/settings_view.dart @@ -276,35 +276,38 @@ class _SettingsViewState extends State { final loc = AppLocalizations.of(context); switch (result) { case ImportResult.success: + await HapticFeedback.successNotification(); if (context.mounted) { - await HapticFeedback.successNotification(); + showSnackbar( + context: context, + message: loc.data_successfully_imported, + ); } - showSnackbar(context: context, message: loc.data_successfully_imported); case ImportResult.invalidSchema: + await HapticFeedback.errorNotification(); if (context.mounted) { - await HapticFeedback.errorNotification(); + showSnackbar(context: context, message: loc.invalid_schema); } - showSnackbar(context: context, message: loc.invalid_schema); case ImportResult.fileReadError: + await HapticFeedback.errorNotification(); if (context.mounted) { - await HapticFeedback.errorNotification(); + showSnackbar(context: context, message: loc.error_reading_file); } - showSnackbar(context: context, message: loc.error_reading_file); case ImportResult.canceled: + await HapticFeedback.errorNotification(); if (context.mounted) { - await HapticFeedback.errorNotification(); + showSnackbar(context: context, message: loc.import_canceled); } - showSnackbar(context: context, message: loc.import_canceled); case ImportResult.formatException: + await HapticFeedback.errorNotification(); if (context.mounted) { - await HapticFeedback.errorNotification(); + showSnackbar(context: context, message: loc.format_exception); } - showSnackbar(context: context, message: loc.format_exception); case ImportResult.unknownException: + await HapticFeedback.errorNotification(); if (context.mounted) { - await HapticFeedback.errorNotification(); + showSnackbar(context: context, message: loc.unknown_exception); } - showSnackbar(context: context, message: loc.unknown_exception); } } @@ -320,13 +323,22 @@ class _SettingsViewState extends State { switch (result) { case ExportResult.success: 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: await HapticFeedback.errorNotification(); - showSnackbar(context: context, message: loc.export_canceled); - await HapticFeedback.errorNotification(); + if (context.mounted) { + showSnackbar(context: context, message: loc.export_canceled); + } case ExportResult.unknownException: - showSnackbar(context: context, message: loc.unknown_exception); + await HapticFeedback.errorNotification(); + if (context.mounted) { + showSnackbar(context: context, message: loc.unknown_exception); + } } } diff --git a/lib/presentation/widgets/buttons/custom_width_button.dart b/lib/presentation/widgets/buttons/custom_width_button.dart index 4fde6f8..556b784 100644 --- a/lib/presentation/widgets/buttons/custom_width_button.dart +++ b/lib/presentation/widgets/buttons/custom_width_button.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/enums.dart'; @@ -48,7 +49,12 @@ class CustomWidthButton extends StatelessWidget { )!; return ElevatedButton( - onPressed: onPressed, + onPressed: onPressed == null + ? null + : () async { + await HapticFeedback.selectionClick(); + onPressed!.call(); + }, style: ElevatedButton.styleFrom( foregroundColor: textcolor, disabledForegroundColor: disabledTextColor, @@ -78,7 +84,12 @@ class CustomWidthButton extends StatelessWidget { : Color.lerp(CustomTheme.primaryColor, Colors.black, 0.5)!; return OutlinedButton( - onPressed: onPressed, + onPressed: onPressed == null + ? null + : () async { + await HapticFeedback.selectionClick(); + onPressed!.call(); + }, style: OutlinedButton.styleFrom( foregroundColor: textcolor, disabledForegroundColor: disabledTextColor, @@ -110,7 +121,12 @@ class CustomWidthButton extends StatelessWidget { disabledBackgroundColor = Colors.transparent; return TextButton( - onPressed: onPressed, + onPressed: onPressed == null + ? null + : () async { + await HapticFeedback.selectionClick(); + onPressed!.call(); + }, style: TextButton.styleFrom( foregroundColor: textcolor, disabledForegroundColor: disabledTextColor, diff --git a/lib/presentation/widgets/buttons/haptic_back_button.dart b/lib/presentation/widgets/buttons/haptic_back_button.dart index 6f3f76a..4b672bf 100644 --- a/lib/presentation/widgets/buttons/haptic_back_button.dart +++ b/lib/presentation/widgets/buttons/haptic_back_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.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 { const HapticBackButton({super.key}); @@ -13,10 +13,9 @@ class HapticBackButton extends StatelessWidget { _ => Icons.arrow_back_rounded, }; - return IconButton( + return HapticIconButton( icon: Icon(iconData), onPressed: () async { - await HapticFeedback.mediumImpact(); Navigator.of(context).maybePop(); }, ); diff --git a/lib/presentation/widgets/buttons/haptic_close_button.dart b/lib/presentation/widgets/buttons/haptic_close_button.dart index bbd8455..f9e2f8b 100644 --- a/lib/presentation/widgets/buttons/haptic_close_button.dart +++ b/lib/presentation/widgets/buttons/haptic_close_button.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.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 { const HapticCloseButton({super.key}); @@ -13,10 +13,9 @@ class HapticCloseButton extends StatelessWidget { _ => Icons.close_rounded, }; - return IconButton( + return HapticIconButton( icon: Icon(iconData), onPressed: () async { - await HapticFeedback.mediumImpact(); Navigator.of(context).maybePop(); }, ); diff --git a/lib/presentation/widgets/buttons/haptic_icon_button.dart b/lib/presentation/widgets/buttons/haptic_icon_button.dart new file mode 100644 index 0000000..8da8e9f --- /dev/null +++ b/lib/presentation/widgets/buttons/haptic_icon_button.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class HapticIconButton extends StatelessWidget { + 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, + 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(); + }, + ); + } +} diff --git a/lib/presentation/widgets/buttons/main_menu_button.dart b/lib/presentation/widgets/buttons/main_menu_button.dart index c5c7a34..708c0f4 100644 --- a/lib/presentation/widgets/buttons/main_menu_button.dart +++ b/lib/presentation/widgets/buttons/main_menu_button.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; class MainMenuButton extends StatefulWidget { /// A button for the main menu with an optional icon and a press animation. @@ -78,6 +79,7 @@ class _MainMenuButtonState extends State onTapUp: (_) async { _cancelTimers(); if (mounted && !_isLongPressing) { + await HapticFeedback.selectionClick(); widget.onPressed(); } _isLongPressing = false; diff --git a/lib/presentation/widgets/dialog/custom_dialog_action.dart b/lib/presentation/widgets/dialog/custom_dialog_action.dart index 47024dc..26dc40d 100644 --- a/lib/presentation/widgets/dialog/custom_dialog_action.dart +++ b/lib/presentation/widgets/dialog/custom_dialog_action.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart'; @@ -26,7 +27,10 @@ class CustomDialogAction extends StatelessWidget { @override Widget build(BuildContext context) { return AnimatedDialogButton( - onPressed: onPressed, + onPressed: () async { + await HapticFeedback.selectionClick(); + onPressed.call(); + }, buttonText: text, buttonType: buttonType, isDescructive: isDestructive, diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index b13e098..00d6c11 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -144,7 +144,8 @@ class _PlayerSelectionState extends State { text: player.name, suffixText: getNameCountText(player), onIconTap: () { - setState(() { + setState(() async { + await HapticFeedback.selectionClick(); // Removes the player from the selection and notifies the parent. selectedPlayers.remove(player); widget.onChanged([...selectedPlayers]); diff --git a/lib/presentation/widgets/tiles/choose_tile.dart b/lib/presentation/widgets/tiles/choose_tile.dart index 41cc7f0..f69aefc 100644 --- a/lib/presentation/widgets/tiles/choose_tile.dart +++ b/lib/presentation/widgets/tiles/choose_tile.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:tallee/core/custom_theme.dart'; class ChooseTile extends StatefulWidget { @@ -30,7 +31,12 @@ class _ChooseTileState extends State { @override Widget build(BuildContext context) { return GestureDetector( - onTap: widget.onPressed, + onTap: () async { + await HapticFeedback.vibrate(); + if (widget.onPressed != null) { + widget.onPressed!.call(); + } + }, child: Container( margin: CustomTheme.tileMargin, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), diff --git a/lib/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart b/lib/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart index 77c9242..bb6c933 100644 --- a/lib/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart +++ b/lib/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:tallee/core/custom_theme.dart'; class CustomCheckboxListTile extends StatelessWidget { @@ -16,7 +17,10 @@ class CustomCheckboxListTile extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onTap: () => onChanged(!value), + onTap: () async { + await HapticFeedback.selectionClick(); + onChanged(!value); + }, child: Container( margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), padding: const EdgeInsets.symmetric(horizontal: 2), @@ -29,7 +33,8 @@ class CustomCheckboxListTile extends StatelessWidget { children: [ Checkbox( value: value, - onChanged: (bool? v) { + onChanged: (bool? v) async { + await HapticFeedback.selectionClick(); if (v == null) return; onChanged(v); }, diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index 018c896..6a81dc3 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -1,6 +1,7 @@ import 'dart:core' hide Match; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; @@ -51,7 +52,10 @@ class _MatchTileState extends State { final loc = AppLocalizations.of(context); return GestureDetector( - onTap: widget.onTap, + onTap: () async { + await HapticFeedback.selectionClick(); + widget.onTap.call(); + }, child: Container( margin: EdgeInsets.zero, width: widget.width,