How to implement a button on the left side of the header in react-navigation - react-native

I have a main Stack navigator(X) and inside that I have screen which takes me to another stack navigator(A). But when I go stack(A) it's not showing me the button to go back to stack(X). I do understand that using multiple stack navigators like these is not the best practice, but I want to implement a Tab navigator inside of stack(A) and each of the tabs would contain a stack navigator.
I have tried implementing a button as described on the react-navigation docs, but it doesn't describe how to implement it on the left side of the header.
How do I get around this?

You can use the same code provided in the docs only change is that instead of headerRight you have to use headerLeft but this will also change the back behavior, check the docs for more details.
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerTitle: props => <LogoTitle {...props} />,
headerLeft: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}}
/>

Related

Share Screens between Tab Navigator and Drawer Navigator while preserving state

My app has 3 main screens (each of which is a Stack Navigator):
MessagesStack
ExpensesStack
CalendarStack
It also has 2 supplemental screens (again, both stacks):
DashboardStack
DocumentsStack
I want to combine a drawer navigator and a tab navigator, but with some overlap. In particular, what I'm trying to achieve is for the drawer navigator to include links to all of the stacks, while the bottom tab navigator contains links only to the three main stacks. The tab navigator should always be visible, on every screen - essentially a sticky footer that enables the user to quickly jump to any of the three main screens. So the layout is like this:
--- Messages
--- Expenses
--- Calendar
--- Dashboard
--- Documents
| Messages | Expenses | Calendar |
Pressing the Messages item in the drawer nav should take the user to the same view as pressing the Messages item from the tab nav. It's important that the state of that view be preserved. In particular, suppose the user opened a specific message in the messages view. This takes them to a MessageDetail screen inside the Messages stack. If they navigate away from the Messages stack and then navigate back, they should still see the MessageDetail screen no matter what navigation route they took.
I've tried a few approaches, but none of them quite work. The closest I got was something like this:
function BottomTabNavigator({ initialRouteName }) {
return (
<BottomTab.Navigator initialRouteName={props.initialRouteName}>
<BottomTab.Screen name="MessagesTab" component={MessagesStackNavigator} />
<BottomTab.Screen name="ExpensesTab" component={ExpensesStackNavigator} />
<BottomTab.Screen name="CalendarTab" component={CalendarStackNavigator} />
<BottomTab.Screen
name="DashboardTab"
component={DashboardStackNavigator}
options={{
tabBarShowLabel: false,
headerShown: false,
tabBarButton: () => <></>,
}}
/>
<BottomTab.Screen
name="DocumentsTab"
component={DocumentsStackNavigator}
options={{
tabBarShowLabel: false,
headerShown: false,
tabBarButton: () => <></>,
}}
/>
</BottomTab.Navigator>
);
}
Then my root navigator was like this:
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen
name="MessagesDrawer"
options={{ title: "Messages" }}
children={() => <BottomTabNavigator initialRouteName="MessagesTab" />}
/>
<Drawer.Screen
name="ExpensesDrawer"
options={{ title: "Expenses" }}
children={() => <BottomTabNavigator initialRouteName="ExpensesTab" />}
/>
<Drawer.Screen
name="CalendarDrawer"
options={{ title: "Calendar" }}
children={() => <BottomTabNavigator initialRouteName="CalendarTab" />}
/>
<Drawer.Screen
name="DashboardDrawer"
options={{ title: "Dashboard" }}
children={() => <BottomTabNavigator initialRouteName="DashboardTab" />}
/>
<Drawer.Screen
name="DocumentsDrawer"
options={{ title: "Documents" }}
children={() => <BottomTabNavigator initialRouteName="DocumentsTab" />}
/>
</Drawer.Navigator>
</NavigationContainer>
Unfortunately, this creates 5 independent TabNavigators which don't share state. So if e.g. the user navigates to Drawer(Messages) -> Tab(Calendar), then navigates away, the next time they tap on Drawer(Messages) it will take them to the Calendar tab! If I set unmountOnRender={true} it solves that problem: tapping Drawer(Messages) will now always open the Messages tab. But the stack state is now lost, so when e.g. the user opens a message, navigates away, and comes back, the message is no longer open.
If it's possible to achieve what I'm trying to do here declaratively that would be ideal, but even if the only way is imperative (e.g. with some navigation.jumpTo calls inside effects in each screen component) that would still be ok. I've tried some imperative approaches but nothing that works yet. Any help is greatly appreciated.

How do I add a navigation button to a React Navigation Stack header with nested Bottom Tab Navigator?

I am trying to build a mobile app in react-native and I'm having some problems setting up React Navigation.
What I want to achieve is a Bottom Tab Navigator that Navigates to the 'Home' screen and the 'Profile' Screen. From the 'Home' screen, there should be a button to navigate to the 'Settings' screen in the Header.
I have got to the point where I have a Bottom Tab Navigator that can successfully navigate between the 'Home' and 'Profile' screens, as well as a button on the header for the Settings screen using the Stack navigation header. However, I am having trouble navigating to the 'Settings' screen with this button.
My code for the Stack navigator is:
const MainStackNavigator = () => {
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen
name="Home"
component={HomeScreen}
options = { ({navigation}) => ({
title: "Home",
headerStyle: {
backgroundColor: '#ff6600',
},
headerRight: () => (
<Button
onPress={() => navigation.navigate(SettingScreen)}
title="Settings"
color="#fff"
/>
)
})}
/>
<Stack.Screen name="Settings" component={SettingScreen} />
</Stack.Navigator>
);
}
When I click on the Settings button, I get the error:
"The action 'NAVIGATE' with payload undefined was not handled by any navigator.
Do you have a screen named 'SettingScreen'?"
Upon looking for a solution to this error I found this article: Nesting Navigators
It recommends keeping nested navigators to a minimal. Is my method even the right way about going for this UI design? Is there a way to achieve this with only using one navigator?
After some time trying to solve this I found the problem was quite silly of me. navigation.navigate takes the name of the screen to navigate to, but I was giving it the component.
To fix the problem I changed
onPress={() => navigation.navigate(SettingScreen)}
to
onPress={() => navigation.navigate('Settings')}
Add this below your render method!
render () {
const { navigate } = this.props.navigation;
}
And then in the onPress
onPress={() => navigate(SettingScreen)}
Hopefully this helps

Is there a way to hide one item in a BottomTabNavigator?

I am building a project in react native. I want to use a bottom tab navigator, but the problem i have is that it automatically shows all screens in the navigator.
I want to hide one of the screens from the bar on the bottom.
Try this on your screen that needs to be hidden
const Tab = createBottomTabNavigator();
<Tab.Navigator>
...
<Tab.Screen
name="screen2"
component={screen2}
options={{ tabBarButton: () => null }}
/>
...
</Tab.Navigator>

Unable to Navigate to a StackNavigator nested in a DrawerNavigator

I started with a drawer navigation to allow me to pick a category page as a starting point. From each category page I want to create a stack navigator so that it navigates with a stack without each page displayed in the drawer. I started by adding a StackNavigator to the "product" page, but was told to move it to the app page and used a nest navigator. So far, this is what I have...
App.js ( Nested Navigators )
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="LoL"
drawerStyle={{
backgroundColor: "#212121",
}}
drawerContentOptions={{
activeTintColor: "orange",
inactiveTintColor: "white",
}}
>
<Drawer.Screen name="LoL" component={LoL} />
<Drawer.Screen name="CSGO" component={CSGO} />
<Drawer.Screen name="matchDetailsnav" component={matchDetailsNav} />
</Drawer.Navigator>
</NavigationContainer>
);
}
const matchDetailsNav = () => {
<Stack.Navigator>
<Stack.Screen name="MatchDetails" component={MatchDetails} />
</Stack.Navigator>
}
If we navigate to LoL.js we will see a list of cards to click on. I am using the following code to generate routes for each card.
{matches.map((match, index) => {
return (
<View>
<TouchableOpacity
key={index}
onPress={() => navigation.navigate('matchDetailsNav', match)}
>
<Card
.. passing some props here..></Card>
</TouchableOpacity>
</View>
This is where I started to get the following error...
The action 'NAVIGATE' with payload {** .. obj of the props im passing .. not important to question *} was not handled by any navigator.
Do you have a screen named 'MatchDetails?
If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
This was happening before I added the stack navigator but I am not sure why. I am importing the MatchDetails page into the App.js, there is not naming typo in my code, and I cannot seem to find why it doesn't see my match details page. Why is it not picking up my page?
EDIT: I just noticed that adding the nested navigator like I did in the example above put the name "matchDetailsNav" in my drawer nav as a clickable link, which is not what I want.
I simply want to gave a drawer nav and then have regular stack navigation for the pages/screens that link within the drawer nav. This way, clicking on a card living on a screen that is accesable from the Drawer Nav should use a stack nav ( allowing for the back button and sliding animation ).
I hope this edit clears up exactly what I am trying to do.

React native: How can I have multiple drawer navigator links point to screens within the same stack navigator

I am new to react native and I haven't seen this question asked by anyone or haven't found a way around this.
Using react navigation 5 with expo.
Currently I have a the following app structure:
Stack navigator inside of drawer navigator.
Example of page structure:
Drawer Navigator ( links ):
Home (RouteStack)
Screen 1
Screen 2
Screen 3
RouteStack( screens) :
Home ( initial route )
Screen 1
Screen 2
Screen 4
How can I get Screen 1/Screen 2 link in drawer navigator load RouteStack: Screen 1/Screen 2?
These links are provided to easily jump to the required screen.
Need some guidance on how to achieve this.
I have thought of the possibility of drawer inside of stack, but there are screens inside of drawer that may not be listed in the stack. Hence, went with stack inside of drawer.
I have also tried to do a navigation.navigate(route.name) inside of RouteStack
Sample code:
Drawer navigator:
<NavigationContainer>
<Drawer.Navigator drawerContent={(props, navigation) => <CustomDrawerContent {...props} {...navigation} />}>
<Drawer.Screen name="Home" component={RouteStack} />
<Drawer.Screen name="MyItems" component={RouteStack} />
<Drawer.Screen name="ContactRep" component={RouteStack} />
<Drawer.Screen name="Settings" component={SettingInfo} />
</Drawer.Navigator>
</NavigationContainer>
Stack navigator (RouteStack) looks like this:
<Stack.Navigator
initialRouteName="Home"
screenOptions={{ gestureEnabled: false, headerTitleAlign: 'auto' }}
// headerMode="float"
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: '',
headerStyle: {
backgroundColor: '#fff',
},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'bold'
},
headerLeft: props => <HeaderLeftMenu {...props} />,
headerRight: props => <HeaderRightMenu {...props} />,
headerTitle: props => <HeaderTitle {...props} />
}}
/>
<Stack.Screen
name="ContactRep"
component={ContactRep}
options={{ headerTitle: props => <HeaderTitle {...props} /> }}
/>
<Stack.Screen
name="MyItems"
component={MyItems}
options={{ headerTitle: (props, navigation) => <HeaderTitle {...props} /> }}
/>
</Stack.Navigator>
Thanks in advance and help is appreciated.
Your method is fine. But to clarify your ideas I will give you an example.
Assume I have a main Drawer. In that drawer I can navigate to 2 different screens. Inside those screens, I can navigate and do diferent things (like going to sub-screens), but never go outside the drawer.
To do this, we would have to created nested navigators. This meaning, one type of navigator if going to be inside another one. In our case of example:
<Papa Drawer>
<Screen 1 component={StackSon1}>
<Screen 2 component={StackSon2}>
<Papa Drawer>
And then StackSon1, for example, will look like this:
StackSon = () => {
return (
<Stack.Navigator>
<Stack.Screen>
<Stack.Screen>
...
)
}
React Navigation will also handle every drawer separately, meaning that you don't have to worry about the user creating an infinite chain of open screens.
Also, remember that, when we Nest navigators using a function (like I did) we must use return (or the simplified version of return with just parenthesis)
Hope it helps.