From d17c6e542fc95fc4a4be42176508ed40d9b8bdd6 Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Thu, 5 Feb 2026 16:40:36 +0100 Subject: [PATCH] improve tabbar dismiss logic; fix #157 --- .../controllers/tab_bar_dismissable.dart | 2 +- .../controllers/tab_bar_dismissable.g.dart | 4 +- .../browser/presentation/screens/browser.dart | 5 +++ .../browser_modules/bottom_app_bar.dart | 39 ++++++++++++------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/app/lib/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.dart b/app/lib/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.dart index bb7c3bbb..46226f58 100644 --- a/app/lib/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.dart +++ b/app/lib/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.dart @@ -21,7 +21,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'tab_bar_dismissable.g.dart'; -@Riverpod() +@Riverpod(keepAlive: true) class TabBarDismissableController extends _$TabBarDismissableController { void show() { state = false; diff --git a/app/lib/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.g.dart b/app/lib/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.g.dart index 1fce1a29..02600525 100644 --- a/app/lib/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.g.dart +++ b/app/lib/features/geckoview/features/browser/presentation/controllers/tab_bar_dismissable.g.dart @@ -21,7 +21,7 @@ final class TabBarDismissableControllerProvider argument: null, retry: null, name: r'tabBarDismissableControllerProvider', - isAutoDispose: true, + isAutoDispose: false, dependencies: null, $allTransitiveDependencies: null, ); @@ -43,7 +43,7 @@ final class TabBarDismissableControllerProvider } String _$tabBarDismissableControllerHash() => - r'2bb0055f5525a21982ba3d7cb6443bcad823b64f'; + r'e0c2f700ccebc35bc8029370cdebd63e98bf95e1'; abstract class _$TabBarDismissableController extends $Notifier { bool build(); diff --git a/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart b/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart index 22d41b3d..44811a95 100644 --- a/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart +++ b/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart @@ -211,6 +211,11 @@ class _TabBar extends HookConsumerWidget { if (!ref.read(generalSettingsWithDefaultsProvider).autoHideTabBar) { return; } + // Don't apply auto-hide when toolbar is manually dismissed + if (ref.read(tabBarDismissableControllerProvider)) { + return; + } + final diff = event.dy; if (diff < 0) { if (diffAcc.value > 0) { diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart index 6dfb4eeb..b6edd05e 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart @@ -18,7 +18,10 @@ * along with this program. If not, see . */ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; @@ -211,8 +214,6 @@ class BrowserTabBar extends HookConsumerWidget { final dragStartPosition = useRef(Offset.zero); - final toolbarHeight = useMemoized(() => getToolbarHeight()); - return GestureDetector( // Tap handling moved to AppBarTitle for split icon/title behavior onHorizontalDragStart: (details) { @@ -245,19 +246,27 @@ class BrowserTabBar extends HookConsumerWidget { } } } - } else if (distance.dx.abs() < 15) { - // Swipe direction for dismiss depends on toolbar position: - // - Bottom bar: swipe down to dismiss (distance.dy negative or small positive) - // - Top bar: swipe up to dismiss (distance.dy positive or small negative) - final dismissThreshold = toolbarHeight * 0.75; - final shouldDismiss = switch (tabBarPosition) { - TabBarPosition.bottom => distance.dy < dismissThreshold, - TabBarPosition.top => distance.dy > -dismissThreshold, - }; - if (shouldDismiss && - ref.read(bottomSheetControllerProvider) == null) { - ref.read(tabBarDismissableControllerProvider.notifier).dismiss(); - } + } + }, + onVerticalDragStart: (details) { + dragStartPosition.value = details.globalPosition; + }, + onVerticalDragEnd: (details) { + final distance = dragStartPosition.value - details.globalPosition; + + // Swipe direction for dismiss depends on toolbar position: + // - Bottom bar: swipe down to dismiss (positive distance.dy) + // - Top bar: swipe up to dismiss (negative distance.dy) + const dismissThreshold = kToolbarHeight * 0.5; + final shouldDismiss = switch (tabBarPosition) { + TabBarPosition.bottom => + distance.dy.isNegative && distance.dy.abs() > dismissThreshold, + TabBarPosition.top => + !distance.dy.isNegative && distance.dy.abs() > dismissThreshold, + }; + if (shouldDismiss && ref.read(bottomSheetControllerProvider) == null) { + unawaited(HapticFeedback.lightImpact()); + ref.read(tabBarDismissableControllerProvider.notifier).dismiss(); } }, child: Column(