feat: add haptic feedback to more user interactions
This commit is contained in:
@@ -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<CustomNavigationBar>
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
scrolledUnderElevation: 0,
|
||||
actions: [
|
||||
IconButton(
|
||||
HapticIconButton(
|
||||
onPressed: () async {
|
||||
final navigator = Navigator.of(context);
|
||||
await HapticFeedback.selectionClick();
|
||||
|
||||
@@ -135,9 +135,13 @@ class _CreateGroupViewState extends State<CreateGroupView> {
|
||||
|
||||
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
|
||||
|
||||
@@ -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<GroupDetailView> {
|
||||
appBar: AppBar(
|
||||
title: Text(loc.group_profile),
|
||||
actions: [
|
||||
IconButton(
|
||||
HapticIconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => CustomAlertDialog(
|
||||
@@ -77,17 +76,11 @@ class _GroupDetailViewState extends State<GroupDetailView> {
|
||||
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,
|
||||
),
|
||||
|
||||
@@ -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<GroupView> {
|
||||
text: loc.create_group,
|
||||
icon: Icons.group_add,
|
||||
onPressed: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
await Navigator.push(
|
||||
context,
|
||||
adaptivePageRoute(
|
||||
|
||||
@@ -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<ChooseGameView> {
|
||||
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<ChooseGameView> {
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
HapticIconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () async {
|
||||
final result = await Navigator.push(
|
||||
|
||||
@@ -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<ChooseGroupView> {
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
leading: HapticIconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(
|
||||
|
||||
@@ -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<CreateGameView> {
|
||||
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;
|
||||
|
||||
@@ -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<MatchDetailView> {
|
||||
appBar: AppBar(
|
||||
title: Text(loc.match_profile),
|
||||
actions: [
|
||||
IconButton(
|
||||
HapticIconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
showDialog<bool>(
|
||||
|
||||
@@ -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<MatchResultView> {
|
||||
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<MatchResultView> {
|
||||
: RadioGroup<Player>(
|
||||
groupValue: _selectedPlayer,
|
||||
onChanged: (Player? value) async {
|
||||
await HapticFeedback.selectionClick();
|
||||
setState(() {
|
||||
_selectedPlayer = value;
|
||||
});
|
||||
@@ -217,6 +220,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
||||
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<MatchResultView> {
|
||||
},
|
||||
);
|
||||
},
|
||||
onReorderStart: (int n) async {
|
||||
await HapticFeedback.selectionClick();
|
||||
},
|
||||
onReorderEnd: (int n) async {
|
||||
await HapticFeedback.selectionClick();
|
||||
},
|
||||
onReorder: (int oldIndex, int newIndex) {
|
||||
setState(() {
|
||||
if (newIndex > oldIndex) {
|
||||
|
||||
@@ -276,35 +276,38 @@ class _SettingsViewState extends State<SettingsView> {
|
||||
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<SettingsView> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
);
|
||||
|
||||
54
lib/presentation/widgets/buttons/haptic_icon_button.dart
Normal file
54
lib/presentation/widgets/buttons/haptic_icon_button.dart
Normal file
@@ -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();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<MainMenuButton>
|
||||
onTapUp: (_) async {
|
||||
_cancelTimers();
|
||||
if (mounted && !_isLongPressing) {
|
||||
await HapticFeedback.selectionClick();
|
||||
widget.onPressed();
|
||||
}
|
||||
_isLongPressing = false;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -144,7 +144,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
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]);
|
||||
|
||||
@@ -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<ChooseTile> {
|
||||
@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),
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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<MatchTile> {
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user