How can we open a link from a navigator entry? For example, like:
const Home = DrawerNavigator ({
Account: { screen: Account },
Availability: { screen: Availability },
Favorites: { screen: Favorites },
Website: { screen: Linking.openURL('http://www.example.com') }, }
I know this was asked a while ago, but I've recently come across this need and found a working solution.
You want to first establish the drawer with createDrawerNavigator:
import { createDrawerNavigator, DrawerContentScrollView, DrawerItemList, DrawerItem } from '#react-navigation/drawer';
import { Linking } from 'react-native';
const Drawer = createDrawerNavigator();
With the latest version of react-native navigation, we can't pass fields to DrawerNavigator like you're doing. One way to set up the drawer and its navigation contents is like so:
return (
<Drawer.Navigator
initialRouteName="Account"
drawerPosition="right"
drawerContentOptions={{
activeTintColor: 'white',
inactiveTintColor: 'blue',
activeBackgroundColor: 'blue',
labelStyle: {
fontFamily: 'Arial',
fontSize: 18,
textTransform: 'uppercase',
paddingTop: 5
}
}}
drawerContent={props => <CustomDrawerContent {...props}/>}
>
<Drawer.Screen name="Account" component={Account} />
<Drawer.Screen name="Availability" component={Availability} />
<Drawer.Screen name="Favorites" component={Favorites} />
{/* Custom Links (defined next) */}
</Drawer.Navigator>
);
I've included my return statement to show that this is what I'm rendering on the screen. drawerContentOptions shows one of the parameters you can pass to the drawer. All options are defined here:
https://reactnavigation.org/docs/drawer-navigator/
Next, we want to create the custom drawer content that we referenced in one of the Drawer.Navigator props. You can also apply very similar props like we did earlier to the drawer navigation items. Keep in mind that all custom links are going to display below the Drawer.Screen components referenced/defined earlier. This is due to the line DrawerItemList {...props}> which takes in the defined list of navigation links and displays them before our custom links. You can reverse this so custom displays first, but I'm not sure if you can put custom links in the middle.
// set up custom links for main navigation drawer
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<DrawerItem
label="Website"
inactiveTintColor={'blue'}
labelStyle= {{
fontFamily: 'Arial',
fontSize: 18,
textTransform: 'uppercase',
paddingTop: 5
}}
onPress={() => Linking.openURL('http://www.example.com')}
/>
</DrawerContentScrollView>
);
}
Related
I have a tab bar that looks like this:
The two side buttons are stack navigators (Learn and Journal) and the middle button needs to navigate the Journal Stack, and depending on what screen in the Journal Stack the user is on, it needs to say and do different things.
const Tab = createBottomTabNavigator();
const TabBarIcon = ({ icon, title, focused }) => {
return (
<View style={styles.iconContainer}>
<FontAwesomeIcon
icon={icon}
color={focused ? Colors.neutral[4] : Colors.neutral[6]}
size={24}
style={styles.icon}
/>
<Text style={[styles.iconText, focused && styles.iconTextFocused]}>
{title}
</Text>
</View>
);
};
const NullScreen = () => null;
const TabNavigator = () => {
return (
<Tab.Navigator
initialRouteName="Journal"
screenOptions={({ route }) => ({
...defaultNavOptions,
headerShown: false,
tabBarStyle: { backgroundColor: Colors.neutral[3] },
tabBarShowLabel: false,
})}
>
<Tab.Screen
name="Learn"
component={LearnStackNavigator}
options={{
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
title={'Learn'}
icon={faUserGraduate}
/>
),
}}
/>
<Tab.Screen
name="Null Screen"
component={NullScreen}
options={{
tabBarButton: ({ focused }) => (
<View
style={{
position: 'relative',
bottom: 25,
width: 80,
height: 80,
borderRadius: '50%',
backgroundColor: 'grey',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
shadowColor: 'black',
shadowOpacity: 0.3,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 3,
}}
>
<TouchableOpacity onPress={() => Alert.alert('hello world')}> // This is the button that I want use for useful things
<View style={[styles.iconContainer, styles.paddingBottom10]}>
<FontAwesomeIcon
icon={faPlus}
color={focused ? Colors.neutral[4] : Colors.neutral[6]}
size={32}
/>
<Text style={styles.iconText}>{'Add Sport'}</Text>
</View>
</TouchableOpacity>
</View>
),
}}
/>
<Tab.Screen
name="Journal"
component={LogbookStackNavigator}
options={{
tabBarIcon: ({ focused }) => (
<TabBarIcon focused={focused} title={'Journal'} icon={faPenAlt} />
),
}}
/>
</Tab.Navigator>
);
};
And here is what the LogbookStackNavigator looks like:
const LogbookStack = createStackNavigator();
const LogbookStackNavigator = () => {
return (
<LogbookStack.Navigator
screenOptions={{
...defaultNavOptions,
headerBackTitleVisible: false,
}}
>
<LogbookStack.Screen
name="Screen1"
component={screen1Component}
options={defaultNavOptions}
/>
<LogbookStack.Screen
name="Screen2"
component={screen2Component}
options={defaultNavOptions}
/>
<LogbookStack.Screen
name="Screen3"
component={screen3Component}
options={entryScreenOptions}
/>
<LogbookStack.Screen
name="Screen4"
component={screen4Component}
options={SaveLogbookScreenOptions}
/>
<LogbookStack.Screen
name="Screen5"
component={screen1Component5}
options={defaultNavOptions}
/>
</LogbookStack.Navigator>
);
};
I know how to use navigation.setOptions, but it only affects the immediate parent navigator, not the grandparent navigator.
Another thing I tried was to make the big circle button on the page itself, but it always rendered underneath the Tab Navigator. If there was a way to make it render above, I think I could just use that. I tried 'position: 'absolute', etc and it always rendered underneath the tab navigator. As it is, I had to basically make a dummy screen in the tab navigator to give me the button on top.
What I need to be able to do, is use big circle button on the Tab Navigator, to navigate to different screens in the LogbookStackNavigator. How do I do that?
Also, I need the title to change from "Add Sport" to "Add " depending on what screen the LogbookStackNavigator is on. How do I do that?
Thanks for your help
Finally figured this out. You have to use react-native-portalize. Just wrap the elements you want to be rendered on top in a
<Portal></Portal>. This will place it above a Bottom Tab navigator.
import { Portal } from 'react-native-portalize';
const FooterButton = () => {
return(
<Portal>
<View>
<Text>I appear above the Tab Navigator!</Text>
</View>
</Portal>
);
export default FooterButton;
Don't forget to wrap the whole app in the the Host:
//In app.js
import { Host } from 'react-native-portalize';
const App = () => {
return (
<Host>
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
</Host>
)
}
export default App;
NOTE: The elements inside the Portal, do not clear when the navigator navigates to another screen. So to get around this, you have to only display the Portal, when the screen is active. Thankfully React Navigation 5+ provides a useIsFocused hook that accomplishes this perfectly.
import { Portal } from 'react-native-portalize';
import { useIsFocused } from '#react-navigation/native';
const FooterButton = () => {
const isFocused = useIsFocused();
// Only display the button when screen is focused. Otherwise, it doesn't go away when you switch screens
return isFocused ? (
<Portal>
<View style={styles.buttonContainer}>
<View style={styles.footer}>{props.children}</View>
</View>
</Portal>
) : null;
};
export default FooterButton;
If you want a modal-style popup, you can wrap react-native-modalize and wrap it with react-native-modalize
Thanks to livin52 on Reddit for the solution
I get this error while trying to navigate between screens with react native stack navigator. I think this was working in my previous project which had react 17.0.1 and react native 0.64.2, this project is react 17.0.2 and react native 0.66.4, helps would be appreciated.
log
Warning: Cannot update a component (`ForwardRef(BaseNavigationContainer)`) while rendering a different component (`Home_Profile`). To locate the bad setState() call inside `Home_Profile`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
error comes when I try to call navigation in an onPress prop on a Flatlist render item component.
renderItem={({ item }) => (
<View style={{ backgroundColor: "#f6f6f6" }}>
<PostCard
item={item}
onPress={() => navigation.navigate("Home_Profile")}
/>
</View>
)}
const PostCard = ({ item, onPress }) => {
React.useEffect(() => {
async function fetchUserData() {
const data = await getUserData(item.userid);
setProfileimg(data.userimg);
setName(data.name);
setLocation(data.location);
}
fetchUserData();
}, []);
return (
<TouchableOpacity onPress={onPress}>
<Text
style={{
color: "#231454",
fontWeight: "bold",
fontSize: 15,
marginBottom: 3,
}}
>
{name}
</Text>
</TouchableOpacity>
)
};
export default PostCard;
Home_Profile
import React from "react";
import { View, Text, Button } from "react-native";
const Home_Profile = ({ navigation }) => {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Home_Profile.js</Text>
<Button title="back" onPress={navigation.navigate("Home")} />
</View>
);
};
export default Home_Profile;
I found the cause of the problem, when you render the second screen, it is executing the onPress method of the button that goes back home, and that navigation causes the render method in that home screen to execute, which means you are trying to execute two renders at the same time, to fix that, just add an arrow function to the navigate home function as shown bellow:
onPress={()=>{navigation.navigate("Home")}}
You have forgot to add an arrow function before navigation.navigation('nameOFComponent')
guys I have a question about navigation in react native.
So I mainly use TabNavigator. I have 2 main stack navigators in the app
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Profile" component={ProfileStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
In my ProfileStack screen, I have also two pages: MyProfile and UsersProfile:
const ProfileStack = createNativeStackNavigator();
function ProfileStackScreen({route, navigation}) {
return (
<ProfileStack.Navigator initialRouteName="MyProfile">
<ProfileStack.Screen name="MyProfile" component={MyProfilePage} />
<ProfileStack.Screen name="UserProfile" component={UserProfilePage} options={{
headerLeft: () => (<View>
<Button title="back" onPress={() => {navigation.goBack()}}/>
</View>)
}}/>
</ProfileStack.Navigator>
);
}
Now I want to navigate from the HomeScreen to the UserProfilePage and pass params to this screen. I'm doing it like this:
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen this</Text>
<Button
title="Go to another user profile"
onPress={() => navigation.navigate('Profile', {screen: 'UserProfile', params: {userId: 1235}})}
/>
</View>
);
}
So now, when I come to the page UserProfile I see that it loads also the Profile page, and just for a second I see blinking of ProfilePage and its UI that is not cool, and should it be like so? I guess not.
Then if I press the BACK button on UsersProfilePage, I'm navigating back to HomeScreen - and this is ok! This is what I expect!
But now, If I will press ProfileTab I see only UsersProfilePage but not MyProfilePage. When I press the BACK button again, I go back to the HomeScreen that is weird for me. Can u explain why it happens? Why I don't get back to the MyProfilePage.
I prepared an expo snack here. You can reproduce this behavior.
This is because you’re navigating to a screen in a nested navigator. It is ignoring the initial route. Then when you press the tab again it is still mounted and will still have the previous route state, which is just the screen without the initial screen.
By default, when you navigate a screen in the nested navigator, the specified screen is used as the initial screen and the initial route prop on the navigator is ignored. This behaviour is different from the React Navigation 4.
If you need to render the initial route specified in the navigator, you can disable the behaviour of using the specified screen as the initial screen by setting initial: false:
navigation.navigate('Root', {
screen: 'Settings',
initial: false,
});
See https://reactnavigation.org/docs/nesting-navigators/#rendering-initial-route-defined-in-the-navigator and https://reactnavigation.org/docs/navigation-lifecycle/
Here's a video showcasing all my visible current bottom-tab-items: Home, My Account, Cart and Menu. https://streamable.com/no6anz
I have other bottom-tab-items I want to render on the screen but not be visible in the bottom tab bar itself.(For example: SettingsView)
How do I achieve this using react native navigation v5?
just on the element (Tab.Screen) you don't want to show, render a null tabBarButton.
<Tab.Screen
name="SignIn"
component={SignInScreen}
options={{
tabBarButton: (props) => null, //like this
tabBarStyle: { display: 'none' }, //this is additional if you want to hide the whole bottom tab from the screen version 6.x
}}
/>
I've solved my own question:
<Tab.Navigator
tabBarOptions={{
activeTintColor: '#161718',
inactiveTintColor: '#ffffff',
style: {
backgroundColor: '#161718',
paddingTop: 10,
borderTopColor: '#161718',
},
labelStyle: {
textAlign: 'center',
top: 8,
},
}}
screenOptions={({route}) => ({
tabBarButton: ['Contact', 'Route2ToExclude'].includes(route.name)
? () => {
return null;
}
: undefined,
})}>
As you can see i'm using screenoptions to define which routes to exclude from the bottom tab bar. Note these routes do need to be an actual screen within the <tab.navigator> component.
React Navigation Bottom Tab Navigation github issue link
https://github.com/react-navigation/react-navigation/issues/5230#issuecomment-595846400
I'm using react navigation in a react native project and I want to customize the header with an image.
For a color I can use simple styling, but since react native doesn't support background images I need a different solution.
Update:
Since v2 of the library there's an special option for setting the header background, namely headerBackground.
This option accepts a React component, so when set to an Image component, it will use that.
For example:
export default createStackNavigator({
Home: {
screen: HomeScreen
},
}, {
navigationOptions: {
headerBackground: () => (
<Image
style={StyleSheet.absoluteFill}
source={{ uri: 'https://upload.wikimedia.org/wikipedia/commons/3/36/Hopetoun_falls.jpg' }}
/>
),
}
});
Working example: https://snack.expo.io/#koen/react-navigation-header-background
Old answer, for when still using React Navigation v1:
Creating a custom header with an image is actually really simple.
By wrapping the Header with a view and placing an absolute positioned image in that view, the image will scale to its parent size.
Important is to set the backgroundColor of the default header to transparent.
const ImageHeader = props => (
<View style={{ backgroundColor: '#eee' }}>
<Image
style={StyleSheet.absoluteFill}
source={{ uri: 'https://upload.wikimedia.org/wikipedia/commons/3/36/Hopetoun_falls.jpg' }}
/>
<Header {...props} style={{ backgroundColor: 'transparent' }}/>
</View>
);
And then use that component as header:
const SimpleStack = StackNavigator({
Home: {
screen: MyHomeScreen,
},
}, {
navigationOptions: {
headerTitleStyle: { color: '#fff' },
header: (props) => <ImageHeader {...props} />,
}
});
Which would result in:
According to the official docs of react-navigation v5, it can be implemented as follows:
https://reactnavigation.org/docs/headers/#replacing-the-title-with-a-custom-component
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
// title: 'App Name'
options={{
headerTitle: (props) => ( // App Logo
<Image
style={{ width: 200, height: 50 }}
source={require('../assets/images/app-logo-1.png')}
resizeMode='contain'
/>
),
headerTitleStyle: { flex: 1, textAlign: 'center' },
}}
/>
</Stack.Navigator>
Update for React Navigation v5! (making this post for future references)
For react navigation 5, I found this solution.
In StackNavigator.js class you can set a different image for each page (Stack.Screen):
<Stack.Screen
name='Home'
component={HomeScreen}
options={{
title: <Image style={{ width: 250, height: 50 }}
source = require('../images/yourimage.png')}/>
}}
/>
Then, you must adjust width, height, and position of the image, but it works! I think it's the simpliest way. Here's the output (yes, it's my image, before adjustments).
Don't forget to import Image!
import { Image } from 'react-native'