From c4b043cba99dbec5dfe4a4e607dd385cf6af3ea6 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Sun, 23 Aug 2026 10:12:26 +0200 Subject: [PATCH] fix(ui): present the library switcher and profile menu as bottom sheets on mobile Both anchored popups were small tap targets on phones. AppMenuButton gains an opt-in adaptiveSheet flag that routes through showAdaptiveAppMenu: full-width, untitled sheet rows on touch platforms (phones plus Android TV / tvOS), the same anchored popup on desktop. --- lib/screens/discover_screen.dart | 1 + lib/screens/libraries/libraries_screen.dart | 1 + lib/widgets/app_menu.dart | 29 ++++++++++++++++----- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/screens/discover_screen.dart b/lib/screens/discover_screen.dart index a60cf2654..c2353fcec 100644 --- a/lib/screens/discover_screen.dart +++ b/lib/screens/discover_screen.dart @@ -664,6 +664,7 @@ class _DiscoverScreenState extends State ? ProfileAvatar(profile: active, size: 32, avatarUrl: activeProvider.avatarUrlFor(active.id)) : const AppIcon(Symbols.account_circle_rounded, fill: 1, size: 32, color: Colors.white), tooltip: t.profiles.sectionTitle, + adaptiveSheet: true, anchorAlignment: AppMenuAnchorAlignment.end, onSelected: (value) => unawaited(_handleUserMenuAction(context, value)), entriesBuilder: (context) => diff --git a/lib/screens/libraries/libraries_screen.dart b/lib/screens/libraries/libraries_screen.dart index 698ca537b..edefb10d2 100644 --- a/lib/screens/libraries/libraries_screen.dart +++ b/lib/screens/libraries/libraries_screen.dart @@ -634,6 +634,7 @@ class _LibrariesScreenState extends State return AppMenuButton( key: _libraryDropdownKey, tooltip: t.libraries.selectLibrary, + adaptiveSheet: true, onSelected: (libraryGlobalKey) { _loadLibraryContent(libraryGlobalKey); }, diff --git a/lib/widgets/app_menu.dart b/lib/widgets/app_menu.dart index f4c6ced55..eec8dd168 100644 --- a/lib/widgets/app_menu.dart +++ b/lib/widgets/app_menu.dart @@ -160,6 +160,12 @@ class AppMenuButton extends StatefulWidget { final Widget? icon; final Widget? child; final String? tooltip; + + /// When true, iOS/Android (phones plus Android TV / tvOS) present the menu + /// as an untitled bottom sheet — just the drag handle and rows — via + /// [showAdaptiveAppMenu]; desktop keeps the anchored popup. False keeps the + /// anchored popup on every platform. + final bool adaptiveSheet; final bool enabled; final AppMenuEntryBuilder entriesBuilder; final ValueChanged? onSelected; @@ -170,6 +176,7 @@ class AppMenuButton extends StatefulWidget { this.icon, this.child, this.tooltip, + this.adaptiveSheet = false, this.enabled = true, required this.entriesBuilder, this.onSelected, @@ -189,13 +196,21 @@ class AppMenuButtonState extends State> { final topLeft = renderBox.localToGlobal(Offset.zero); final anchorRect = Rect.fromLTWH(topLeft.dx, topLeft.dy, renderBox.size.width, renderBox.size.height); - final selected = await showAppMenu( - context, - entries: widget.entriesBuilder(context), - anchorRect: anchorRect, - anchorAlignment: widget.anchorAlignment, - focusFirstItem: focusFirstItem, - ); + final selected = widget.adaptiveSheet + ? await showAdaptiveAppMenu( + context, + entries: widget.entriesBuilder(context), + anchorRect: anchorRect, + anchorAlignment: widget.anchorAlignment, + focusFirstItem: focusFirstItem, + ) + : await showAppMenu( + context, + entries: widget.entriesBuilder(context), + anchorRect: anchorRect, + anchorAlignment: widget.anchorAlignment, + focusFirstItem: focusFirstItem, + ); if (!mounted || selected == null) return selected; widget.onSelected?.call(selected); return selected;