mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
feat: add haptic feedback (#1459)
Co-authored-by: Ushie <ushiekane@gmail.com> Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
This commit is contained in:
parent
ef9b1d5c2d
commit
7911459817
@ -1,6 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
|
@ -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(
|
||||
|
@ -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<AppSelectorView> {
|
||||
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: () {
|
||||
|
@ -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(
|
||||
|
@ -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'
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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<PatchesSelectorView> {
|
||||
builder: (context, model, child) => Scaffold(
|
||||
floatingActionButton: Visibility(
|
||||
visible: model.patches.isNotEmpty,
|
||||
child: FloatingActionButton.extended(
|
||||
child: HapticFloatingActionButtonExtended(
|
||||
label: Row(
|
||||
children: <Widget>[
|
||||
I18nText('patchesSelectorView.doneButton'),
|
||||
|
@ -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<SUpdateThemeUI> {
|
||||
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<SUpdateThemeUI> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
RadioListTile(
|
||||
HapticRadioListTile(
|
||||
title: I18nText('settingsView.systemThemeLabel'),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
value: 0,
|
||||
@ -138,7 +140,7 @@ class _SUpdateThemeUIState extends State<SUpdateThemeUI> {
|
||||
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<SUpdateThemeUI> {
|
||||
newTheme.value = value!;
|
||||
},
|
||||
),
|
||||
RadioListTile(
|
||||
HapticRadioListTile(
|
||||
title: I18nText('settingsView.darkThemeLabel'),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
value: 2,
|
||||
|
@ -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<PatchItem> {
|
||||
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<PatchItem> {
|
||||
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,
|
||||
|
@ -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<SAutoUpdatePatches> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile(
|
||||
return HapticSwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
title: I18nText(
|
||||
'settingsView.autoUpdatePatchesLabel',
|
||||
|
@ -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<SEnablePatchesSelection> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile(
|
||||
return HapticSwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
title: I18nText(
|
||||
'settingsView.enablePatchesSelectionLabel',
|
||||
|
@ -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<SRequireSuggestedAppVersion> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile(
|
||||
return HapticSwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
title: I18nText(
|
||||
'settingsView.requireSuggestedAppVersionLabel',
|
||||
|
@ -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<SUniversalPatches> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile(
|
||||
return HapticSwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
title: I18nText(
|
||||
'settingsView.universalPatchesLabel',
|
||||
|
@ -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<SVersionCompatibilityCheck> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchListTile(
|
||||
return HapticSwitchListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
title: I18nText(
|
||||
'settingsView.versionCompatibilityCheckLabel',
|
||||
|
32
lib/ui/widgets/shared/haptics/haptic_checkbox.dart
Normal file
32
lib/ui/widgets/shared/haptics/haptic_checkbox.dart
Normal file
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
32
lib/ui/widgets/shared/haptics/haptic_checkbox_list_tile.dart
Normal file
32
lib/ui/widgets/shared/haptics/haptic_checkbox_list_tile.dart
Normal file
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
33
lib/ui/widgets/shared/haptics/haptic_custom_card.dart
Normal file
33
lib/ui/widgets/shared/haptics/haptic_custom_card.dart
Normal file
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
38
lib/ui/widgets/shared/haptics/haptic_radio_list_tile.dart
Normal file
38
lib/ui/widgets/shared/haptics/haptic_radio_list_tile.dart
Normal file
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
36
lib/ui/widgets/shared/haptics/haptic_switch_list_tile.dart
Normal file
36
lib/ui/widgets/shared/haptics/haptic_switch_list_tile.dart
Normal file
@ -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),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user