From 79114598173c3059ad3a1d9cc33708e095747d12 Mon Sep 17 00:00:00 2001 From: Benjamin <73490201+BenjaminHalko@users.noreply.github.com> Date: Sun, 14 Jan 2024 14:29:24 -0800 Subject: [PATCH] feat: add haptic feedback (#1459) Co-authored-by: Ushie Co-authored-by: Pun Butrach --- android/app/src/main/AndroidManifest.xml | 3 +- lib/services/manager_api.dart | 3 +- .../views/app_selector/app_selector_view.dart | 3 +- lib/ui/views/home/home_viewmodel.dart | 3 +- lib/ui/views/installer/installer_view.dart | 3 +- .../patch_options/patch_options_view.dart | 3 +- lib/ui/views/patcher/patcher_view.dart | 3 +- .../patches_selector_view.dart | 3 +- .../settings_update_theme.dart | 10 +++-- .../patchesSelectorView/patch_item.dart | 6 ++- .../settings_auto_update_patches.dart | 3 +- .../settings_enable_patches_selection.dart | 3 +- ...ettings_require_suggested_app_version.dart | 3 +- .../settings_universal_patches.dart | 3 +- .../settings_version_compatibility_check.dart | 3 +- .../shared/haptics/haptic_checkbox.dart | 32 ++++++++++++++++ .../haptics/haptic_checkbox_list_tile.dart | 32 ++++++++++++++++ .../shared/haptics/haptic_custom_card.dart | 33 ++++++++++++++++ ...aptic_floating_action_button_extended.dart | 29 ++++++++++++++ .../haptics/haptic_radio_list_tile.dart | 38 +++++++++++++++++++ .../haptics/haptic_switch_list_tile.dart | 36 ++++++++++++++++++ 21 files changed, 236 insertions(+), 19 deletions(-) create mode 100644 lib/ui/widgets/shared/haptics/haptic_checkbox.dart create mode 100644 lib/ui/widgets/shared/haptics/haptic_checkbox_list_tile.dart create mode 100644 lib/ui/widgets/shared/haptics/haptic_custom_card.dart create mode 100644 lib/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart create mode 100644 lib/ui/widgets/shared/haptics/haptic_radio_list_tile.dart create mode 100644 lib/ui/widgets/shared/haptics/haptic_switch_list_tile.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 782ef780c7..4d80807552 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ - + + diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index 1ccfadab30..1e9b4606da 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -16,6 +16,7 @@ import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/services/root_api.dart'; import 'package:revanced_manager/services/toast.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_checkbox_list_tile.dart'; import 'package:revanced_manager/utils/check_for_supported_patch.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:timeago/timeago.dart'; @@ -617,7 +618,7 @@ class ManagerAPI { ), ), const SizedBox(height: 8), - CheckboxListTile( + HapticCheckboxListTile( value: value, contentPadding: EdgeInsets.zero, title: I18nText( diff --git a/lib/ui/views/app_selector/app_selector_view.dart b/lib/ui/views/app_selector/app_selector_view.dart index a4c6014970..ef679b0c91 100644 --- a/lib/ui/views/app_selector/app_selector_view.dart +++ b/lib/ui/views/app_selector/app_selector_view.dart @@ -4,6 +4,7 @@ import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.da import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart'; import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart'; import 'package:revanced_manager/ui/widgets/appSelectorView/not_installed_app_item.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart'; import 'package:revanced_manager/ui/widgets/shared/search_bar.dart'; import 'package:stacked/stacked.dart' hide SkeletonLoader; @@ -23,7 +24,7 @@ class _AppSelectorViewState extends State { onViewModelReady: (model) => model.initialize(), viewModelBuilder: () => AppSelectorViewModel(), builder: (context, model, child) => Scaffold( - floatingActionButton: FloatingActionButton.extended( + floatingActionButton: HapticFloatingActionButtonExtended( label: I18nText('appSelectorView.storageButton'), icon: const Icon(Icons.sd_storage), onPressed: () { diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index 6913e409e1..242305af3c 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -20,6 +20,7 @@ import 'package:revanced_manager/services/toast.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/homeView/update_confirmation_dialog.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_checkbox_list_tile.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; @@ -207,7 +208,7 @@ class HomeViewModel extends BaseViewModel { ), ), ), - CheckboxListTile( + HapticCheckboxListTile( value: value, contentPadding: EdgeInsets.zero, title: I18nText( diff --git a/lib/ui/views/installer/installer_view.dart b/lib/ui/views/installer/installer_view.dart index 589021bfb6..b78b836c64 100644 --- a/lib/ui/views/installer/installer_view.dart +++ b/lib/ui/views/installer/installer_view.dart @@ -5,6 +5,7 @@ import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/installerView/gradient_progress_indicator.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart'; import 'package:stacked/stacked.dart'; class InstallerView extends StatelessWidget { @@ -22,7 +23,7 @@ class InstallerView extends StatelessWidget { child: Scaffold( floatingActionButton: Visibility( visible: !model.isPatching && !model.hasErrors, - child: FloatingActionButton.extended( + child: HapticFloatingActionButtonExtended( label: I18nText( model.isInstalled ? 'installerView.openButton' diff --git a/lib/ui/views/patch_options/patch_options_view.dart b/lib/ui/views/patch_options/patch_options_view.dart index e6ac1bb280..74f56657de 100644 --- a/lib/ui/views/patch_options/patch_options_view.dart +++ b/lib/ui/views/patch_options/patch_options_view.dart @@ -4,6 +4,7 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/ui/views/patch_options/patch_options_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/patchesSelectorView/patch_options_fields.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart'; import 'package:stacked/stacked.dart'; class PatchOptionsView extends StatelessWidget { @@ -17,7 +18,7 @@ class PatchOptionsView extends StatelessWidget { builder: (context, model, child) => GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: Scaffold( - floatingActionButton: FloatingActionButton.extended( + floatingActionButton: HapticFloatingActionButtonExtended( onPressed: () async { final bool saved = model.saveOptions(context); if (saved && context.mounted) { diff --git a/lib/ui/views/patcher/patcher_view.dart b/lib/ui/views/patcher/patcher_view.dart index 9def6c05a0..fb0592c008 100644 --- a/lib/ui/views/patcher/patcher_view.dart +++ b/lib/ui/views/patcher/patcher_view.dart @@ -6,6 +6,7 @@ import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/patcherView/app_selector_card.dart'; import 'package:revanced_manager/ui/widgets/patcherView/patch_selector_card.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart'; import 'package:stacked/stacked.dart'; class PatcherView extends StatelessWidget { @@ -19,7 +20,7 @@ class PatcherView extends StatelessWidget { builder: (context, model, child) => Scaffold( floatingActionButton: Visibility( visible: model.showPatchButton(), - child: FloatingActionButton.extended( + child: HapticFloatingActionButtonExtended( label: I18nText('patcherView.patchButton'), icon: const Icon(Icons.build), onPressed: () async { diff --git a/lib/ui/views/patches_selector/patches_selector_view.dart b/lib/ui/views/patches_selector/patches_selector_view.dart index 71e90d7962..3aa1da241f 100644 --- a/lib/ui/views/patches_selector/patches_selector_view.dart +++ b/lib/ui/views/patches_selector/patches_selector_view.dart @@ -3,6 +3,7 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart'; import 'package:revanced_manager/ui/widgets/shared/search_bar.dart'; import 'package:stacked/stacked.dart'; @@ -36,7 +37,7 @@ class _PatchesSelectorViewState extends State { builder: (context, model, child) => Scaffold( floatingActionButton: Visibility( visible: model.patches.isNotEmpty, - child: FloatingActionButton.extended( + child: HapticFloatingActionButtonExtended( label: Row( children: [ I18nText('patchesSelectorView.doneButton'), diff --git a/lib/ui/views/settings/settingsFragment/settings_update_theme.dart b/lib/ui/views/settings/settingsFragment/settings_update_theme.dart index 09c1b28b19..cdd6c540bb 100644 --- a/lib/ui/views/settings/settingsFragment/settings_update_theme.dart +++ b/lib/ui/views/settings/settingsFragment/settings_update_theme.dart @@ -7,6 +7,8 @@ import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_radio_list_tile.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_switch_list_tile.dart'; class SUpdateThemeUI extends StatefulWidget { const SUpdateThemeUI({super.key}); @@ -42,7 +44,7 @@ class _SUpdateThemeUIState extends State { onTap: () => {showThemeDialog(context)}, ), if (managerAPI.isDynamicThemeAvailable) - SwitchListTile( + HapticSwitchListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), title: I18nText( 'settingsView.dynamicThemeLabel', @@ -129,7 +131,7 @@ class _SUpdateThemeUIState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - RadioListTile( + HapticRadioListTile( title: I18nText('settingsView.systemThemeLabel'), contentPadding: const EdgeInsets.symmetric(horizontal: 16), value: 0, @@ -138,7 +140,7 @@ class _SUpdateThemeUIState extends State { newTheme.value = value!; }, ), - RadioListTile( + HapticRadioListTile( title: I18nText('settingsView.lightThemeLabel'), contentPadding: const EdgeInsets.symmetric(horizontal: 16), value: 1, @@ -147,7 +149,7 @@ class _SUpdateThemeUIState extends State { newTheme.value = value!; }, ), - RadioListTile( + HapticRadioListTile( title: I18nText('settingsView.darkThemeLabel'), contentPadding: const EdgeInsets.symmetric(horizontal: 16), value: 2, diff --git a/lib/ui/widgets/patchesSelectorView/patch_item.dart b/lib/ui/widgets/patchesSelectorView/patch_item.dart index 70692ab2e8..233455118c 100644 --- a/lib/ui/widgets/patchesSelectorView/patch_item.dart +++ b/lib/ui/widgets/patchesSelectorView/patch_item.dart @@ -5,6 +5,8 @@ import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/toast.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_checkbox.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_custom_card.dart'; // ignore: must_be_immutable class PatchItem extends StatefulWidget { @@ -56,7 +58,7 @@ class _PatchItemState extends State { widget._managerAPI.isVersionCompatibilityCheckEnabled() == true ? 0.5 : 1, - child: CustomCard( + child: HapticCustomCard( padding: EdgeInsets.only( top: 12, bottom: 16, @@ -88,7 +90,7 @@ class _PatchItemState extends State { children: [ Transform.scale( scale: 1.2, - child: Checkbox( + child: HapticCheckbox( value: widget.isSelected, activeColor: Theme.of(context).colorScheme.primary, checkColor: Theme.of(context).colorScheme.secondaryContainer, diff --git a/lib/ui/widgets/settingsView/settings_auto_update_patches.dart b/lib/ui/widgets/settingsView/settings_auto_update_patches.dart index 2063d658e6..14c5126cfe 100644 --- a/lib/ui/widgets/settingsView/settings_auto_update_patches.dart +++ b/lib/ui/widgets/settingsView/settings_auto_update_patches.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_switch_list_tile.dart'; class SAutoUpdatePatches extends StatefulWidget { const SAutoUpdatePatches({super.key}); @@ -14,7 +15,7 @@ final _settingsViewModel = SettingsViewModel(); class _SAutoUpdatePatchesState extends State { @override Widget build(BuildContext context) { - return SwitchListTile( + return HapticSwitchListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), title: I18nText( 'settingsView.autoUpdatePatchesLabel', diff --git a/lib/ui/widgets/settingsView/settings_enable_patches_selection.dart b/lib/ui/widgets/settingsView/settings_enable_patches_selection.dart index a2fcc86b88..c8384044f2 100644 --- a/lib/ui/widgets/settingsView/settings_enable_patches_selection.dart +++ b/lib/ui/widgets/settingsView/settings_enable_patches_selection.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_switch_list_tile.dart'; class SEnablePatchesSelection extends StatefulWidget { const SEnablePatchesSelection({super.key}); @@ -15,7 +16,7 @@ final _settingsViewModel = SettingsViewModel(); class _SEnablePatchesSelectionState extends State { @override Widget build(BuildContext context) { - return SwitchListTile( + return HapticSwitchListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), title: I18nText( 'settingsView.enablePatchesSelectionLabel', diff --git a/lib/ui/widgets/settingsView/settings_require_suggested_app_version.dart b/lib/ui/widgets/settingsView/settings_require_suggested_app_version.dart index da583a9715..9e91b9cb83 100644 --- a/lib/ui/widgets/settingsView/settings_require_suggested_app_version.dart +++ b/lib/ui/widgets/settingsView/settings_require_suggested_app_version.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_switch_list_tile.dart'; class SRequireSuggestedAppVersion extends StatefulWidget { const SRequireSuggestedAppVersion({super.key}); @@ -16,7 +17,7 @@ class _SRequireSuggestedAppVersionState extends State { @override Widget build(BuildContext context) { - return SwitchListTile( + return HapticSwitchListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), title: I18nText( 'settingsView.requireSuggestedAppVersionLabel', diff --git a/lib/ui/widgets/settingsView/settings_universal_patches.dart b/lib/ui/widgets/settingsView/settings_universal_patches.dart index 1add0ddf33..98aefa9393 100644 --- a/lib/ui/widgets/settingsView/settings_universal_patches.dart +++ b/lib/ui/widgets/settingsView/settings_universal_patches.dart @@ -3,6 +3,7 @@ import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_switch_list_tile.dart'; class SUniversalPatches extends StatefulWidget { const SUniversalPatches({super.key}); @@ -18,7 +19,7 @@ final _patcherViewModel = PatcherViewModel(); class _SUniversalPatchesState extends State { @override Widget build(BuildContext context) { - return SwitchListTile( + return HapticSwitchListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), title: I18nText( 'settingsView.universalPatchesLabel', diff --git a/lib/ui/widgets/settingsView/settings_version_compatibility_check.dart b/lib/ui/widgets/settingsView/settings_version_compatibility_check.dart index a17fcb3b40..7f90bac256 100644 --- a/lib/ui/widgets/settingsView/settings_version_compatibility_check.dart +++ b/lib/ui/widgets/settingsView/settings_version_compatibility_check.dart @@ -3,6 +3,7 @@ import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; +import 'package:revanced_manager/ui/widgets/shared/haptics/haptic_switch_list_tile.dart'; import 'package:revanced_manager/utils/check_for_supported_patch.dart'; class SVersionCompatibilityCheck extends StatefulWidget { @@ -21,7 +22,7 @@ class _SVersionCompatibilityCheckState extends State { @override Widget build(BuildContext context) { - return SwitchListTile( + return HapticSwitchListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), title: I18nText( 'settingsView.versionCompatibilityCheckLabel', diff --git a/lib/ui/widgets/shared/haptics/haptic_checkbox.dart b/lib/ui/widgets/shared/haptics/haptic_checkbox.dart new file mode 100644 index 0000000000..5a16a8801b --- /dev/null +++ b/lib/ui/widgets/shared/haptics/haptic_checkbox.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class HapticCheckbox extends StatelessWidget { + const HapticCheckbox({ + super.key, + required this.value, + required this.onChanged, + this.activeColor, + this.checkColor, + this.side, + }); + final bool value; + final Function(bool?)? onChanged; + final Color? activeColor; + final Color? checkColor; + final BorderSide? side; + + @override + Widget build(BuildContext context) { + return Checkbox( + value: value, + onChanged: (value) => { + HapticFeedback.selectionClick(), + if (onChanged != null) onChanged!(value), + }, + activeColor: activeColor, + checkColor: checkColor, + side: side, + ); + } +} diff --git a/lib/ui/widgets/shared/haptics/haptic_checkbox_list_tile.dart b/lib/ui/widgets/shared/haptics/haptic_checkbox_list_tile.dart new file mode 100644 index 0000000000..11b8e878ad --- /dev/null +++ b/lib/ui/widgets/shared/haptics/haptic_checkbox_list_tile.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class HapticCheckboxListTile extends StatelessWidget { + const HapticCheckboxListTile({ + super.key, + required this.value, + required this.onChanged, + this.title, + this.subtitle, + this.contentPadding, + }); + final bool value; + final Function(bool?)? onChanged; + final Widget? title; + final Widget? subtitle; + final EdgeInsetsGeometry? contentPadding; + + @override + Widget build(BuildContext context) { + return CheckboxListTile( + contentPadding: contentPadding ?? EdgeInsets.zero, + title: title, + subtitle: subtitle, + value: value, + onChanged: (value) => { + HapticFeedback.lightImpact(), + if (onChanged != null) onChanged!(value), + }, + ); + } +} diff --git a/lib/ui/widgets/shared/haptics/haptic_custom_card.dart b/lib/ui/widgets/shared/haptics/haptic_custom_card.dart new file mode 100644 index 0000000000..fa889329ea --- /dev/null +++ b/lib/ui/widgets/shared/haptics/haptic_custom_card.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; + +class HapticCustomCard extends StatelessWidget { + const HapticCustomCard({ + super.key, + this.isFilled = true, + required this.child, + this.onTap, + this.padding, + this.backgroundColor, + }); + final bool isFilled; + final Widget child; + final Function()? onTap; + final EdgeInsetsGeometry? padding; + final Color? backgroundColor; + + @override + Widget build(BuildContext context) { + return CustomCard( + isFilled: isFilled, + onTap: () => { + HapticFeedback.selectionClick(), + if (onTap != null) onTap!(), + }, + padding: padding, + backgroundColor: backgroundColor, + child: child, + ); + } +} diff --git a/lib/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart b/lib/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart new file mode 100644 index 0000000000..c930ace629 --- /dev/null +++ b/lib/ui/widgets/shared/haptics/haptic_floating_action_button_extended.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class HapticFloatingActionButtonExtended extends StatelessWidget { + const HapticFloatingActionButtonExtended({ + super.key, + required this.onPressed, + required this.label, + this.icon, + this.elevation, + }); + final Function()? onPressed; + final Widget label; + final Widget? icon; + final double? elevation; + + @override + Widget build(BuildContext context) { + return FloatingActionButton.extended( + onPressed: () => { + HapticFeedback.lightImpact(), + if (onPressed != null) onPressed!(), + }, + label: label, + icon: icon, + elevation: elevation, + ); + } +} diff --git a/lib/ui/widgets/shared/haptics/haptic_radio_list_tile.dart b/lib/ui/widgets/shared/haptics/haptic_radio_list_tile.dart new file mode 100644 index 0000000000..6c5a799cba --- /dev/null +++ b/lib/ui/widgets/shared/haptics/haptic_radio_list_tile.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class HapticRadioListTile extends StatelessWidget { + const HapticRadioListTile({ + super.key, + required this.title, + required this.value, + required this.groupValue, + this.subtitle, + this.onChanged, + this.contentPadding, + }); + final Widget title; + final Widget? subtitle; + final int value; + final Function(int?)? onChanged; + final int groupValue; + final EdgeInsetsGeometry? contentPadding; + + @override + Widget build(BuildContext context) { + return RadioListTile( + contentPadding: contentPadding ?? EdgeInsets.zero, + title: title, + subtitle: subtitle, + value: value, + groupValue: groupValue, + onChanged: (val) => { + if (val == value) { + HapticFeedback.lightImpact(), + }, + + if (onChanged != null) onChanged!(val), + }, + ); + } +} diff --git a/lib/ui/widgets/shared/haptics/haptic_switch_list_tile.dart b/lib/ui/widgets/shared/haptics/haptic_switch_list_tile.dart new file mode 100644 index 0000000000..8c8cb8d5d5 --- /dev/null +++ b/lib/ui/widgets/shared/haptics/haptic_switch_list_tile.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class HapticSwitchListTile extends StatelessWidget { + const HapticSwitchListTile({ + super.key, + required this.value, + required this.onChanged, + this.title, + this.subtitle, + this.contentPadding, + }); + final bool value; + final Function(bool)? onChanged; + final Widget? title; + final Widget? subtitle; + final EdgeInsetsGeometry? contentPadding; + + @override + Widget build(BuildContext context) { + return SwitchListTile( + contentPadding: contentPadding ?? EdgeInsets.zero, + title: title, + subtitle: subtitle, + value: value, + onChanged: (value) => { + if (value) { + HapticFeedback.mediumImpact(), + } else { + HapticFeedback.lightImpact(), + }, + if (onChanged != null) onChanged!(value), + }, + ); + } +}