@@ -22,16 +22,25 @@ const ROUTE_TO_NAVIGATION_TAB: Record<string, ValueOf<typeof NAVIGATION_TABS>> =
2222 [ NAVIGATORS . WORKSPACE_NAVIGATOR ] : NAVIGATION_TABS . WORKSPACES ,
2323} ;
2424
25- // Tab wrapper navigators — when `getFocusedLeafScreenName` bottoms out on one of these
26- // (because the nested stack state hasn't hydrated yet), the user is landing on the tab root,
27- // so treat it as "at root" to keep the tab bar visible during the transition.
25+ // Count as tab-root when they surface as the resolved leaf.
2826const TAB_WRAPPER_NAVIGATORS = new Set < string > ( [
2927 NAVIGATORS . REPORTS_SPLIT_NAVIGATOR ,
3028 NAVIGATORS . SEARCH_FULLSCREEN_NAVIGATOR ,
3129 NAVIGATORS . SETTINGS_SPLIT_NAVIGATOR ,
3230 NAVIGATORS . WORKSPACE_NAVIGATOR ,
3331] ) ;
3432
33+ const isAtTabRootLevel = ( name : string | undefined ) : boolean => ! name || ROOT_TAB_SCREENS . has ( name ) || TAB_WRAPPER_NAVIGATORS . has ( name ) ;
34+
35+ // Deepest `screen` in a `{screen, params}` chain (e.g. WORKSPACE_NAV → WORKSPACE_SPLIT_NAV → WORKSPACE.INITIAL).
36+ const getPushTargetLeaf = ( params : unknown ) : string | undefined => {
37+ const p = params as { screen ?: unknown ; params ?: unknown } | undefined ;
38+ if ( typeof p ?. screen !== 'string' ) {
39+ return undefined ;
40+ }
41+ return getPushTargetLeaf ( p . params ) ?? p . screen ;
42+ } ;
43+
3544/**
3645 * Custom tab bar rendered by the BottomTabNavigator. Only receives `state` (not the
3746 * full BottomTabBarProps) to avoid `descriptors` thrashing memoization.
@@ -44,8 +53,8 @@ function TabNavigatorBar({state}: Pick<BottomTabBarProps, 'state'>) {
4453 const StyleUtils = useStyleUtils ( ) ;
4554 const activeRoute = state . routes [ state . index ] ;
4655 const selectedTab = ROUTE_TO_NAVIGATION_TAB [ activeRoute ?. name ?? SCREENS . HOME ] ?? NAVIGATION_TABS . HOME ;
47- const focusedScreen = getFocusedLeafScreenName ( activeRoute ?. state ) ;
48- const isAtRoot = ! focusedScreen || ROOT_TAB_SCREENS . has ( focusedScreen ) || TAB_WRAPPER_NAVIGATORS . has ( focusedScreen ) ;
56+ // Check both leaves so wrapper hydration doesn't flash the tab bar on the push target (Android).
57+ const isAtRoot = isAtTabRootLevel ( getFocusedLeafScreenName ( activeRoute ?. state ) ) && isAtTabRootLevel ( getPushTargetLeaf ( activeRoute ?. params ) ) ;
4958 // --- Narrow-only animation logic (hooks must run unconditionally per Rules of Hooks) ---
5059 // On native, screens also render the tab bar via bottomContent for swipe-back animations.
5160 // Delay showing this navigator's tab bar only when navigating back from a deeper screen
0 commit comments