React navigation v5, add a stack navigation to my application header - react-native

I'm using react navigation 5 to build my app navigation system.
I want to add a link to open Notifications scree:
I created a RenderHeaderRight component to override the right component of all my stacks
navigation :
const RenderHeaderRight = ({navigation}) => {
return (
<View style={{flexDirection: 'row-reverse'}}>
<TouchableHighlight
underlayColor={COLORS.DEFAULT}
style={styles.iConMenuContainer}
onPress={() => navigation.openDrawer()}>
<Image source={menu} style={styles.iConMenu} />
</TouchableHighlight>
<TouchableHighlight
underlayColor={COLORS.DEFAULT}
style={styles.notificationsIconContainer}
onPress={() => navigation.navigate('Notifications')}>
<>
<Image source={notifications} style={styles.notificationsIcon} />
<Image source={notifMark} style={styles.badge} />
</>
</TouchableHighlight>
</View>
);
};
In my HomeStackScreen i'm using the RenderHeaderRight :
const HomeStackScreen = ({navigation}) => (
<HomeStack.Navigator
initialRouteName="Home"
headerMode="screen"
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: COLORS.WHITE,
elevation: 0, // remove shadow on Android
shadowOpacity: 0, // remove shadow on iOS
borderBottomWidth: 0,
},
headerTintColor: COLORS.GREY,
headerTitleStyle: {
fontFamily: 'Montserrat-SemiBold',
fontWeight: '600',
fontSize: 18,
},
}}>
<HomeStack.Screen
name="Home"
component={Home}
options={{
title: 'Expanded',
headerLeft: () => <RenderHeaderLeft />,
headerRight: () => <RenderHeaderRight navigation={navigation} />,
headerTitleAlign: 'left',
}}
/>
<HomeStack.Screen name="HomeDetails" component={HomeDetails} />
</HomeStack.Navigator>
);
When i click on the Header right button to open the Notifications screen i got an error :
The action 'NAVIGATE' with payload {"name":"Notifications"} was not handled by any navigator.
Where shoud i create the stack navigation of Notifications screen ?
I triend to add the notifications like this :
const HomeStackScreen = ({navigation}) => (
<HomeStack.Navigator
initialRouteName="Home"
headerMode="screen"
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: COLORS.WHITE,
elevation: 0, // remove shadow on Android
shadowOpacity: 0, // remove shadow on iOS
borderBottomWidth: 0,
},
headerTintColor: COLORS.GREY,
headerTitleStyle: {
fontFamily: 'Montserrat-SemiBold',
fontWeight: '600',
fontSize: 18,
},
}}>
<HomeStack.Screen
name="Home"
component={Home}
options={{
title: 'Expanded',
headerLeft: () => <RenderHeaderLeft />,
headerRight: () => <RenderHeaderRight navigation={navigation} />,
headerTitleAlign: 'left',
}}
/>
<HomeStack.Screen name="HomeDetails" component={HomeDetails} />
<HomeStack.Screen
name="Notifications". // add the screen here
component={Notifications}
options={{headerShown: false}}
/>
</HomeStack.Navigator>
);

Removed the unnecessary styling / images
From my understanding you need to have a root Drawer.Navigator and from inside your Home screen, you need a Stack.Navigator there
This article explains the details of combining react-native-navigators
import React from "react"
import { TouchableHighlight, View, Text } from "react-native"
import { NavigationContainer } from "#react-navigation/native"
import { createDrawerNavigator } from "#react-navigation/drawer"
import { createStackNavigator } from "#react-navigation/stack"
const RenderHeaderRight = ({ navigation }) => {
return (
<View style={{ flexDirection: "row-reverse" }}>
<TouchableHighlight onPress={() => navigation.openDrawer()}>
<View>
<Text>Menu</Text>
</View>
</TouchableHighlight>
<TouchableHighlight onPress={() => navigation.navigate("Notifications")}>
<>
<View>
<Text>Image 1</Text>
</View>
<View>
<Text>Image 2</Text>
</View>
</>
</TouchableHighlight>
</View>
)
}
const Stack = createStackNavigator()
const Drawer = createDrawerNavigator()
const Notifications = () => (
<View>
<Text>Notifications</Text>
</View>
)
const Home = () => (
<View>
<Text>Home</Text>
</View>
)
const NotificationsScreen = () => (
<View>
<Text>Notifications Screen</Text>
</View>
)
const HomeScreen = () => (
<View>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={({ navigation }) => ({
title: "Home",
headerRight: () => <RenderHeaderRight navigation={navigation} />,
})}
navigationOptions={({ navigation }) => ({
headerTitleAlign: "left",
})}
/>
<Stack.Screen name="Notifications" component={Notifications} />
</Stack.Navigator>
</View>
)
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
</NavigationContainer>
)
}

Related

I can't go back to previous screen with navigation.goBack() in react-native

I have the Profile tab with it's children. From ChildA I navigate to ChildB, but when I'm trying to go back to Profile, it won't work. I have tried navigation.goBack(null) and useNavigation() hook but without any luck. This is the code I have so far:
ChildA
const ChildA = ({navigation}) =>{
const onClick = (item) =>{
navigation.navigate("ChildB",{
photo:item.uri
})
}
return (
<View style={styles.container}>
<FlatList
data={images}
numColumns={2}
showsVerticalScrollIndicator={false}
style={styles.list}
contentContainerStyle={{ alignItems: 'center', }}
renderItem={({ item }) => (
<TouchableOpacity onPress={()=>onClick(item)}>
<Image
source={item}
style={styles.listItem}
keyExtractor={(item) => item.key}
/>
</TouchableOpacity>
)}
/>
</View>
)
}
ChildB
const ChildB = ({ navigation, route }) => {
console.log(route.name)
const {photo} = route.params
const DEVICE = Dimensions.get("screen")
return (
<View style={styles.container}>
<BackButton onPress={()=>navigation.goBack()} />
<Image source={{ uri: photo }} style={{ width: DEVICE.width, height: DEVICE.height / 2 }} />
<Text>This is the comments section</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
position: 'absolute'
}
})
App.jsx
function Navigation(): JSX.Element {
return (
<Tab.Navigator
shifting={false}
activeColor="#315399"
// inactiveColor="#3e2465"
barStyle={{ backgroundColor: "white" }}>
<Tab.Screen
name="TabA" />
),
}}
component={TabA}
/>
<Tab.Screen
name="TabB" />;
},
}}
component={TabB}
/>
<Tab.Screen
name="Profile" />
),
}}
component={Profile}
/>
</Tab.Navigator>
);
}
const Tab = createMaterialBottomTabNavigator();
const Stack = createStackNavigator();
function App(): JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
{/* Auth Navigator: Include Login and Signup */}
<Stack.Screen
name="Login"
component={Login}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Register"
component={Register}
options={{headerShown:false}}
/>
<Stack.Screen name="ChildB"
component={ChildB}
options={{headerShown:false}}/>
<Stack.Screen
name="ForgotPassword"
component={ForgotPassword}
options={{headerShown:false}}
/>
<Stack.Screen
name="Navigation"
component={Navigation}
// Hiding header for Navigation Drawer
options={{headerShown:false}}
// options={({ route }) => ({
// headerTitle: getHeaderTitle(route),
// })}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
How can I go back from ChildB to Profile? I'm new to react-native so any advice would be appreciated!
I have managed to make it work by passing the navigation as prop instead of the simple function prop I was using.Since I was using a customised back button, the prop function wasn't triggered from screen to stack, only between screens(e.g from Register back to Login).
BackButton before:
export default function BackButton({goBack}) {
return (
<TouchableOpacity onPress={goBack} style={styles.container}>
<FontAwesomeIcon icon={faArrowLeft}/>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 10 + getStatusBarHeight(),
left: 4,
zIndex:1
},
image: {
width: 24,
height: 24,
},
})
and used it in another component by passing the prop navigation to that component and then passing the navigation.goBack to the goBack from BackButton:
const componentA = ({navigation})=>{
return(
<View>
<BackButton goBack={navigation.goBack}
</View>
)}
BackButton working:
export default function BackButton({navigation}) {
return (
<TouchableOpacity onPress={navigation.goBack} style={styles.container}>
<FontAwesomeIcon icon={faArrowLeft}/>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 10 + getStatusBarHeight(),
left: 4,
zIndex:1
},
image: {
width: 24,
height: 24,
},
})
and Component:
const componentA = ({navigation})=>{
return(
<View>
<BackButton navigation={navigation}
</View>
)}

How to solve navigation.navigate error when set TouchableOpacity in StackScreen header options?

I want to add a button(icon) on stack header(on right side). On-click it goes to that page but it's not working. It appears 'undefined is not an object (evaluating 'navigation.navigate')'.
Below is my code:
<Stack.Navigator>
<Stack.Screen name="Page1" component={Page1} />
<Stack.Screen
name="Page2"
component={Page2}
options={{
headerRight: () => (
<View>
<TouchableOpacity
onPress={() => navigation.navigate('Page3')}>
<Image source={require('../assets/image.png')} />
</TouchableOpacity>
</View>
)
}}
/>
<Stack.Screen name="Page4" component={Page4} />
<Stack.Screen name="Page5" component={Page5} />
</Stack.Navigator>
You can pass the navigation from the options to the headerRight:
options={({navigation}) => ({
headerRight: () => (
...
),
})}
or useNavigation():
const navigation = useNavigation();
EDIT 2:
fixed your snack code and its working fine:
You had to add a stackScreen called 'MyorderStack' because you're trying to navigate to that.
<NavigationContainer independent={true}>
<Stack.Navigator screenOptions={{ headerTintColor: 'blue' }}>
<Stack.Screen name="Global Page" component={AppNavigator} options={{ headerShown: false }} />
<Stack.Screen name="DetailOne" component={DetailOne} options={({navigation}) => ({ headerBackTitleVisible: false, title: 'Global Page',
headerRight: () => (
<View style={{flexDirection: 'row',justifyContent: 'flex-end',width: '50%'}}>
<View style={{ marginRight: 10 }}>
<TouchableOpacity onPress={() => navigation.navigate('MyorderStack')}>
<Image source={require('./assets/shop.png')} style={styles.Image} />
</TouchableOpacity>
</View>
</View>
), headerTitleAlign:'center', headerTintColor:colors.primary
})}
/>
<Stack.Screen name="DetailTwo" component={DetailTwo} options={{headerBackTitleVisible: false, headerTitleAlign: 'center', title: 'Global Page', headerTintColor: colors.primary}} />
<Stack.Screen name="MyorderStack" component={MyorderStack} options={{headerBackTitleVisible: false, headerTitleAlign: 'center', title: 'Global Page', headerTintColor: colors.primary}} />
</Stack.Navigator>
</NavigationContainer>
Navigation is only defined within the screen's components. In your case, you can try useNavigation hook to navigate to different screen. Import it like:
import { useNavigation } from '#react-navigation/native';
and declare it like:
const navigation = useNavigation();
Then you it to your TouchableOpacity prop like onPress={() => navigation.navigate('Page3')}.
Hope this works for you. Thanks

React Stack Navigator V5 does not response when headerMode="float"

im useing react-native-drawer v5, and in the header i have hamburger icon.
when headerMode!=float -> its worked like expected and open and close the drawer.
when i change to headerMode="float", the header is not response to the click, and nothing happend,
note that im checking it on Android device. I dont need to know about IOS..
here my stack and drawer nav:
import...
const MainStack = createStackNavigator();
const Drawer = createDrawerNavigator();
const stackScreenOptions = {
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
gestureEnabled: true,
gestureDirection: "horizontal",
headerTitleAlign: "center",
headerStyle: {
borderBottomWidth: 0.5,
elevation: 2,
borderColor: "white",
backgroundColor: "#1d2731",
height: 82,
},
headerTitleStyle: {
color: "white",
},
};
const MainStackScreen = ({ navigation }) => (
<MainStack.Navigator headerMode="float" screenOptions={stackScreenOptions}>/////////HERE THE PROBLEM OCCURE -> when i declare headerMode="float
<MainStack.Screen
name="Main"
component={MainScreen}
options={{
title: "ליאור",
headerLeft: () => (
<Icon
style={{ position: "absolute", left: 15 }}
name="bars"
color="white"
size={30}
onPress={() => {
navigation.openDrawer();
}}
/>
),
}}
/>
<MainStack.Screen name="MeetingPicker" component={MeetingPicker} />
<MainStack.Screen name="MyMeetings" component={UserHistory} />
<MainStack.Screen name="About" component={AboutScreen} />
<MainStack.Screen name="Gallery" component={GalleryScreen} />
<MainStack.Screen name="Location" component={LocationScreen} />
<MainStack.Screen name="Logout" component={LogoutModal} />
</MainStack.Navigator>
);
export const DrawerNavigator = () => {
return (
<Drawer.Navigator drawerStyle={styles.drawerStyle} drawerContent={props => <DrawerContent {...props} />}>
<Drawer.Screen name="Main" component={MainStackScreen} />
</Drawer.Navigator>
);
};

react navigation 5 header is not shown

Using react navigation 5, I want to create a dynamic map for all my Drawer Screens, but the header is not shown with code:
<NavigationContainer>
<Drawer.Navigator
drawerContent={props => <DrawerContent {...props} />}>
{stackNavigItens.map((props, r) => (
<Drawer.Screen
key={r.name}
name={r.name}
component={({navigation}) => (
<Stack.Navigator
initialRouteName="Home"
headerMode="screen"
screenOptions={{
headerTitle: r.label,
headerStyle: {
backgroundColor: '#2e72e8',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
{...props}>
<Stack.Screen
name={r.name}
component={r.component}
options={{
title: r.label,
headerLeft: () => (
<Icon.Button
name="ios-menu"
size={25}
backgroundColor="#2e72e8"
onPress={() => {
navigation.openDrawer();
}}
/>
),
}}
{...props}
/>
</Stack.Navigator>
)}
{...props}
/>
))}
</Drawer.Navigator>
</NavigationContainer>
If I use every createStackNavigator in a const like below, and then call inside component of the Drawer the header shows correctly, I don't know Why ? I think maybe because of the {navigation} arrow function, but don't work too.
const HomeStackScreen = ({navigation}) => (
<HomeStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#2e72e8',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}>
<HomeStack.Screen
name="Home"
component={HomeScreen}
options={{
headerLeft: () => (
<Icon.Button
name="ios-menu"
size={25}
backgroundColor="#2e72e8"
onPress={() => {
navigation.openDrawer();
}}
/>
),
}}
/>
</HomeStack.Navigator> );

navigation.setOptions not change

i'm creating screens with react-navigator but i can not change the options of the Header Bar when screen opened
My code (App.js):
function App() {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{
headerTitleAlign: 'center',
headerTitle: props => <LogoTitle {...props} />,
headerRight: props => <HeaderRight {...props} />,
headerStyle: {
backgroundColor: '#F46A0D'
},
headerTintColor: '#fff',
}}>
<Stack.Screen name="Search" component={SearchScreen}/>
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
My SearchScreen.js:
import React from 'react'
import { Text, View } from 'react-native'
import { Button } from 'react-native-paper'
const SearchScreen = ({ navigation }) => {
navigation.setOptions = {
headerTitle: props => <TextInput {...props} placeholder="Search" placeholderTextColor="#FFFF" style={{
fontSize: 24
}}></TextInput>,
headerRight: props => <FontAwesome5 {...props} name={'search'} size={22} color="white" style={{ marginRight: 15 }}></FontAwesome5>
}
return (
<View>
</View>
)
}
export default SearchScreen;
in the navigation.setOptions line nothing happens and the default screenOptions Stack.Navigator continues
on route file
<Stack.Screen
name="FuncName"
component={FuncName}
options={{
headerTitle:''
}}/>
on Press go to FuncName screen
onPress={()=>{
props.navigation.navigate("FuncName",{title:"title"})
}}
then change header by props title
const FuncName = (props) => {
useEffect(()=>{
props.navigation.setOptions({ headerTitle: props.route.params.title })
},[])
}
Try changing this
navigation.setOptions = {
headerTitle: props => <TextInput {...props} placeholder="Search" placeholderTextColor="#FFFF" style={{
fontSize: 24
}}></TextInput>,
headerRight: props => <FontAwesome5 {...props} name={'search'} size={22} color="white" style={{ marginRight: 15 }}></FontAwesome5>
}
to this:
navigation.setOptions({
headerTitle: props => <TextInput {...props} placeholder="Search" placeholderTextColor="#FFFF" style={{
fontSize: 24
}} />,
headerRight: props => <FontAwesome5 {...props} name={'search'} size={22} color="white" style={{ marginRight: 15 }}></FontAwesome5>
})
In my case I was able to make it possible to update "gestureEnabled" option in runtime using React lifecycle.
Each of my navigators was created as function that subscribes to UiContext value via hook.
In case of navigators re-rendering on dependency change, I was able to disable "swipe back" functionality on specific nested screen easily.