Home > Mobile >  Possible to hide tab navigator from screen in stack nested in tab?
Possible to hide tab navigator from screen in stack nested in tab?

Time:03-25

I have a Stack Navigator with some screens and an initial route like "Profile", and when I navigate to "Options" via a navigation.navigate("Options") from the "Profile" screen, I don't want to see the bottom tabs. Here's an example of what I have:

ProfileNav.js

export default function ProfileNav () {
   return (
      <Stack.Navigator initialRoutName="Profile">
         <Stack.Screen name="Profile" component={ProfileScreen}>
         <Stack.Screen name="Options" component={OptionsScreen}>
      </Stack.Navigator>
   );
};

TabNav.js

export default function TabNav () {
   return (
      <Tab.Navigator initialRouteName="Home">
         <Tab.Screen name="Home" component={HomeScreen}>
         <Tab.Screen name="ProfileNav" component={ProfileNav}>
      </Tab.Navigator>
   );
};

I'm using React Navigation v6. I've seen the Hiding tab bar in specific screens docs describe how to swap around screens to achieve hiding the tabs from a single screen, but in this case I'm trying to have the parent screen of the ProfileNav stack still show the bottom tabs, but I don't want the rest of the screens in the stack to show them, which is not what the docs help in achieving unless I missed something.

So how do I achieve hiding the bottom tabs from select screens of a stack navigator nested in a tab navigator?

I have also tried passing in tabBarVisible into the "Options" screen options, but this didn't work.

CodePudding user response:

You can pass the navigationContainerRef to the NavigationContainer and get the current route name via getCurrentRoute in the TabNav component in order to hide the tab bar for specific screens that are handled by a different navigator.

Then, pass the route name as a state to the Tab navigator and decide for which screens to show the tab bar and for which not.

In this case, you want to show the TabBar for the route Profile only. Here is a minimal working example.

import React, {useState} from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {NavigationContainer} from '@react-navigation/native';
import { createNavigationContainerRef } from "@react-navigation/native"

const Stack = createNativeStackNavigator()

function ProfileNav () {
   return (
      <Stack.Navigator initialRoutName="Profile">
         <Stack.Screen name="Profile" component={ProfileScreen} />
         <Stack.Screen name="Options" component={OptionsScreen} />
      </Stack.Navigator>
   );
}

const Tab = createBottomTabNavigator();

function TabNav (props) {
  const hide = props.routeName != "Profile"
   return (
      <Tab.Navigator initialRouteName="Home">
         <Tab.Screen name="Home" component={HomeScreen} />
         <Tab.Screen name="ProfileNav" component={ProfileNav} options={{
          headerShown: false,
          tabBarStyle: { display: hide ? "none" : "flex" }
        }} />
      </Tab.Navigator>
   );
}

function HomeScreen() {
  return <View></View>
}

function ProfileScreen(props) {
  return <View>
     <Button title="Nav to options" onPress={() => props.navigation.navigate("Options")}></Button>
  </View>
}

function OptionsScreen() {
  return <View></View>
}

const ref = createNavigationContainerRef();

export default function App() {
  const [routeName, setRouteName] = useState();

  return (
    <NavigationContainer
      ref={ref}
      onReady={() => {
        setRouteName(ref.getCurrentRoute().name)
      }}
      onStateChange={async () => {
        const previousRouteName = routeName;
        const currentRouteName = ref.getCurrentRoute().name;
        setRouteName(currentRouteName);
      }}
    >
       <TabNav routeName={routeName} />
     </NavigationContainer>
  );
}

And here is a working snack.

CodePudding user response:

you can use react's useLayoutEffect with getFocusedRouteNameFromRoute function from @react-navigation/native

first, we need to extract navigation and route from tab navigator params

then using useLayoutEffect every time the focused layout is changed we are checking for focused route name if the focused route name matches our route name ( in your case "Options" ) where are going to set "tabBarVisible" to "false"

export default function ProfileNav ({ navigation, route }) {
    React.useLayoutEffect(() => {
        const routeName = getFocusedRouteNameFromRoute(route);
        if (routeName == "Options") {
        navigation.setOptions({ tabBarVisible: false });
        } else {
        navigation.setOptions({ tabBarVisible: true });
        }
    }, [navigation, route]);
   return (
      <Stack.Navigator initialRoutName="Profile">
         <Stack.Screen name="Profile" component={ProfileScreen}>
         <Stack.Screen name="Options" component={OptionsScreen}>
      </Stack.Navigator>
   );
};

CodePudding user response:

You need to call TabNavigator stack from StackNavigator instead, that should works.

export default function TabNav () {
   return (
      <Tab.Navigator>
         <Tab.Screen name="Home" component={HomeScreen}>
         <Tab.Screen name="Profile" component={ProfileScreen}>
      </Tab.Navigator>
   );
};

return(<NavigationContainer
>
    <Stack.Navigator initialRoutName="Home">
      <Stack.Screen name="Home" component={TabNav} />
      <Stack.Screen name="Options" component={OptionsScreen} />
    </Stack.Navigator>

 </NavigationContainer>)
  • Related