28

I wanted to know how to hide the bottom tab bar from a specific screen inside my stack navigator that is nested on a material bottom tab bar

This is my code for my stack navigator

import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import PondScreen from '../screens/PondScreen/PondScreen';
import PondDetailScreen from '../screens/PondScreen/PondDetailScreen';

const Stack = createStackNavigator();

export function PondStack() {
  return (
    <Stack.Navigator
      initialRouteName="PondScreen"
      headerMode="none"
      mode="card"
    >
      <Stack.Screen
        name="PondScreen"
        component={PondScreen}
      />
      <Stack.Screen
        name="PondDetailScreen"
        component={PondDetailScreen}
        options={{
          tabBarVisible: false
        }}
      />
    </Stack.Navigator>
  );
}

This is my code for my material bottom tab navigator

import React from 'react';
import { View } from 'react-native';
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
import { Entypo, Feather } from '@expo/vector-icons';
import { PondStack } from './StackNavigators';
import StockScreen from '../screens/StockScreen';
import OrderScreen from '../screens/OrderScreen';
import SettingsScreen from '../screens/SettingsScreen';

const Tab = createMaterialBottomTabNavigator();

export default function BottomTab() {
  return (
    <Tab.Navigator
      labeled={false}
      initialRouteName="Pond"
      activeColor="#EB3349"
      inactiveColor="#888888"
      backBehavior="none"
      shifting={true}
      barStyle={{
        backgroundColor: '#FFFFFF'
      }}
    >
      <Tab.Screen
        name="Pond"
        component={PondStack}
        options={{
          tabBarIcon: ({ color}) => (
            <View style={{ flex: 1 }}>
              <Entypo name="air" color={color} size={20} />
            </View>
          )
        }}
      />
      <Tab.Screen
        name="Stock"
        component={StockScreen}
        options={{
          tabBarIcon: ({ color }) => (
            <View style={{ flex: 1 }}>
              <Feather name="box" color={color} size={20} />
            </View>
          )
        }}
      />
      <Tab.Screen
        name="Order"
        component={OrderScreen}
        options={{
          tabBarIcon: ({ color}) => (
            <View style={{ flex: 1 }}>
              <Feather name="dollar-sign" color={color} size={20} />
            </View>
          )
        }}
      />
      <Tab.Screen
        name="Settings"
        component={SettingsScreen}
        options={{
          tabBarIcon: ({ color}) => (
            <View style={{ flex: 1 }}>
              <Feather name="settings" color={color} size={20} />
            </View>
          )
        }}
      />
    </Tab.Navigator>
  )
}

I am currently using Expo to build my project.

My dependencies (package.json)

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject"
  },
  "dependencies": {
    "@react-native-community/masked-view": "^0.1.5",
    "@react-navigation/material-bottom-tabs": "^5.0.0",
    "@react-navigation/native": "^5.0.0",
    "@react-navigation/stack": "^5.0.0",
    "@types/react-native": "^0.61.12",
    "expo": "~36.0.0",
    "react": "~16.9.0",
    "react-dom": "~16.9.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz",
    "react-native-gesture-handler": "~1.5.0",
    "react-native-paper": "^3.6.0",
    "react-native-raw-bottom-sheet": "^2.0.6",
    "react-native-reanimated": "~1.4.0",
    "react-native-safe-area-context": "0.6.0",
    "react-native-screens": "2.0.0-alpha.12",
    "react-native-vector-icons": "^6.6.0",
    "react-native-web": "~0.11.7"
  },
  "devDependencies": {
    "@babel/core": "^7.0.0",
    "babel-preset-expo": "~8.0.0"
  },
  "private": true
}
Andrew
  • 592
  • 1
  • 7
  • 17
  • For your next question, you may want to tag your question with react-navigation so more people can see it. – satya164 Feb 12 '20 at 19:28

8 Answers8

54

I had almost the same issue with a tabnavigation as parent and stacknavigation as childs and rearranging my screen layer wasn't an option. So I looked for another solution and from the docs I found out that the parent navigation UI is always shown on the child. But the docs also gave a great example on how to change a parent header from within a child. So I took that example and implemented it for the tabbar visibility. This is how I implemented it.

So I have a tabbar navigation with Home, Contacts and More, and inside each tab I have a stack. The screen that I hide the tabbar in is in the CameraView, and that screen is a stackscreen in the More tab.

  • Home
  • Contacts
  • More
    • Profile
    • CameraView (here I want to hide the tabbar)

Tabnavigation:

As you can see I get the visibility of the tabbar from a method.

<NavigationContainer>
  <Tab.Navigator>
    <Tab.Screen name="Home" component={HomeNavigation} />
    <Tab.Screen name="Contacts" component={ContactNavigation} />
    <Tab.Screen
      name="More"
      component={MoreNavigation}
      options={({ route }) => ({
        tabBarVisible: this.getTabBarVisibility(route)
      })}
    />
  </Tab.Navigator>
</NavigationContainer>

Method getTabBarVisibility:

This is were I check if the name of the route is CameraView which I defined in the StackNavigation.

getTabBarVisibility = (route) => {
  const routeName = route.state
    ? route.state.routes[route.state.index].name
    : '';

  if (routeName === 'CameraView') {
    return false;
  }

  return true;
}

And the component MoreNavigation:

This is my stacknavigation for More, where you can see that the screen name is CameraView.

<Stack.Navigator initialRouteName="More">
  <Stack.Screen name="More" component={More}/>
  <Stack.Screen name="UserProfile" component={Profile}/>
  <Stack.Screen name="CameraView" component={CameraView}/>
</Stack.Navigator>
fredrivett
  • 5,419
  • 3
  • 35
  • 48
Emmie
  • 796
  • 11
  • 22
  • 3
    The documentation recommends not using this approach because it will cause glitchy animations with the footer component. I would recommend not using this approach and instead re-organizing your pages like the documentation [suggests](https://reactnavigation.org/docs/hiding-tabbar-in-screens). – Eric Wiener May 18 '20 at 19:45
  • 4
    @EricWiener the documentation is recently updated, I gave this answer before the documentation was updated. We haven't experienced any glitches in our app. Therefor we stick with this solution and will not re-organize our pages, while it is weird to have an extra stack that you don't use, only for hiding a bottombar. By the way they suggest this approach if you want to set the header differently.... – Emmie May 19 '20 at 08:07
  • @Emmie got it. Thanks for letting me know. Your answer definitely gets the job done, but for me, it was causing the tab bar to disappear too early before navigating to a new page. – Eric Wiener May 19 '20 at 12:58
  • 2
    @EricWiener Good to know, when we have issues with glitching we will look into this. – Emmie May 20 '20 at 06:42
  • @EricWiener thank you its working but it shows white space in bottom.when i hide bottom tab in nextscreen. anyone have any idea. – Pragnesh May 26 '20 at 10:09
  • @pragnesh I ended up having the same issue when using a bottom tab with a Keyboard Accessory view. I hide the tab bar on the screen that was an issue and displayed an identical tab bar component as a child of that screen – Eric Wiener May 26 '20 at 10:27
  • This worked for me when transitioning to a WebView that took over the entire view but when the user swipes to go back it kind of chugs on the animation :/ – Taylor A. Leach Jun 01 '20 at 16:17
  • "tabBarVisible" is not available in createMaterialBottomTabNavigator, so it does not work! – Adam Ri Nov 02 '20 at 08:27
  • @AdamRi it is not available for specific createMaterialBottomTabNavigator. So it does work for the user who asked the question, but it is indeed not working for the specific Material bottom. – Emmie Nov 02 '20 at 11:02
  • this method will give you warning about you can't access route directly something. – Abhi Dec 30 '20 at 18:08
  • @AbhishekYadav you got to give more information than this, otherwise no one can help you out. – Emmie Dec 31 '20 at 08:55
  • Hi, i have a very similar problem. where should i define the getTabBarVisibility method? in the same file as the tab navigator? or stack navigator? – kd12345 Apr 25 '21 at 07:33
  • @kd12345 in the tabnavigation you see: this.getTabBarVisibility(route). So the method is here inside the tabnavigation. You can place it wherever you like as long as you perform that part of code. – Emmie Apr 26 '21 at 06:44
  • 2
    use: const routeName = getFocusedRouteNameFromRoute(route); to avoid the error: Accessing the 'state' property of the 'route' object is not supported. If you want to get the focused route name, use the 'getFocusedRouteNameFromRoute' helper instead: https://reactnavigation.org/docs/screen-options-resolution/#setting-parent-screen-options-based-on-child-navigators-state – Talal Yousif Jul 16 '21 at 21:04
  • thanx broooooooo... – Berk Kanburlar Feb 11 '22 at 19:14
18

You should try to rearrange your screen layer,

Original

  • TabBar
    • Pond(Stack)
      • PondScreen
      • DetailScreen
    • Stock
    • Others

Instead, try to set a top stack over

  • TopStack
    • TabBar
      • PondScreen
      • Stock
      • Others
    • Details

That should be able to hide the bottom tab bar or tab header in each screen

18

The accepted answer is great, but you might want to do it inline, and use the getFocusedRouteNameFromRoute to be safe. This code does the same as the accepted answer:

<Tabs.Screen
    name="Home"
    component={HomeStack}
    options={({ route }) => ({
        tabBarVisible: ((route) => {
            const routeName = getFocusedRouteNameFromRoute(route) ?? ""

            if (routeName === "CameraView") {
                return false
            }

            return true
        })(route),
    })}
/>
angeldroth
  • 379
  • 4
  • 5
  • Cleanest solution by far. Exactly what I needed. The docs suggest this will be "glitchy" but that is not my experience. During animation the tab bar simply slides down and leaves the viewport, then slides up when you call goBack(). Thank you! – Frankie Jarrett Dec 25 '20 at 03:45
  • 3
    you need to import getFocusedRouteNameFromRoute from '@react-navigation/native'; – Abhi Dec 30 '20 at 18:09
  • Sorry if this is a dumb question (new to React Native) but why would inline be preferred? – Ryker Jun 08 '21 at 18:18
  • 5
    The tabBarVisible option is no longer present# Since the the tab bar now supports a tabBarStyle option, we have removed the tabBarVisible option. You can achieve the same behavior by specifying tabBarStyle: { display: 'none' } in options. https://reactnavigation.org/docs/6.x/upgrading-from-5.x/#the-tabbarvisible-option-is-no-longer-present – Lonare Jun 08 '21 at 18:23
  • After I use this approach hide tab bar, the present view height didn't correct sometimes(the tab bar didn't show, but an empty block still at there). any solution? – frank61003 Oct 19 '22 at 04:16
8

The trick is to add the TabsStack, OtherStack or SomeOtherScreen inside your

<Stack.Navigator /> via a <Stack.Screen />

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="TabsStack" component={TabsStack} />
        <Stack.Screen name="SomeOtherScreen" component={SomeOtherScreen} />
        <Stack.Screen name="OtherStack" component={OtherStack} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

it is documented over here

loekTheDreamer
  • 576
  • 7
  • 24
  • 1
    Thanks Luke. Good answer with example code in it and it works – Andrew Apr 13 '20 at 13:58
  • 1
    I use to have this on Navigation4.x but in 5, everything like new so I'm missed about this. It is works – Luiey Aug 11 '20 at 04:32
  • 2
    Thank you, this the best solution, you can show the TabsStack header only and hide the "Stack.Screen" using options={{ headerShown: false }} like this: – hamzeh.hanandeh Nov 16 '20 at 06:02
3

Add this function for hiding the bottom bar in MyTabBar

const focusedOptions = descriptors[state.routes[state.index].key].options;
if (focusedOptions.tabBarVisible === false) {
  return null;
}

MyTabBar

    import { View, Text, TouchableOpacity } from 'react-native';

    function MyTabBar({ state, descriptors, navigation }) {
      const focusedOptions = descriptors[state.routes[state.index].key].options;

      if (focusedOptions.tabBarVisible === false) {
        return null;
      }

      return (
        <View style={{ flexDirection: 'row' }}>
          {state.routes.map((route, index) => {
            const { options } = descriptors[route.key];
            const label =
              options.tabBarLabel !== undefined
                ? options.tabBarLabel
                : options.title !== undefined
                ? options.title
                : route.name;

            const isFocused = state.index === index;

            const onPress = () => {
              const event = navigation.emit({
                type: 'tabPress',
                target: route.key,
                canPreventDefault: true,
              });

              if (!isFocused && !event.defaultPrevented) {
                navigation.navigate(route.name);
              }
            };

            const onLongPress = () => {
              navigation.emit({
                type: 'tabLongPress',
                target: route.key,
              });
            };

            return (
              <TouchableOpacity
                accessibilityRole="button"
                accessibilityStates={isFocused ? ['selected'] : []}
                accessibilityLabel={options.tabBarAccessibilityLabel}
                testID={options.tabBarTestID}
                onPress={onPress}
                onLongPress={onLongPress}
                style={{ flex: 1 }}
              >
                <Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
                  {label}
                </Text>
              </TouchableOpacity>
            );
          })}
        </View>
      );
    }

add bottom bar visibility in screen stack

    const getTabBarVisibility = (route) => {
      const routeName = route.state
        ? route.state.routes[route.state.index].name
        : '';

      if (routeName === 'Profile') {
        return false;
      }

      return true;
    };

Add options in main tab navigators

    const MainAppNavigator = ({userToken}) => {
      return (
        <NavigationContainer>
          {!userToken ? (
            <AuthNavigator />
          ) : (
              <Tab.Navigator tabBar={(props) => <MyTabBar {...props} />}>
              <Tab.Screen
                name={'Dashboard'}
                component={DashboardStackScreen}
              />
              <Tab.Screen
                name={'More'}
                component={MoreStackScreen}
                options={({route}) => ({
                  tabBarVisible: getTabBarVisibility(route),
                })}
              />
            </Tab.Navigator>

          )}
        </NavigationContainer>
      );
    };

Add Profile screen in More Stack

  const MoreStack = createStackNavigator();

  export default class MoreStackScreen extends React.Component {
    render() {
      return (
        <MoreStack.Navigator initialRouteName={'More'}>
          <MoreStack.Screen
            name={'More'}
            component={More}
          />
          <MoreStack.Screen
            name={'Profile'}
            component={Profile}
          />
        </MoreStack.Navigator>
      );
    }
  }
rahulworld
  • 547
  • 1
  • 5
  • 5
1

Tabbarvisible property didn't work for me and I made a small workaround. It includes ReactContext + Hoc.

  1. First thing we need to do is to create a react context with two fields visible and setVisible

export const TabBarVisibilityContext = React.createContext({
  visible: false,
  setVisible: () => {
  },
});
2) Then we will create a hoc that will wrap a tab navigator component.

export const TabBarHidable = (Component) => (props) => {
  const [visible, setVisible] = useState(true);
  return (
    <TabBarVisibilityContext.Provider value={{ visible, setVisible }}>
      <Component {...props} />
    </TabBarVisibilityContext.Provider>
  );
};
  1. Apply hoc for tab navigator component and extract a visible property from the context.

const { visible, setVisible } = useContext(TabBarVisibilityContext);
  1. Update properties in tab navigator options tabBarOptions = {{ style: { transform: !visible ? [{ scale: 0 }] : [], }}

  2. Use setVisible from context to make the tabbar hidden.

 const { setVisible } = useContext(TabBarVisibilityContext);

useEffect(() => {
 setVisible(false);
  return () => setVisible(true);
, []})

Hope it will help someone to save his/her time. I haven't found an appropriate and concise solution to the given problem.

1

I am using typescript and I encountered such an issue too. Since the tabBarVisible option is no longer available, I used the tabBarStyle instead and set the display property to 'none'.

Suppose I have two screens, the Home screen which is the tabbed screen and another screen say Side screen which is a Stacked screen where I want to hide the tab bar, then I do this in the Stacked screen navigator component:

const setTabStyleVisibility = (shouldBeVisible: boolean) =>
shouldBeVisible
  ? ({
      tabBarStyle: { display: 'flex' },
    } as Partial<BottomTabNavigationOptions>)
  : ({
      tabBarStyle: { display: 'none' },
    } as Partial<BottomTabNavigationOptions>);

I have a custom button defined as:

<Button title={btnTitle} onPress={onPressHandler}/>

On the Home Screen, I have a button which navigates to the Side screen and hides the tab by defining the custom onPressedHandler prop to the custom button as:

onPressHandler={() => {
    navigation.setOptions({
      ...setTabStyleVisibility(false),
    });
    navigation.navigate("Side");
  }}

Then I had a button passed to the next screen (Side screen) where the tab bar will be shown on going back to Home screen. I set the onPressedHandler which is a custom prop to the custom button to

onPressHandler={() => {
    navigation.setOptions({
      ...setTabStyleVisibility(true),
    });
    navigation.navigate("Home");
  }}
0

You don't need to use Keyboard listeners and change your AndroidManifest.xml file you can solve this issue by just adding this tabBarOptions props :

 <Tab.Navigator

 tabBarOptions={{

keyboardHidesTabBar: true,

 }}>

 </Tab.Navigator>
Muhammad Haidar
  • 1,541
  • 16
  • 17