Andrew
Andrew

Reputation: 612

React navigation 5 hide tab bar from stack navigator

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
}

Upvotes: 28

Views: 49751

Answers (8)

Oluwatobi Akanji
Oluwatobi Akanji

Reputation: 164

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");
  }}

Upvotes: 1

Roman Lapin
Roman Lapin

Reputation: 11

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.

Upvotes: 1

Muhammad Haidar
Muhammad Haidar

Reputation: 2019

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>

Upvotes: 0

angeldroth
angeldroth

Reputation: 407

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),
    })}
/>

Upvotes: 18

rahulworld
rahulworld

Reputation: 577

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>
      );
    }
  }

Upvotes: 4

Emmie
Emmie

Reputation: 796

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>

Upvotes: 54

loekTheDreamer
loekTheDreamer

Reputation: 586

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

Upvotes: 8

Jer-Shen Chen
Jer-Shen Chen

Reputation: 181

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

Upvotes: 18

Related Questions