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.
Related
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>
)}
the drawer menu setup is like below.
how can i disable click on first Drawer.Screen ?
it must show a single image!!
also how can i hide one element programmatically?
<Drawer.Navigator
initialRouteName="Feed"
drawerPosition="right"
drawerStyle={{
backgroundColor: '#c7c4b8',
width: 240,
}}
overlayColor="transparent">
<Drawer.Screen
name="Profile2"
component={Profile2}
options={{ drawerLabel: '', drawerIcon: ({ focused, size }) => (
<Image source={require('./images/3.jpg')} resizeMode={'cover'} style={{ width: '100%', height: 200 }} />)
}}
/>
<Drawer.Screen
name="Home"
component={Notifications}
options={{ drawerLabel: 'Feed' }}
/>
<Drawer.Screen
name="Routes"
component={Profile}
options={{ drawerLabel: 'Routes' }}
/>
</Drawer.Navigator>
For add header or whatever you want you should use drawerContent prop and there you can make your custom drawer or manipulate the existing one like in the example below.
For show/hide screen in a drawer programatically you can just use it with useState or pass a redux store and conditionally render it or not, remember that navigator its just a react component.
I let you a snack to check it online as well.
https://snack.expo.io/#anthowm/drawer-navigation-%7C-react-navigation
import * as React from 'react';
import {useState} from 'react';
import { View, Text, Image } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import {
createDrawerNavigator,
DrawerContentScrollView,
DrawerItemList,
DrawerItem,
} from '#react-navigation/drawer';
function Feed() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Feed Screen</Text>
</View>
);
}
function Article() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Article Screen</Text>
</View>
);
}
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<Image source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}} resizeMode={'cover'} style={{ width: '100%', height: 200 }} />
<DrawerItemList {...props} />
<DrawerItem
label="Hide Screen"
onPress={() => props.setShow(false)}
/>
</DrawerContentScrollView>
);
}
const Drawer = createDrawerNavigator();
function MyDrawer() {
const [show, setShow] = useState(true);
return (
<Drawer.Navigator drawerContent={props => <CustomDrawerContent setShow={setShow} {...props} />}>
<Drawer.Screen name="Feed" component={Feed} />
{show && <Drawer.Screen name="Article" component={Article} />}
</Drawer.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<MyDrawer />
</NavigationContainer>
);
}
How to navigate between screens using buttons on the header? When I try to go back or navigate to other pages using header buttons I get the same error.
The buttons on the screen work perfectly.
"undefined is not an object (evaluating 'navigation.navigate')
(Device)"
"undefined is not an object (evaluating 'navigation.goBack')"
import 'react-native-gesture-handler';
import React from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import Constants from 'expo-constants';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
//Screen 1
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
//Screen 2
function DetailsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details</Text>
<Button title="back home" onPress={() => navigation.goBack()} />
</View>
);
}
export default function App({ navigation, route }) {
return (
<NavigationContainer>
<Stack.Navigator initialRout="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{
headerTitle: () => <Text>Register</Text>,
headerRight: () => <Button title="Test" onPress={() => navigation.navigate('Home')} />,
headerLeft: () => <Button title="back home" onPress={() => navigation.goBack()} />,
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
https://snack.expo.io/#camileppst/navigation-test
you need to inject the navigation props, not getting it from the props of the App component:
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRout="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
name="Details"
component={DetailsScreen}
options={({navigation}) => ({
headerTitle: () => <Text>Register</Text>,
headerRight: () => <Button title="Test" onPress={() => navigation.navigate('Home')} />,
headerLeft: () => <Button title="back home" onPress={() => navigation.goBack()} />,
})}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
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>
)
}
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> );