React Navigation 6 - how to close modal from navigation stack - react-native

I am trying to close a React Navigation modal from the navigation stack.
Following the official React Navigation documentation navigation.goBack''' is being used within the '''Modal Screen. This successfully closes the modal, but what I would like to achieve is closing the modal by pressing a button in the navigation stack instead of in the screen.
I have tried both of the below functions and although they work as expected in the Screen, they do not work in the stack navigator.
<Button onPress={() => navigation.pop()} title="Dismiss" />
<Button onPress={() => navigation.goBack()} title="Dismiss" />
I get the following error:
undefined is not an object (evaluating 'navigation.goBack')
How can I get the modal to close by pressing a button in the stack navigator?

I think you have misunderstood this Example.
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30 }}>This is the home screen!</Text>
<Button
onPress={() => navigation.navigate('MyModal')}
title="Open Modal"
/>
</View>
);
}
function ModalScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30 }}>This is a modal!</Text>
<Button onPress={() => navigation.goBack()} title="Dismiss" />
</View>
);
}
const RootStack = createStackNavigator();
function RootStackScreen() {
return (
<RootStack.Navigator>
<RootStack.Group>
<RootStack.Screen name="Home" component={HomeScreen} />
</RootStack.Group>
<RootStack.Group screenOptions={{ presentation: 'modal' }}>
<RootStack.Screen name="MyModal" component={ModalScreen} />
</RootStack.Group>
</RootStack.Navigator>
);
}
Note:
both HomeScreen and ModalScreen are screen not model of any kind. screenOptions is an object where you define what type of animation you want to see when user navigate to that screen.
when you set screenOptions to model:
The modal behavior slides the screen in from the bottom and allows the
user to swipe down from the top to dismiss it on iOS.
Finally let talk about your error:
undefined is not an object (evaluating 'navigation.goBack')
navigation props will only be available on those screen which are include in your navigation.
so check that screen is present or not where your accessing navigation.goBack

Related

How to use modalPresentationStyle .fullscreen in React Native navigation

I am trying to create my first React Native application. I have a login screen from which I want to navigate to a register screen if users want to sign up.
To achieve this, I was thinking of opening a modal above the first screen (login). I created the following:
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
const Stack = createStackNavigator();
function RegisterScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30 }}>Register Screen</Text>
<Button onPress={() => navigation.goBack()} title="Go back" />
</View>
);
}
function MyStack() {
return (
<Stack.Navigator>
<Stack.Group>
<Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
</Stack.Group>
<Stack.Group screenOptions={{ presentation: "modal", headerShown: false }} options={{modalPresentationStyle: 'fullScreen'}}>
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Group>
</Stack.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<MyStack />
</NavigationContainer>
);
}
This works perfectly fine, the modal shows. However, as you can see in this screenshot below, the modal is shown with modalPresentationStyle .automatic (in iOS). I want it to show as .fullScreen, so the hierarchy is not visible in the screen. Basically, I want the view to be shown from the safeArea/statusBar all the way down to the safeArea on the bottom.
How can I achieve this?
The terminology in React Native isn't the same as terminology in native apps. If you want a full-screen screen, you probably don't want a modal. If you want a vertical animation, change the animation based on docs:
screenOptions={{ cardStyleInterpolator: CardStyleInterpolators. forVerticalIOS, headerShown: false }}
https://reactnavigation.org/docs/stack-navigator/#animation-related-options
There's also no modalPresentationStyle: 'fullScreen' in documentation and Group component doesn't take an options prop.
The accepted answer states:
If you want a full-screen screen, you probably don't want a modal.
That's not necessarily true. Use
screenOptions={{ presentation: 'transparentModal' }}
or
screenOptions={{ presentation: 'fullScreenModal' }}
for a full screen modal animation you can't dismiss on iOS.

React Navigation Stack doesn't cover whole screen on desktop

I'm using React Native Web to build a simple Reddit Clone.
I came across this issue while using a StackNavigator from React Navigation.
The stack 'screen' seems to get calculated by the dimensions of the monitor, instead of the content. So when a screen before the currently navigated screen has a larger height than the current screen, the content is still visible 'behind/below' the stack screen.
My homescreen:
<View style={{ flex: 1, backgroundColor: colors.background }}>
<Button style={{ margin: 5 }} action={() => props.navigation.navigate('CreatePost')}><DText>Create post</DText></Button>
<FlatList
data={getPosts}
renderItem={renderPost}
keyExtractor={item => item.id}
/>
</View>
My createpost screen:
<View style={{ flex: 1, backgroundColor: 'red' }}>
<View style={styles.element}>
<TextInput
placeholder={'Title'}
onChangeText={setTitle}
value={getTitle}
/>
<View style={styles.editor}>
<SlateEditor setBody={setBody} />
</View>
<Button action={() => onCreate(getTitle, getBody)}><DText>Post</DText></Button>
</View>
</View>
Here is an example:
I don't want to the user to be able to scroll on the CreatePost screen.
This is fixed by updating #react-navigation/stack to 5.12.6.
See https://github.com/react-navigation/react-navigation/commit/da35085f1e3440f26eea800c892c88aec64d072f

Navigating to cart screen with stack navigation header

I am having a stack navigator that consists of 7 different screens and a cart icon is placed on the every screen header. I want to navigate by pressing that cart icon to the cart screen/page. I tried to put onPress function on the icon and in the header option also but all in vain.
Also, I want to know how the cart system will work.
Below is my single stack navigator screen
<HomeStack.Screen name="EARRINGS" component={Earring}
options={{headerRight:() => <ShoppingCartIcon /> }}
/>
Below is my ShoppingCartIcon code:
const ShoppingCartIcon = () => {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<View style={{position: 'absolute', top: -20, right: 0, width: 70, height: 70, alignItems:"center",justifyContent:"center", zIndex:2000 }}>
<Text style={{fontSize:20,fontWeight:"bold"}}>0</Text>
</View>
<AntDesign name="shoppingcart" color="black" size={24} style={{marginRight:10}} />
</View>
)
}
export default (ShoppingCartIcon)
Please help me.
You can pass the navigation prop like below
<HomeStack.Screen
name="EARRINGS"
component={Earring}
options={({ navigation }) => ({
headerRight: () => (<ShoppingCartIcon navigation={navigation}/> )
})}
/>
And you can use the navigation prop to navigate to any screen you need
const ShoppingCartIcon = ({navigation}) => {
//other code
navigation.navigate('Cartscreen')
}

React Navigation Top Bar Getting Same Height (NOT) dependent on its own components

Looking at this example from React Navigation website, between Chat and Contacts, the heights are independent of each other. However, When I tried to implement a top bar, the heights are the same. It takes the height of which ever tab has the most content in it.
But in the example, you can see in Contacts that there may be more contacts where you can scroll, and in Chat, the input field is at the bottom, making it look like that's where it stops.
Here's a screen shot from the example:
Chat tab
Contacts tab
Here's some of my code and what I'm experiencing:
<View style={{backgroundColor: 'orange'}}>
{!loading &&
!loadingProfileInfo &&
typeof data.infoByUser !== 'undefined' && (
<FlatList
data={data.infoByUser}
ListHeaderComponent={
<View>
<UserInfo />
<Tab.Navigator>
<Tab.Screen
name="Test"
component={TestComponent} />
<Tab.Screen
name="New Tab"
component={NewTabComponent}
/>
</Tab.Navigator>
</View>
}
numColumns={2}
renderItem={({item}) => (
<View>
// ...
</View>
)}
keyExtractor={item => item._id}
/>
)}
</View>
TestComponent
<View style={{backgroundColor: 'grey'}}>
<View>
<Text>Test Test</Text>
</View>
<View>
<Text>Test Test</Text>
</View>
<View>
<Text>Test Test</Text>
</View>
<View>
<Text>Test Test</Text>
</View>
<View>
<Text>Test Test</Text>
</View>
</View>
NewTabComponent
<View style={{backgroundColor: 'red'}}>
<Text>new tab</Text>
</View>
Notice how the New Tab has a big gap between the red and the orange, that's from the Test tab's height.
You are rendering your entire navigator as the FlatList 'header'. You shouldn't even use Flatlist in this component. Each screen has a different number of items, and should have its own Flatlist.
To fix this:
Remove the FlatList from your main component (with backgroundColor: 'orange'), and just render the Tab Navigator in that render function.
Inside of TestComponent and NewTabComponent, render a ScrollView (or FlatList) if you need it.
All screens (TestComponent and NewTabComponent) should have a height: '100%' or flex: 1 if you want all screens to take all up all the space of the screen, even when its real height is less than the screen height.
To have heights according to the content inside the particular tab, use Scrollview inside each tab screen. Thank me later if this works perfectly. :)
import * as React from "react";
import { ScrollView, Text, View } from "react-native";
import { createMaterialTopTabNavigator } from "#react-navigation/material-top-tabs";
function Screen1() {
return (
<ScrollView>
<View style={{ flex: 1, alignItems: "center" }}>
<Text style={{ padding: 10 }}>Screen1 </Text>
</View>
</ScrollView>
);
}
function Screen2() {
return (
<ScrollView>
<View style={{ flex: 1, alignItems: "center" }}>
<Text style={{ padding: 10 }}>Screen2</Text>
</View>
</ScrollView>
);
}
function Screen3() {
return (
<ScrollView>
<View style={{ flex: 1, alignItems: "center" }}>
<Text style={{ padding: 10 }}>Screen3 </Text>
</View>
</ScrollView>
);
}
const Tab = createMaterialTopTabNavigator();
export default function TopBarNavigator() {
return (
<Tab.Navigator>
<Tab.Screen name="Screen1" component={Screen1} />
<Tab.Screen name="Screen2" component={Screen2} />
<Tab.Screen name="Screen3" component={Screen3} />
</Tab.Navigator>
);
}
I'm having no idea why you're rendering it inside FlatList as it also inherits props from Scroll View.
<View style={{backgroundColor: 'orange'}}>
<Tab.Navigator>
<Tab.Screen
name="Test"
component={TestComponent} />
<Tab.Screen
name="New Tab"
component={NewTabComponent}
/>
</Tab.Navigator>
</View>
and move the FlatList and other business logics inside the TestComponent or the NewTabComponent.
The point is don't render Tabs inside ScrollView or FlatList or SectionList as they both inherit the props of ScrollView.
After a long research I found out that your problem is because react-native-tab-view (which is used by material-tob-tabs), has this code :
_defineProperty(this, "handleLayout", e => {
const {
height,
width
} = e.nativeEvent.layout;
if (this.state.layout.width === width && this.state.layout.height === height) {
return;
}
this.setState({
layout: {
height,
width
}
});
With option lazy={true} , and using the "New Tab" component on first tab and "Test" on second, you get the right height until you change the tab. When you press on "Test" the layout changes so the height is bigger. After changing tab the height does't change anymore because the layout stays the same ("New Tab" fits perfectly in the bigger height) and doesn't trigger handleLayout from react-native-tab-view.
Hope you can find a way to change the layout again when you change tabs.

How to add section divider in navigation drawer using react navigation

Suppose I have five items in drawer navigation. I want to add separator after three items. How to add this using react-navigation.
As mentioned vonovak, you could achieve this by using contentComponent which allows complete customization of drawer. For this you will need to create custom component which will override default drawer. Code sample:
Navigation Drawer
const NavigationDrawer = DrawerNavigator({
screen1: { screen: Screen1 },
screen2: { screen: Screen2 },
screen3: { screen: Screen3 },
}, {
contentComponent: SideMenu
})
Custom component which overrides default drawer (DrawerContainer)
class SideMenu extends React.Component {
render() {
return (
<View style={styles.container}>
<ScrollView>
<Text
onPress={() => this.props.navigation.navigate('Screen1')}
style={styles.item}>
Page1
</Text>
// 'separator' line
<View
style={{
borderBottomColor: 'black',
borderBottomWidth: 1
}}/>
<Text
onPress={() => this.props.navigation.navigate('Screen2')}
style={styles.item}>
Page2
</Text>
<Text
onPress={() => this.props.navigation.navigate('Screen3')}
style={styles.item}>
Page3
</Text>
</ScrollView>
</View>
);
}
}
You'll need to use the contentComponent prop to make custom changes. Check out the docs
If you are using DrawerItem component you can use itemStyle prop to add style as follows
const props = {
itemStyle: {
borderBottomWidth: 1,
borderColor: '#E2E4E8',
}
}
<DrawerItems {...props} />
You can also modify the style of container containing all items with
itemsContainerStyle prop.
Check official docs here.
3.x: https://reactnavigation.org/docs/3.x/drawer-navigator#contentoptions-for-draweritems
4.x: https://reactnavigation.org/docs/4.x/drawer-navigator#contentoptions-for-draweritems
5.x: DrawerContent is default drawer Item from v.5
You can pass props to it using drawerContentOptions object in Drawer.Navigator component.
https://reactnavigation.org/docs/5.x/drawer-navigator#drawercontentoptions
You can also pass custom drawer component using drawerContent prop.
https://reactnavigation.org/docs/5.x/drawer-navigator#drawercontent
6.x: Custom component can be added using drawerContent prop
https://reactnavigation.org/docs/drawer-navigator#drawercontent
Style to drawerItem is changed to
https://reactnavigation.org/docs/drawer-navigator#draweritemstyle
Just add this code:
const Seperator = () => <View style={styles.separator} />;
at the top of your code that you want to have the section divider/separator on. If it's a navigation drawer, menu, categories or etc.
Add this styling prop:
separator: {
marginVertical: 8,
borderBottomColor: "#737373",
borderBottomWidth: StyleSheet.hairlineWidth
}
Add section divider/separator between each section, menu, category block of code that you want to separate them like this:
//Block of code of first section/menu/category starts from here
<Icon.Button
name="th-large"
raised={true}
backgroundColor="#ffa500"
size={30}
onPress={() => {
Linking.openURL("https://www.buymeacoffee.com/splendor");
}}
>
<Text style={{ fontSize: 15 }}>
Herat: The "Academy" of Prince Bay Sunghur (1420-1433)
</Text>
</Icon.Button>
**<Seperator />**
//Block of code of first section/menu/category ends here
//Block of code of second section/menu/category starts from here
<Icon.Button
name="th-large"
raised={true}
backgroundColor="#ffa500"
size={30}
onPress={() => {
Linking.openURL("https://www.buymeacoffee.com/splendor");
}}
>
<Text style={{ fontSize: 15 }}>
Venice, Istanbul, and Herat (15th Century)
</Text>
</Icon.Button>
**<Seperator />**
//Block of code of second section/menu/category ends here
//Block of code of third section/menu/category starts from here
<Icon.Button
name="th-large"
raised={true}
backgroundColor="#ffa500"
size={30}
onPress={() => {
Linking.openURL("https://www.buymeacoffee.com/splendor");
}}
>
<Text style={{ fontSize: 15 }}>
The Age of Bihzad of Herat (1465-1535)
</Text>
</Icon.Button>
**<Seperator />**
//Block of code of thirds section/menu/category ends here