Reputation: 612
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
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
Reputation: 11
Tabbarvisible property didn't work for me and I made a small workaround. It includes ReactContext + Hoc.
export const TabBarVisibilityContext = React.createContext({
visible: false,
setVisible: () => {
},
});
export const TabBarHidable = (Component) => (props) => {
const [visible, setVisible] = useState(true);
return (
<TabBarVisibilityContext.Provider value={{ visible, setVisible }}>
<Component {...props} />
</TabBarVisibilityContext.Provider>
);
};
const { visible, setVisible } = useContext(TabBarVisibilityContext);
Update properties in tab navigator options tabBarOptions = {{ style: { transform: !visible ? [{ scale: 0 }] : [], }}
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
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
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
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
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.
- 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
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
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