added setting to disable double back to close tab

This commit is contained in:
Fabian Freund
2026-01-11 18:07:37 +01:00
parent 890d612522
commit edbc157c46
6 changed files with 91 additions and 28 deletions
@@ -569,6 +569,10 @@ class _Browser extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final doubleBackCloseTab = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.doubleBackCloseTab),
);
final lastBackButtonPress = useRef<DateTime?>(null); final lastBackButtonPress = useRef<DateTime?>(null);
final overlayBuilder = ref.watch(overlayControllerProvider); final overlayBuilder = ref.watch(overlayControllerProvider);
@@ -682,39 +686,44 @@ class _Browser extends HookConsumerWidget {
return false; return false;
} }
if (lastBackButtonPress.value != null && // Handle double back to close (if enabled)
DateTime.now().difference(lastBackButtonPress.value!) < if (doubleBackCloseTab) {
_backButtonPressTimeout) { if (lastBackButtonPress.value != null &&
lastBackButtonPress.value = null; DateTime.now().difference(lastBackButtonPress.value!) <
_backButtonPressTimeout) {
lastBackButtonPress.value = null;
if (tabState != null && tabCount > 1) { if (tabState != null && tabCount > 1) {
await ref await ref
.read(tabRepositoryProvider.notifier) .read(tabRepositoryProvider.notifier)
.closeTab(tabState.id); .closeTab(tabState.id);
if (context.mounted) { if (context.mounted) {
ui_helper.showTabUndoClose( ui_helper.showTabUndoClose(
context, context,
ref.read(tabRepositoryProvider.notifier).undoClose, ref.read(tabRepositoryProvider.notifier).undoClose,
); );
}
return true;
} else {
//Mark back as unhandled and navigator will pop
await SystemNavigator.pop();
return false;
} }
} else {
lastBackButtonPress.value = DateTime.now();
ui_helper.showTabBackButtonMessage(
context,
tabCount,
_backButtonPressTimeout,
);
return true; return true;
} else {
//Mark back as unhandled and navigator will pop
await SystemNavigator.pop();
return false;
} }
} else {
lastBackButtonPress.value = DateTime.now();
ui_helper.showTabBackButtonMessage(
context,
tabCount,
_backButtonPressTimeout,
);
return true;
} }
return true;
}, },
child: _BrowserView( child: _BrowserView(
isFullscreen: tabInFullScreen, isFullscreen: tabInFullScreen,
@@ -67,6 +67,7 @@ class GeneralSettingsScreen extends StatelessWidget {
_BottomSheetTabViewTile(), _BottomSheetTabViewTile(),
_TabBarSwipeBehaviorSection(), _TabBarSwipeBehaviorSection(),
_PullToRefreshTile(), _PullToRefreshTile(),
_DoubleBackCloseTabTile(),
_IconCacheTile(), _IconCacheTile(),
], ],
); );
@@ -867,6 +868,34 @@ class _PullToRefreshTile extends HookConsumerWidget {
} }
} }
class _DoubleBackCloseTabTile extends HookConsumerWidget {
const _DoubleBackCloseTabTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final doubleBackCloseTab = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.doubleBackCloseTab),
);
return SwitchListTile.adaptive(
title: const Text('Double Back to Close Tab'),
subtitle: const Text(
'When enabled, press back twice to close the tab. When disabled, back button only navigates page history.',
),
secondary: const Icon(MdiIcons.gestureDoubleTap),
value: doubleBackCloseTab,
onChanged: (value) async {
await ref
.read(saveGeneralSettingsControllerProvider.notifier)
.save(
(currentSettings) =>
currentSettings.copyWith.doubleBackCloseTab(value),
);
},
);
}
}
class _IconCacheTile extends HookConsumerWidget { class _IconCacheTile extends HookConsumerWidget {
const _IconCacheTile(); const _IconCacheTile();
@@ -81,6 +81,7 @@ class GeneralSettings with FastEquatable {
final TabBarPosition tabBarPosition; final TabBarPosition tabBarPosition;
final QuickTabSwitcherMode quickTabSwitcherMode; final QuickTabSwitcherMode quickTabSwitcherMode;
final bool pullToRefreshEnabled; final bool pullToRefreshEnabled;
final bool doubleBackCloseTab;
GeneralSettings({ GeneralSettings({
required this.themeMode, required this.themeMode,
@@ -104,6 +105,7 @@ class GeneralSettings with FastEquatable {
required this.tabBarPosition, required this.tabBarPosition,
required this.quickTabSwitcherMode, required this.quickTabSwitcherMode,
required this.pullToRefreshEnabled, required this.pullToRefreshEnabled,
required this.doubleBackCloseTab,
}); });
GeneralSettings.withDefaults({ GeneralSettings.withDefaults({
@@ -128,6 +130,7 @@ class GeneralSettings with FastEquatable {
TabBarPosition? tabBarPosition, TabBarPosition? tabBarPosition,
QuickTabSwitcherMode? quickTabSwitcherMode, QuickTabSwitcherMode? quickTabSwitcherMode,
bool? pullToRefreshEnabled, bool? pullToRefreshEnabled,
bool? doubleBackCloseTab,
}) : themeMode = themeMode ?? ThemeMode.dark, }) : themeMode = themeMode ?? ThemeMode.dark,
enableReadability = enableReadability ?? true, enableReadability = enableReadability ?? true,
enforceReadability = enforceReadability ?? false, enforceReadability = enforceReadability ?? false,
@@ -151,7 +154,8 @@ class GeneralSettings with FastEquatable {
tabBarPosition = tabBarPosition ?? TabBarPosition.bottom, tabBarPosition = tabBarPosition ?? TabBarPosition.bottom,
quickTabSwitcherMode = quickTabSwitcherMode =
quickTabSwitcherMode ?? QuickTabSwitcherMode.lastUsedTabs, quickTabSwitcherMode ?? QuickTabSwitcherMode.lastUsedTabs,
pullToRefreshEnabled = pullToRefreshEnabled ?? true; pullToRefreshEnabled = pullToRefreshEnabled ?? true,
doubleBackCloseTab = doubleBackCloseTab ?? true;
factory GeneralSettings.fromJson(Map<String, dynamic> json) => factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
_$GeneralSettingsFromJson(json); _$GeneralSettingsFromJson(json);
@@ -181,5 +185,6 @@ class GeneralSettings with FastEquatable {
tabBarPosition, tabBarPosition,
quickTabSwitcherMode, quickTabSwitcherMode,
pullToRefreshEnabled, pullToRefreshEnabled,
doubleBackCloseTab,
]; ];
} }
@@ -59,6 +59,8 @@ abstract class _$GeneralSettingsCWProxy {
GeneralSettings pullToRefreshEnabled(bool pullToRefreshEnabled); GeneralSettings pullToRefreshEnabled(bool pullToRefreshEnabled);
GeneralSettings doubleBackCloseTab(bool doubleBackCloseTab);
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`.
/// ///
@@ -88,6 +90,7 @@ abstract class _$GeneralSettingsCWProxy {
TabBarPosition tabBarPosition, TabBarPosition tabBarPosition,
QuickTabSwitcherMode quickTabSwitcherMode, QuickTabSwitcherMode quickTabSwitcherMode,
bool pullToRefreshEnabled, bool pullToRefreshEnabled,
bool doubleBackCloseTab,
}); });
} }
@@ -186,6 +189,10 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
GeneralSettings pullToRefreshEnabled(bool pullToRefreshEnabled) => GeneralSettings pullToRefreshEnabled(bool pullToRefreshEnabled) =>
call(pullToRefreshEnabled: pullToRefreshEnabled); call(pullToRefreshEnabled: pullToRefreshEnabled);
@override
GeneralSettings doubleBackCloseTab(bool doubleBackCloseTab) =>
call(doubleBackCloseTab: doubleBackCloseTab);
@override @override
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`.
@@ -216,6 +223,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
Object? tabBarPosition = const $CopyWithPlaceholder(), Object? tabBarPosition = const $CopyWithPlaceholder(),
Object? quickTabSwitcherMode = const $CopyWithPlaceholder(), Object? quickTabSwitcherMode = const $CopyWithPlaceholder(),
Object? pullToRefreshEnabled = const $CopyWithPlaceholder(), Object? pullToRefreshEnabled = const $CopyWithPlaceholder(),
Object? doubleBackCloseTab = const $CopyWithPlaceholder(),
}) { }) {
return GeneralSettings( return GeneralSettings(
themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null
@@ -340,6 +348,12 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
? _value.pullToRefreshEnabled ? _value.pullToRefreshEnabled
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: pullToRefreshEnabled as bool, : pullToRefreshEnabled as bool,
doubleBackCloseTab:
doubleBackCloseTab == const $CopyWithPlaceholder() ||
doubleBackCloseTab == null
? _value.doubleBackCloseTab
// ignore: cast_nullable_to_non_nullable
: doubleBackCloseTab as bool,
); );
} }
} }
@@ -405,6 +419,7 @@ GeneralSettings _$GeneralSettingsFromJson(
json['quickTabSwitcherMode'], json['quickTabSwitcherMode'],
), ),
pullToRefreshEnabled: json['pullToRefreshEnabled'] as bool?, pullToRefreshEnabled: json['pullToRefreshEnabled'] as bool?,
doubleBackCloseTab: json['doubleBackCloseTab'] as bool?,
); );
Map<String, dynamic> _$GeneralSettingsToJson( Map<String, dynamic> _$GeneralSettingsToJson(
@@ -439,6 +454,7 @@ Map<String, dynamic> _$GeneralSettingsToJson(
'quickTabSwitcherMode': 'quickTabSwitcherMode':
_$QuickTabSwitcherModeEnumMap[instance.quickTabSwitcherMode]!, _$QuickTabSwitcherModeEnumMap[instance.quickTabSwitcherMode]!,
'pullToRefreshEnabled': instance.pullToRefreshEnabled, 'pullToRefreshEnabled': instance.pullToRefreshEnabled,
'doubleBackCloseTab': instance.doubleBackCloseTab,
}; };
const _$ThemeModeEnumMap = { const _$ThemeModeEnumMap = {
@@ -126,6 +126,10 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
DriftSqlType.bool, DriftSqlType.bool,
db.typeMapping, db.typeMapping,
), ),
'doubleBackCloseTab': settings['doubleBackCloseTab']?.readAs(
DriftSqlType.bool,
db.typeMapping,
),
}); });
} }
@@ -35,7 +35,7 @@ final class GeneralSettingsRepositoryProvider
} }
String _$generalSettingsRepositoryHash() => String _$generalSettingsRepositoryHash() =>
r'dceccac3a93e96907c7bd2b4c58f5021912553ac'; r'ada58479ceea436f08e22780be200c99e3abedb7';
abstract class _$GeneralSettingsRepository abstract class _$GeneralSettingsRepository
extends $StreamNotifier<GeneralSettings> { extends $StreamNotifier<GeneralSettings> {