diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 4eaa8ad0..8e46f6f9 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -280,6 +280,7 @@ class _MainScreenState extends State _offlineUntilConnected = widget.isOfflineMode; WidgetsBinding.instance.addObserver(this); + _contentFocusScope.addListener(_syncSidebarFocusWithContent); if (PlatformDetector.isDesktopOS()) { windowManager.addListener(this); @@ -823,6 +824,7 @@ class _MainScreenState extends State _startupSettleTimeout?.cancel(); _startupSettleTimeout = null; _sidebarFocusScope.dispose(); + _contentFocusScope.removeListener(_syncSidebarFocusWithContent); _contentFocusScope.dispose(); _setTvosMenuPassthrough(false); @@ -1089,6 +1091,15 @@ class _MainScreenState extends State }); } + /// _isSidebarFocused is hand-toggled; if anything moves real focus into the + /// content scope without going through _focusContent (e.g. a deferred + /// focusActiveTabIfReady), collapse the rail to match reality (#1411). + void _syncSidebarFocusWithContent() { + if (!mounted || !_isSidebarFocused || !_contentFocusScope.hasFocus) return; + setState(() => _isSidebarFocused = false); + _updateTvosMenuPassthrough(); + } + void _handleSidebarInteractionExpandedChanged(bool expanded) { if (_isSidebarInteractionExpanded == expanded) return; setState(() => _isSidebarInteractionExpanded = expanded); @@ -1142,6 +1153,9 @@ class _MainScreenState extends State final homeTab = tabs.first.id; if (_currentTab != homeTab) { _selectTab(homeTab); + // Keep the focus ring in step with the new selection; the sidebar scope + // already has focus, so no post-frame deferral is needed. + _sideNavKey.currentState?.focusHomeItem(); _lastBackPressAt = null; return KeyEventResult.handled; } @@ -1390,8 +1404,13 @@ class _MainScreenState extends State if (newState case final TabVisibilityAware aware) { aware.onTabShown(); } - if (newState case final FocusableTab focusable) { - focusable.focusActiveTabIfReady(); + // Back-to-home keeps the sidebar focused (chain: content → sidebar → + // home → exit); stealing focus here left _isSidebarFocused stuck true + // while real focus sat on a content card (#1411). + if (!_isSidebarFocused) { + if (newState case final FocusableTab focusable) { + focusable.focusActiveTabIfReady(); + } } } diff --git a/lib/widgets/side_navigation_rail.dart b/lib/widgets/side_navigation_rail.dart index bf8b9425..bc36e83b 100644 --- a/lib/widgets/side_navigation_rail.dart +++ b/lib/widgets/side_navigation_rail.dart @@ -341,6 +341,13 @@ class SideNavigationRailState extends State with MountedSetS _requestFocusAndReveal(node); } + /// Focus the Home nav item (Back returning to the home tab). + void focusHomeItem() { + final node = _mountedFocusNodeFor(_kHome); + if (node == null) return; + _requestFocusAndReveal(node); + } + /// Resolve the best mounted focus node in priority order: /// 1. Explicit [targetKey] (captured before scope switch) /// 2. Last focused key still in the tracker