feat: add haptic feedback for various user interactions
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 45s
Pull Request Pipeline / lint (pull_request) Failing after 48s

This commit is contained in:
2026-05-10 23:04:43 +02:00
parent 699d4378b2
commit 1d20127af4
13 changed files with 131 additions and 17 deletions

View File

@@ -0,0 +1,24 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class HapticBackButton extends StatelessWidget {
const HapticBackButton({super.key});
@override
Widget build(BuildContext context) {
final iconData = switch (defaultTargetPlatform) {
TargetPlatform.iOS ||
TargetPlatform.macOS => Icons.arrow_back_ios_new_rounded,
_ => Icons.arrow_back_rounded,
};
return IconButton(
icon: Icon(iconData),
onPressed: () async {
await HapticFeedback.mediumImpact();
Navigator.of(context).maybePop();
},
);
}
}

View File

@@ -0,0 +1,24 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class HapticCloseButton extends StatelessWidget {
const HapticCloseButton({super.key});
@override
Widget build(BuildContext context) {
final iconData = switch (defaultTargetPlatform) {
TargetPlatform.iOS || TargetPlatform.macOS => CupertinoIcons.xmark,
_ => Icons.close_rounded,
};
return IconButton(
icon: Icon(iconData),
onPressed: () async {
await HapticFeedback.mediumImpact();
Navigator.of(context).maybePop();
},
);
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:tallee/core/common.dart';
import 'package:tallee/core/constants.dart';
@@ -197,7 +198,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
text: suggestedPlayers[index].name,
suffixText: getNameCountText(suggestedPlayers[index]),
icon: Icons.add,
onPressed: () {
onPressed: () async {
await HapticFeedback.selectionClick();
setState(() {
// If the player is not already selected
if (!selectedPlayers.contains(
@@ -294,8 +296,10 @@ class _PlayerSelectionState extends State<PlayerSelection> {
if (success) {
_handleSuccessfulPlayerCreation(createdPlayer);
await HapticFeedback.successNotification();
showSnackBarMessage(loc.successfully_added_player(playerName));
} else {
await HapticFeedback.errorNotification();
showSnackBarMessage(loc.could_not_add_player(playerName));
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/models/group.dart';
@@ -33,7 +34,10 @@ class _GroupTileState extends State<GroupTile> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
onTap: () async {
await HapticFeedback.selectionClick();
widget.onTap?.call();
},
child: AnimatedContainer(
margin: CustomTheme.standardMargin,
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/licenses/license_detail_view.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart';
@@ -15,8 +16,10 @@ class LicenseTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
onTap: () async {
final navigator = Navigator.of(context);
await HapticFeedback.selectionClick();
navigator.push(
MaterialPageRoute(
builder: (context) => LicenseDetailView(package: package),
),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart';
@@ -36,7 +37,10 @@ class SettingsListTile extends StatelessWidget {
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.95,
child: GestureDetector(
onTap: onPressed ?? () {},
onTap: () async {
await HapticFeedback.selectionClick();
onPressed?.call();
},
child: Container(
margin: EdgeInsets.zero,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),