I have an app with a bottom bar and I'd like to present a modal screen from one of them without the bottom tab. I don't understand why it works as expected on iOS but not on Android.
Here is my bottom tab navigator which contains all my tabs:
// AppStack.tsx
const Tab = createBottomTabNavigator()
const AppStack = () => {
return (
<Tab.Navigator initialRouteName="homeStack" >
<Tab.Screen name="homeStack" component={HomeStack} />
...
</Tab.Navigator>
)
}
And here is the stack navigator that contains several screens but also contains one screen that should be presented modally (thus without the bottom bar).
// HomeStack.tsx
const Stack = createNativeStackNavigator()
const HomeStack = () => {
return (
<Stack.Navigator initialRouteName="home">
<Stack.Screen name="home" component={HomeScreen} />
...
<Stack.Screen
name="addStuff"
component={StuffStack}
options={{
presentation: 'fullScreenModal',
}}
/>
</Stack.Navigator>
)
}
Presenting the addStuff modal screen from one of HomeStack's screens works on iOS as expected: the bottom bar isn't displayed. But on Android, the bottom bar is still present...
// HomeScreen.tsx
navigation.navigate('addStuff')
Does anybody have an idea on how to tell react-navigation not to add this bottom bar on this modal screen?
You can create conditional rules to check the screen name.
To achieve this, you need to get the navigation props (which is present on your HomeStack) and use the getFocusedRouteNameFromRoute method to get the current screen name:
import {getFocusedRouteNameFromRoute} from '#react-navigation/native';
// rest of the codes ...
const HomeStack = ({route, navigation}) => {
React.useLayoutEffect(() => {
const routeName = getFocusedRouteNameFromRoute(route);
if (routeName === 'addStuff') {
navigation.setOptions({tabBarVisible: false});
} else {
navigation.setOptions({tabBarVisible: true});
}
}, [navigation, route]);
return (
<Stack.Navigator initialRouteName="home">
<Stack.Screen name="home" component={HomeScreen} />
// rest of the codes ...
<Stack.Screen
name="addStuff"
component={StuffStack}
options={{
presentation: 'fullScreenModal',
}}
/>
</Stack.Navigator>
)
}
Related
I want to achieve twitter like tab and drawer navigation, where there is a bottom tab bar in the home page and a drawer on the side. However, in twitter when clicking on the left drawer, it opens a page with the left drawer disabled and instead the page will have a back navigation that leads to the home page. This is the behavior I want, and how can I achieve that? (I don't want bottom tab in any of the page I opened from the side drawer, different from twitter)
Following is my code:
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
function Home() {
const { signOut } = useAuth();
return (
<Tab.Navigator screenOptions={{ headerShown: false }}>
<Tab.Screen name="Feed" component={Feed} />
<Tab.Screen name="Chat" component={Chat} />
</Tab.Navigator>
);
}
function DrawerHome() {
return (
<Drawer.Navigator>
<Drawer.Screen
name="Start"
component={Home}
options={({ route }) => ({
headerTitle: getHeaderTitle(route),
})}
/>
<Drawer.Screen name="Friends" component={FriendsList} />
</Drawer.Navigator>
);
}
I have HomeScreen with a link that goes to DeckScreen. When I click a button to navigate to the DeckScreen, the back button in the header bar shows up fine.
But when I reload the page in browser or directly navigate to this URL (localhost/deck), there is no back button.
And clicking on the BottomTab doesn't do anything, will not take us back Home.
I am using BottomTab that has a HomeStack, which contains the HomeScreen and DeckScreen.
export default function Navigation () {
return (
<NavigationContainer linking={linking} theme={DefaultTheme}>
<RootNavigator/>
</NavigationContainer>
);
}
function RootNavigator () {
return (
<Stack.Navigator>
<Stack.Screen name='Root' component={Nav} options={{headerShown: false, ...fade}}/>
<Stack.Group screenOptions={{presentation: 'modal'}}>
<Stack.Screen name='Modal' component={ModalScreen}/>
</Stack.Group>
</Stack.Navigator>
);
}
function HomeStackScreen () {
return (
<HomeStack.Navigator initialRouteName='dashboard'>
<HomeStack.Screen name='dashboard' component={HomeScreen} options={{headerShown: false, title: 'Dashboard'}}/>
<HomeStack.Screen name='deck' component={DeckScreen} options={{title: 'Deck'}}/>
</HomeStack.Navigator>
);
}
function Nav ({navigation}) {
return (
<BottomTab.Navigator
initialRouteName='home'
screenOptions={{
headerShown: false,
}}>
<BottomTab.Screen
name='home'
component={HomeStackScreen}
})}
/>
</BottomTab.Navigator>
);
}
And here is my Linking:
const linking: LinkingOptions<RootStackParamList> = {
prefixes: [Linking.makeUrl('/')],
config: {
screens: {
Root: {
screens: {
home: {
screens: {
dashboard: 'dashboard',
deck: 'deck'
},
}
},
}
}
}
};
I've tried using getStateFromPath to try to inject a route in stack but it doesn't work and feels wrong.
How do you tell React Navigation, this screen is part of a stack, and it should always have a back button in that header?
The reason why there's no back button when you're opening from the link is most likely because you don't set headerLeft in the screen and there's no other screen in the navigation stack (you went directly to the DeckScreen).
You can set the back button in the option in Screen, like this example below:
function StackScreen() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}}
/>
</Stack.Navigator>
);
}
You can find the example here
when I hide the tabbar using tabBarVisible in a specific screen the upper half of the button in the middle will be still visible (above the red line), any ideas how I can hide that also?
I'm using react-navigation v5
const StackNav = (props: any) => {
React.useLayoutEffect(() => {
routes.includes(name)
? navigation.setOptions({tabBarVisible: false;})
: navigation.setOptions({tabBarVisible: true;})
}, [navigation, route]);
return (
<Stack.Navigator>
<Stack.Screen name="home" component={HomeScreen} />
<Stack.Screen name="food" component={FoodScreen} />
<Stack.Screen name="review" component={ReviewScreen} />
</Stack.Navigator>
);
};
so the BottomTab navigation wraps the StackNav that holds all the screens of the App
const BottomTab = () => {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
screenOptions={({route}) => ({
tabBarIcon: ({focused}) => {
return <Tab focused={focused} />;
},
})}
>
<Tab.Screen name="profile" component={StackNav} />
<Tab.Screen name="story" component={StackNav} />
</Tab.Navigator>
);
};
React-navigation don't recommend using the tabBarVisible option. To solve your problem, you can use this workflow to hide the tabBar properly.
The principle is simply to take out the screens that don't need the tabBar from the Tab.Navigator by a parent using a Stack.Navigator.
I use this to my own app using the same tabBar UI as you, it works perfectly.
With React Navigation 5, I want to open Drawer when I click on bottom tab navigator (I use material bottom navigator).
I manage to create the bottom tabs buttons and click on them, the home page opens for both tabs (GymIndexScreen or FoodIndexScreen).
When I am on the home pages (GymIndexScreen or FoodIndexScreen), I can open the different Drawers with my fingers (GymDrawerNavigator and FoodDrawerNavigator ) : everything works fine.
Question :
I want the drawers to open / close (toggle) automatically when I click the bottom tabs buttons, without having to open them with my fingers.
App.js :
import { NavigationContainer } from '#react-navigation/native'
const App = () => {
return (
<NavigationContainer>
<BottomTabNavigator />
</NavigationContainer>
)
}
BottomTabNavigator.js :
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs'
const Tab = createMaterialBottomTabNavigator()
const BottomTabNavigator = (props) => {
return (
<Tab.Navigator>
<Tab.Screen
name="Gym"
component={GymDrawerNavigator}
options={{
tabBarLabel: "Musculation",
)}
/>
<Tab.Screen
name="Food"
component={FoodDrawerNavigator}
options={{
tabBarLabel: "Alimentation",
)}
/>
</Tab.Navigator>
)
}
GymDrawerNavigator.js :
import { createDrawerNavigator } from '#react-navigation/drawer'
const Drawer = createDrawerNavigator()
const GymDrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen
name="Gym"
component={GymStackNavigator}
/>
</Drawer.Navigator>
)
}
GymStackNavigator.js :
import { createStackNavigator } from '#react-navigation/stack'
const Stack = createStackNavigator()
const GymStackNavigator = () => {
return (
<Stack.Navigator initialRouteName="GymIndex">
<Stack.Screen
name="GymIndex"
component={GymIndexScreen}
}}
/>
<Stack.Screen
name="GymExerciseIndex"
component={GymExerciseIndexScreen}
}}
/>
... list of screens
If I understood your problem correctly you want to open the drawer automatically when you navigate to the screen?
Add this to the screen components you wish to open the drawer when navigated to.
import {useEffect} from 'react'
...
useEffect(()=>{
navigation.addListener('focus', () => {
// when screen is focused (navigated to)
navigation.openDrawer();
});
},[navigation])``
This answer helped me.
Just use the listeners prop to preventDefault behaviour and then open the drawer.
<Tabs.Screen
name={"More"}
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault();
navigation.openDrawer();
}
})}
component={Home}
/>
Trying to update my app to react navigation 5 and been confronting some issues.
First of all, the header does not show up. Snips from the code:
[from App.js]
const Tab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator >
<Tab.Screen name="Home" component={HomeScreen} options={{ title:'some title' }}/>
<Tab.Screen name="Upload" component={UploadScreen} />
<Tab.Screen name="Find" component={FindScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default App;
and the style of the current screen:
<View style={{flex:1, flexDirection:'column',justifyContent:'space-between'}}>
Here is a screenshot of the app on an Android emulator (and it looks the same on my phone):
As you can see, the header is not shown, the tab navgiation does not right, and so are the buttons (something changed about their background). I did not change anything in the app besides upgrading to react-navigation 5..
Thanks for the help!
Tab navigators do not have header support. You have to wrap your tab navigator inside a stack navigator.
import { createStackNavigator } from "#react-navigation/stack";
// ... other imports
export const App = () => {
return (
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
);
}
const Stack = createStackNavigator();
const StackNavigator = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Tabs" component={TabNavigator} />
</Stack.Navigator>
);
}
const Tab = createTabNavigator();
const TabNavigator = () => {
return (
<Tab.Navigator >
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Upload" component={UploadScreen} />
<Tab.Screen name="Find" component={FindScreen} />
</Tab.Navigator>
);
}