React-native Drawer navigator position change with custom header(Required) - javascript

I am creating a header with a drawer navigator and Expo. I have made a drawer navigator but the position is not changing even after using: drawerPosition="right".
Also, I want to create a custom header and need to integrate this drawer navigator in that header. My code is something like this:
import * as React from 'react';
import { View, Text, Image, TouchableOpacity, Button } from 'react-native';
import {
createDrawerNavigator,
DrawerContentScrollView,
DrawerItemList,
} from '#react-navigation/drawer';
import { NavigationContainer } from '#react-navigation/native';
import News from "./Components/News";
const Drawer = createDrawerNavigator();
const CustomDrawer = props => {
return (
<View style={{ flex: 1 }}>
<DrawerContentScrollView {...props}>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 20,
backgroundColor: '#f6f6f6',
marginBottom: 20,
}}
>
<View>
<Text>John Doe</Text>
<Text>example#email.com</Text>
</View>
<Image
source={{
uri: 'https://cdn.pixabay.com/photo/2015/01/08/18/29/entrepreneur-593358_1280.jpg',
}}
style={{ width: 65, height: 65, borderRadius: 30 }}
/>
</View>
<DrawerItemList {...props} />
</DrawerContentScrollView>
<TouchableOpacity
style={{
position: 'absolute',
right: 0,
left: 0,
bottom: 50,
backgroundColor: '#f6f6f6',
padding: 20,
}}
>
<Text>Log Out</Text>
</TouchableOpacity>
</View>
);
};
const DrawerNavigator = () => {
return (
<Drawer.Navigator
drawerPosition="right"
screenOptions={{
headerShown: true,
headerStyle: {
backgroundColor: 'transparent',
elevation: 0,
shadowOpacity: 0,
},
headerTitle: '',
}}
drawerContent={props => <CustomDrawer {...props} />}
>
<Drawer.Screen component={News} name='Home' />
</Drawer.Navigator>
);
};
export default function App() {
return (
<NavigationContainer>
<DrawerNavigator />
</NavigationContainer>
);
}
I have seen many tutorials and read documentation yet no results. Can please someone help me
![Text] (Actual)
![Text] (Expected)

Related

Return next page and close current page after loads in React Native

I'm newbie in React-native and I'm confused on how can I pass the parameter to another js file when returning a View, The second problem , The code I used below is when returning home the previous page(login) didn't close or gone and the home didn't view properly. I'm aware using react-nativigation but it's hard for me to implement but is there any other way to direct login when it loads? please need some help for this , here's my code,
Login.js After login from googlesignin when the state loaded it goes to another activity
import { Home } from "../screens"
render() {
return (
<View style={styles.container}>
<GoogleSigninButton
style={{ width: 222, height: 48 }}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
onPress={this.signIn}
/>
{this.state.loaded ?
<View style={{ width: 160, marginTop: 10 }}>
{renderLogin(this)} //return some value
</View>
: <Text>Not signIn</Text>}
</View>
);
}
}
function renderLogin(ts) {
return (
<Home /> // i want to pass value to home js example: 'ts.state.userGoogleInfo.user.photo'
)
}
Whole code in Login.js
import React, { Component } from 'react';
import { View, StyleSheet, ToastAndroid, Button, Text, Image } from "react-native";
import { Home } from "../screens"
import {
GoogleSignin,
GoogleSigninButton,
statusCodes,
} from '#react-native-community/google-signin';
GoogleSignin.configure({
webClientId: '2622122222248-3v21124124124124.apps.googleusercontent.com',
offlineAccess: true, // if you want to access Google API on behalf
});
class Login extends Component {
constructor(props) {
super(props)
this.state = {
userGoogleInfo: {},
loaded: false
}
}
static navigationOptions = {
title: 'Login',
};
signIn = async () => {
try {
console.log("Processing");
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
this.setState({
userGoogleInfo: userInfo,
loaded: true
})
console.log(this.state.userGoogleInfo);
console.log(this.state.userGoogleInfo.user.name)
console.log(this.state.userGoogleInfo.user.email)
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
console.log("e 1");
} else if (error.code === statusCodes.IN_PROGRESS) {
console.log("e 2");
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
console.log("e 3");
} else {
console.log(error.message);
}
}
};
render() {
return (
<View style={styles.container}>
<GoogleSigninButton
style={{ width: 222, height: 48 }}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
onPress={this.signIn}
/>
{this.state.loaded ?
<View style={{ width: 160, marginTop: 10 }}>
{renderLogin(this)}
</View>
: <Text>Not signIn</Text>}
</View>
);
}
}
function renderLogin(ts) {
return (
<Home />
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000000',
padding: 15,
},
buttons: {
width: 20
}
});
export default Login;
Home.js
import React from 'react';
import {
StyleSheet,
View,
Text,
ScrollView,
FlatList,
TouchableOpacity,
Image,
ImageBackground,
LogBox
} from 'react-native';
import { PriceAlert, TransactionHistory } from "../components"
import { dummyData, COLORS, SIZES, FONTS, icons, images} from '../constants';
const Home = ({ navigation }) => {
const [trending, setTrending] = React.useState(dummyData.trendingCurrencies)
const [transactionHistory, setTransactionHistory] = React.useState(dummyData.transactionHistory)
function renderHeader(){
const renderItem = ({item, index}) =>(
<TouchableOpacity
style={{
width:155,
paddingVertical:SIZES.padding,
paddingHorizontal:SIZES.padding,
marginLeft: index == 0 ? SIZES.padding : 0,
marginRight: SIZES.radius,
borderRadius: 15,
backgroundColor: COLORS.white
}}
onPress={() => navigation.navigate("CryptoDetail", {currency:item})}
>
{/* Currency*/}
<View style={{ flexDirection:'row'}}>
<View>
<Image
source={item.image}
resizeMode="cover"
style={{
marginTop: 5,
width: 25,
height: 25
}}
/>
</View>
<View style={{marginLeft: SIZES.base}}>
<Text style={{...FONTS.h3}}>{item.currency}</Text>
<Text style={{ color:COLORS.gray, ...FONTS.body3 }}></Text>
</View>
</View>
{/* value*/}
{/* <View style={{ marginTop:SIZES.radius}}> */}
{/* <Text style={{...FONTS.h6}}>₱{item.amount}</Text> */}
{/* <Text style={{color: item.type =="I" ? COLORS.green : COLORS.red, ...FONTS.h5}}>₱{item.amount}</Text> */}
{/* </View> */}
</TouchableOpacity>
)
return(
<View
style={{
width: "100%",
height: 210,
...styles.shadow
}}
>
<ImageBackground
source={images.banner}
resizeMode="cover"
style={{
flex: 1,
alignItems:'center'
}}
>
{/* Header Bar */}
<View
style={{
marginTop:SIZES.padding *1,
width: "100%",
alignItems: "flex-end",
paddingHorizontal: SIZES.padding
}}
>
<TouchableOpacity
style={{
width: 20,
height: 20,
alignItems: "center",
justifyContent:"center"
}}
onPress={() => console.log("Notification on pressed")}
>
<Image
source={icons.notification_white}
resizeMode="contain"
style={{flex: 1}}
/>
</TouchableOpacity>
</View>
{/* Balance */}
<View
style={{
alignItems: 'center',
justifyContent:'center'
}}
>
<Text style={{ color: COLORS.white, ...FONTS.h3}}>Available Balance</Text>
<Text style={{ marginTop:SIZES.base, color:COLORS.white, ...FONTS.h2}}>₱{dummyData.portfolio.balance}</Text>
<Text style={{color:COLORS.white, ...FONTS.body5}}>{dummyData.portfolio.changes} Last 24 hours</Text>
</View>
{/* Trending */}
<View
style={{
position:'absolute',
bottom: "-30%"
}}
>
<Text style={{ marginLeft:SIZES.padding,
color: COLORS.white, ...FONTS.h3 }}>Dashboard</Text>
<FlatList
contentContainerStyle={{marginTop:SIZES.base}}
data={trending}
renderItem={renderItem}
keyExtractor={item => `${item.id}`}
horizontal
showsHorizontalScrollIndicator={false}
/>
</View>
</ImageBackground>
</View>
)
}
function renderAlert(){
return (
<PriceAlert/>
)
}
function renderNotice(){
return (
<View
style={{
marginTop:SIZES.padding-6,
marginHorizontal: SIZES.padding,
padding: 12,
borderRadius:SIZES.radius,
backgroundColor:COLORS.secondary,
...styles.shadow
}}
>
<Text style={{color:COLORS.white, ...FONTS.h4}}>Announcement:</Text>
<Text style={{marginTop:SIZES.base, color:COLORS.white, ...FONTS.body4, lineHeight:18}}>We offer you an application to guide and track your data.
Learn how to use this application by reading instructions and guide.
</Text>
<TouchableOpacity
style={{
marginTop:SIZES.base
}}
onPress={()=> console.log("Learn More")}
>
<Text style={{ textDecorationLine: 'underline',
color:COLORS.green, ...FONTS.h4}}>Learn more
</Text>
</TouchableOpacity>
</View>
)
}
function renderTransactionHistory(){
return (
<TransactionHistory
customContainerStyle={{ ...styles.shadow}}
history={transactionHistory}
/>
)
}
return (
<ScrollView>
<View style={{ flex:1, paddingBottom:130 }}>
{renderHeader()}
{renderAlert()}
{renderNotice()}
{renderTransactionHistory()}
</View>
</ScrollView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
shadow: {
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 4,
},
shadowOpacity: 0.30,
shadowRadius: 4.65,
elevation: 8,
}
})
export default Home;
Apps.js
import React from 'react';
import { Transaction, TevDetail, Login } from "./screens";
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from '#react-navigation/native';
import SplashScreen from 'react-native-splash-screen';
import Tabs from "./navigation/tabs";
const Stack = createStackNavigator();
const App = () => {
React.useEffect(() => {
SplashScreen.hide()
}, [])
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName={'Login'}
>
<Stack.Screen
name="Home"
component={Tabs}
/>
<Stack.Screen
name="TevDetail"
component={TevDetail}
/>
<Stack.Screen
name="Transaction"
component={Transaction}
/>
<Stack.Screen
name="Login"
component={Login}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default App;

Navigate Between Stack Screens using imported button

I am new to react native and am having trouble establishing connections between components.
For example
onClickFunction() {this.props.navigation.navigate('CurrencyList')} is not working for my pattern. I did a several research on this subject but I could not understand its logic.
This is my App.js
import React from 'react'
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
// Screens---------------------------------------------------------
// Auth Screens
import Login from './screens/Authentication/Login'
import Register from './screens/Authentication/Register'
// Main Screens
import Home from './screens/Home'
import Converter from './screens/Converter'
import CurrencyList from './screens/CurrencyList'
//-----------------------------------------------------------------
const Stack = createStackNavigator();
var routeName = 'Converter';
export default class App extends React.Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName={`${routeName}`}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Register" component={Register} />
<Stack.Screen name="Converter" component={Converter} />
<Stack.Screen name="CurrencyList" component={CurrencyList} />
</Stack.Navigator>
</NavigationContainer>
)
}
}
This my Converter Page
// Currency Converter Page
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
// Components
import UpdateButton from '../../components/UpdateButton'
import CurrencySelectButton from '../../components/CurrencySelectButton'
import CurrencyTextInput from '../../components/CurrencyTextInput'
import ConvertedCurrencyTextInput from '../../components/ConvertedCurrencyTextInput'
export default function Home() {
return (
<View style={styles.container}>
<View style={styles.textHolder}>
<CurrencyTextInput />
<Text>=</Text>
<ConvertedCurrencyTextInput />
</View>
<View style={styles.buttonHolder}>
<CurrencySelectButton />
<Text>to</Text>
<UpdateButton />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
buttonHolder: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f1f1f1',
alignContent: 'center'
},
textHolder:{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#f1f1f1',
alignContent: 'center'
}
});
This is the button component that I want to change the screen
import React from 'react'
import { TouchableOpacity, Text, StyleSheet } from 'react-native'
export default class CurrencySelectButton extends React.Component {
onClickFunction() {
this.props.navigation.navigate('CurrencyList')
}
render() {
return (
<TouchableOpacity onPress={() => this.onClickFunction()} style={styles.button} >
<Text style={styles.buttonText} >TRY</Text>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
button: {
backgroundColor: 'black',
padding: 15,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 15,
flex: 1,
height: 75,
marginHorizontal:20
},
buttonText: {
color: 'white',
fontWeight: 'bold',
fontSize: 17
}
})
You will have to pass the navigation prop from home to the button like below
export default function Home({navigation}) {
return (
<View style={styles.container}>
<View style={styles.textHolder}>
<CurrencyTextInput />
<Text>=</Text>
<ConvertedCurrencyTextInput navigation={navigation}/>
</View>
<View style={styles.buttonHolder}>
<CurrencySelectButton />
<Text>to</Text>
<UpdateButton />
</View>
</View>
);
}

How to use default props with Redux props?

I was trying to make a "photo galley app" where each image is a "button" that toggle on the boolean property for Modal tag. The problem is that the images are rendered by a FlatList which pass props to component "GenerateImage.js", but at the same time, I need to pass the state and dispatch function as a parameter as well.
File who render the FlatList:
import React, { Component } from 'react'
import { FlatList, View, StyleSheet, TouchableOpacity, Text, AppRegistry } from 'react-native'
import { connect } from 'react-redux'
import GenerateImage from '../components/GenerateImage'
import commonStyles from '../commonStyles'
import Modal from '../components/Modal'
const Photos = ({ params }) => {
return (
<View style={{ flex: 1 }}>
<Modal isVisible={params.showFullImage} />
<View style={styles.buttonsContainer}>
<TouchableOpacity style={styles.touchContainer}>
<Text style={styles.button}>Hoje</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.touchContainer}>
<Text style={styles.button}>Mês</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.touchContainer}>
<Text style={styles.button}>Ano</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.touchContainer}>
<Text style={styles.button}>Álbuns</Text>
</TouchableOpacity>
</View>
<View style={{ flex: 30 }}>
<FlatList data={params.images} keyExtractor={item => `${item.id}`}
renderItem={({item}) => <GenerateImage {...item} />} numColumns={2} />
</View>
</View>
)
}
const styles = StyleSheet.create({
buttonsContainer: {
flex: 3,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#1c2431',
},
touchContainer: {
marginHorizontal: 20,
backgroundColor: '#439ce8',
borderRadius: 30,
width: 65,
alignItems: 'center',
justifyContent: 'center',
padding: 5
},
button: {
color: 'white',
fontFamily: commonStyles.Font,
fontSize: 14
}
})
export default connect(state => ({ params: state[0] }))(Photos)
GenerateImage.js:
import React from 'react'
import { View,
StyleSheet,
Image,
PixelRatio,
Dimensions,
TouchableOpacity } from 'react-native'
import { connect } from 'react-redux'
const GenerateImage = (props, {param, dispatch}) => {
function toggleModal(param) {
return {
type: 'TOGGLE_MODAL_ON',
}
}
return (
<View style={styles.container}>
<View style={styles.imgContainer}>
<TouchableOpacity activeOpacity={0.7} onPress={() => dispatch(toggleModal(param))}>
<Image source={props.image} style={styles.imgContainer} />
</TouchableOpacity>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginVertical: 5,
marginHorizontal: 4
},
imgContainer: {
height: Dimensions.get('window').height * 0.25,
width: Dimensions.get('window').width * 0.48,
//borderRadius: 8,
//flex: 1,
//orderWidth: 1,
},
image: {
resizeMode: 'contain',
aspectRatio: PixelRatio.get()
},
})
export default connect(state => ({ param: state[0].showFullImage }))(GenerateImage)
So, how can I pass both data?

React Native - Under render can navigate but not in navigation bar

This is my Booking.js codes.
import React, { Component } from 'react';
import { StyleSheet, View, ScrollView, Alert, } from 'react-native';
import { Button } from 'react-native-elements';
class Booking extends Component {
static navigationOptions = {
title: 'Booking',
headerTintColor: 'white',
headerBackTitle: 'Back',
headerStyle: { backgroundColor: 'black', },
headerRight: (
<Button
onPress={() => { this.props.navigation.navigate('Bnew') }}
title='New'
color='white'
backgroundColor='black'
/>
),
}
render() {
return (
<View style={styles.container}>
<View style={styles.boohis}>
<Button
onPress={() => { Alert.alert("", "Upcoming is Coming Soon!") }}
title='Upcoming'
color='white'
backgroundColor='black'
style={{ width: 185, margin:1 }}
/>
<Button
onPress={() => { Alert.alert("", "History is Coming Soon!") }}
title='History'
color='white'
backgroundColor='black'
style={{ width: 185, margin:1 }}
/>
</View>
<View style={styles.container2}>
<ScrollView contentContainerStyle={{ justifyContent: 'space-between', padding: 3, alignItems: 'center' }}>
<Button
title='New'
fontWeight='bold'
buttonStyle={{ borderRadius: 15, backgroundColor: '#10a366', height: 50, width: 300, margin: 15 }}
onPress={() => { this.props.navigation.navigate('Bnew') }}
/>
</ScrollView>
</View>
</View>
)
}
}
export default Booking;
This is the look of my Booking.js.
This is the error that show out.
The error came out because I press the NEW button on the navigation bar
but when I click on the green New button it will navigate to the page I want.
In my render the navigation is work but not on my navigation bar.
What's the problem here?
You cannot access this from a static method. You have to use the navigation object that navigationOptions receives.
So your navigationOptions should look like this.
static navigationOptions = ({ navigation }) => {
return {
title: 'Booking',
headerTintColor: 'white',
headerBackTitle: 'Back',
headerStyle: { backgroundColor: 'black' },
headerRight: (
<Button
onPress={() => {
navigation.navigate('Bnew');
}}
title='New'
color='white'
backgroundColor='black'
/>
),
};
};

Close react native modal by clicking on overlay?

Is it possible to close react native modal by clicking on overlay when transparent option is true? Documentation doesn't provide anything about it. Is it possible?
If I understood correctly, you want to close the modal when the user clicks outside of it, right ?
If yes, I searched for this some time ago and the only solution that I remember was this one (which is the one that
I've been using so far):
render() {
if (!this.state.modalVisible)
return null
return (
<View>
<Modal
animationType="fade"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => {this.setModalVisible(false)}}
>
<TouchableOpacity
style={styles.container}
activeOpacity={1}
onPressOut={() => {this.setModalVisible(false)}}
>
<ScrollView
directionalLockEnabled={true}
contentContainerStyle={styles.scrollModal}
>
<TouchableWithoutFeedback>
<View style={styles.modalContainer}>
// Here you put the content of your modal.
</View>
</TouchableWithoutFeedback>
</ScrollView>
</TouchableOpacity>
</Modal>
</View>
)
}
// Then on setModalVisible(), you do everything that you need to do when closing or opening the modal.
setModalVisible(visible) {
this.setState({
modalVisible: visible,
})
}
Explanation
This is basically using a TouchableOpacity in the whole screen to get when the user clicks to close the modal. The TouchableWithoutFeedback is to avoid the TouchableOpacity to work inside of the Modal.
If you have a better solution, please share here.
Another solution:
// Modal.js
import React from 'react';
import {
TouchableWithoutFeedback,
StyleSheet,
Modal,
View,
} from 'react-native';
import t from 'prop-types';
class MyModal extends React.Component {
static propTypes = {
children: t.node.isRequired,
visible: t.bool.isRequired,
dismiss: t.func.isRequired,
transparent: t.bool,
animationType: t.string,
};
static defaultProps = {
animationType: 'none',
transparent: true,
};
render() {
const { props } = this;
return (
<View>
<Modal
visible={props.visible}
transparent={props.transparent}
onRequestClose={props.dismiss}
animationType={props.animationType}
>
<TouchableWithoutFeedback onPress={props.dismiss}>
<View style={styles.modalOverlay} />
</TouchableWithoutFeedback>
<View style={styles.modalContent}>
{props.children}
</View>
</Modal>
</View>
);
}
}
const styles = StyleSheet.create({
modalContent: {
flex: 1,
justifyContent: 'center',
margin: '5%',
},
modalOverlay: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0,0,0,0.5)'
},
});
export default MyModal;
Usage example:
// SomeScreen.js
import React from 'react';
import { View, Text, Button } from 'react-native';
import Modal from './Modal';
class SomeScreen extends React.Component {
state = {
isModalVisible: false,
};
showModal = () => this.setState({ isModalVisible: true });
hideModal = () => this.setState({ isModalVisible: false });
render() {
return (
<View>
<Button onPress={this.showModal} />
<Modal
visible={this.state.isModalVisible}
dismiss={this.hideModal}
>
<Text>Hello, I am modal</Text>
</Modal>
</View>
);
}
}
Simple solution. You need one touchableOpacity for clicking outside and another touchableOpacity for actual modal that will do nothing onPress.
import React, { Component } from 'react'
import { View, StyleSheet, TouchableOpacity, Modal} from 'react-native';
export class Modal extends Component {
constructor(props){
this.state = { modalVisible : true}
}
render() {
return (
<View>
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => { this.setState({modalVisible: false})
}}
>
<TouchableOpacity style={styles.modalContainer} onPress={() => { this.setState({ modalVisible : false})}}>
<TouchableOpacity style={styles.modal} onPress={() => console.log('do nothing')} activeOpacity={1} >
Modal Content...
</TouchableOpacity>
</TouchableOpacity>
</Modal>
</View>
)
}
}
const styles = StyleSheet.create({
modalContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
modal: {
width: 155,
height: 300
},
});
export default Modal;
activeOpacity={1} just removes touchableOpacity fade effect
We can work it out by adding:
import { TouchableOpacity } from 'react-native';
<TouchableOpacity onPress={()=>this.setState({modalVisibilty:false})}>
<View style={{opacity:0, flex:1 }}/>
</TouchableOpacity>
under the window and another one above and change the layout style to fit your screen.
Explanation:
You will make 2 big hidden buttons to catch the user touch and change the modal visibility state to false.
Here is my simple implementation:
<TouchableWithoutFeedback onPress={}> // Code to close your modal goes here
<View style={styles.background}> // The view to drawn the background
<View
onStartShouldSetResponder={() => true}
style={styles.container}
> // The view to drawn your modal
// Your content
</View>
</Androw>
</View>
</TouchableWithoutFeedback>
I use TouchableWithoutFeedback since i do not want to change the background color when clicking on it. I also added onStartShouldSetResponder on the modal view to prevent closing the modal when you click inside the view.
I am also not using the Modal component because i done it using react-navigation.
I tried to implement some of the suggested answers, however none worked with buttons inside modal.
I was googling around and found out that instead of using TouchableWithoutFeedback, native component called Pressable allows you to check what you click on, therefore allows you to close only when clicked on it and not it's children.
return (
<View>
<Modal
animationType="slide"
transparent={true}
visible={visible}
onRequestClose={() => {
setVisible(false)}}
>
<Pressable
onPress={(event) => event.target == event.currentTarget && setVisible(false)}
style={styles.background}
>
<View style={styles.modal}>
//Content of the modal
</View>
</Pressable>
</Modal>
</View>
)
Found the answer here.
<Modal isVisible={this.state.isVisible}
onBackdropPress={() => this.setState({ isVisible: false })}>
<View style={{ flex: 1 }}>
<Text>I am the modal content!</Text>
</View>
</Modal>
<Modal
animationType="slide"
closeOnClick={true}
transparent={true}
visible={this.state.modalVisible}
>
<TouchableOpacity onPress={() => { this.setModalVisible(!this.state.modalVisible)}} style={{flex:1, justifyContent:'center', alignItems:'center',}}>
<View style={{flex:0.2,backgroundColor:'white', margin:20, borderRadius:20, borderWidth:2, borderColor:'gray'}}>
<Text style={{margin:20}}>모달 테스트</Text>
</View>
</TouchableOpacity>
</Modal>
this code is my solution.
The best and easy way to do it using TouchableWithoutFeedback and Modal of react-native is
<Modal
visible={visible}//modal visible true or false
onRequestClose={()=>onClose(false)} // function to close modal
transparent={true}
>
<TouchableWithoutFeedback
onPress={()=>onClose(false)}//outer button/view
<View style={{flex:1, backgroundColor:'rgba(0,0,0,0.5)'}}>//for transparent view, this is outer view
<TouchableWithoutFeedback>// outer button so any child view can be added
<View>// this inner view
<View>
//your content goes here
</View>
</View>
</TouchableWithoutFeedback>
</View>
</TouchableWithoutFeedback>
</Modal>
if you want to add flatlist better to provide height so the content won't go out of view
Simple solution with simple code, if you are using expo.
Here is a complete component which can you just copy and paste and get it working.
//MyModal.js
import React from 'react';
import { BlurView } from 'expo-blur';
import { View, StyleSheet, Modal, TouchableWithoutFeedback } from 'react-native';
export const MyModal = ({ children, visible, onRequestClose, onPressOverlay, }) => {
return (
<Modal
visible={visible}
transparent
animationType='none'
onRequestClose={onRequestClose}
>
<TouchableWithoutFeedback onPress={onPressOverlay}>
<BlurView
style={{ ...StyleSheet.absoluteFill }}
tint='dark'
intensity={100}
/>
</TouchableWithoutFeedback>
<View style={styles.container}>
{children}
</View>
</Modal>
);
};
const styles = StyleSheet.create({
container: {
height: '100%',
width: '100%',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
},
});
Now you can import it into your work-space and use it like this.
I'm using functional component and useState hook to show or hide the modal.
//yourScreen.js
import React, { useState } from 'react';
import { View, Button } from 'react-native';
import { MyModal } form './path/to/MyModal.js';
const YourScreen = () => {
const [modalVisible, setModalVisible] = useState(false);
return (
<View style={{ flex:1 }}>
<MyModal
visible={modalVisible}
onRequestClose={()=>{
setModalVisible(false);
}}
onPressOverlay={()=>{
setModalVisible(!modalVisible);
}}
>
// your modal content goes here
</MyModal>
<Button
title='Show Modal'
onPress={()=>{
setModalVisible(!modalVisible);
}}
/>
</View>
);
}
export default YourScreen;
Here is my solution,
import React from "react";
import {Modal, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, View} from "react-native";
// make sure the styles props is passed to the model and contains modalContainer, as well as the childrenContainer style objects.
export default class DismissibleModal extends React.Component {
render() {
return (
<Modal
animationType="slide"
transparent={true}
visible={this.props.visible}
onRequestClose={() => {this.props.dismiss()}}>
<TouchableOpacity
style={Styles.modal}
activeOpacity={1}
onPressOut={() => {this.props.dismiss()}}>
<View style={[this.props.styles.modalContainer]}>
<TouchableWithoutFeedback>
<View style={[this.props.styles.childrenContainer]}>
{this.props.children}
</View>
</TouchableWithoutFeedback>
</View>
</TouchableOpacity>
</Modal>
)
}
}
const Styles = StyleSheet.create({
modal: {
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
},
modalView: {
backgroundColor: "white",
borderRadius: 10,
padding: 20,
paddingTop: 20,
alignItems: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 9,
},
shadowOpacity: 0.50,
shadowRadius: 12.35,
elevation: 19,
},
});
#Gui Herzog answer are quite good, but makingTouchableOpacity as a parent component, it deals with conflict when it comes touching child component.
it's not good practice having a multiple component inside of TouchableOpacity, else all component inside TouchableOpacity parent will be clickable, best way to address this problem is, make your TouchableOpacity component in absolute position, with 100% width and height in screen.
here's some example:
Modal.js
export default function(props){
const { width, height } = Dimensions.get('window');
const hp = hp => (hp / 100) * height;
const wp = wp => (wp / 100) * width;
const size = size => Math.round((size / 100) * width);
return (
<KeyboardAvoidingView>
<TouchableOpacity
style={{ position: 'absolute', width: wp(100), height: hp(100) }}
onPress={props.onTouchOutSide}
/>
<ScrollView>
{/*...child*/}
</ScrollView>
</KeyboardAvoidingView>
)
}
You Welcome.
Here is the best solution for you just copy and paste. It will work definitely.
I also facing problem Here is the solution.
import React,{useState} from 'react';
import{Button,StyleSheet,View,TouchableOpacity,Modal} from 'react-native';
const App=()=>{
const [show, setShow] = useState(false);
return (
<View>
<TouchableOpacity style={{marginTop:200}}>
<Button title='show' onPress={()=>setShow(!show)}/>
</TouchableOpacity>
<Modal transparent={true} visible={show} animationType="slide">
<TouchableOpacity activeOpacity={1} style={{backgroundColor:'#000000aa',flex:1}} onPress={()=>setShow(!show)}/>
<View style={{backgroundColor:'#FFFFFF',flex: 1}}>
<View >
<Button title='close' onPress={()=>setShow(!show)}/>
</View>
</View>
</Modal>
</View>
)};
export default App;
You Can Check Another Example Here Also:
https://snack.expo.dev/#saurabh_chavan/model-with-border-radius
If you are using store solution like mobx or redux,
you can simply solve with the flag on store.
Below is the parent's code,
<Modal
animationType="fade"
transparent={true}
visible={uiControlStore.dialogVisible}
onRequestClose={() => {
uiControlStore.dialogVisible = false;
}}
>
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.6)' }}
onTouchStart={() => {
if (!uiControlStore.childClicked) uiControlStore.dialogVisible = false;
uiControlStore.childClicked= false;
}}
>
<YourCustomDialog />
</View>
</Modal>
and below is child's code. (using mobx)
const YourCustomDialog: React.FC = observer(() => {
const { uiControlStore } = useStores();
return (
<View
style={[styles.container, styles.border]}
onTouchStart={() => {
uiControlStore.childClicked= true;
}}
>
...
)
}
I realize I'm very late to this party. But I just stumbled upon this thread and Gui Herzog's answer was exactly what I was looking for. If you don't want to add any outside dependencies that is the way to go. Thanks!
However, I wanted to include some optional negative/positive action buttons in my component and grabbed them from react-native-paper for the material style.
That's when I realized react-native-paper probably have modals as well.
Here's their documentation:
https://callstack.github.io/react-native-paper/modal.html
They also have a component for Dialogs
https://callstack.github.io/react-native-paper/dialog.html
So I ended up with using the paper Dialog and it's well worth if if you're going to use the library throughout you app. Both Dialog and Modal handles the dismiss on click outside by default.
Here's a Dialog component created before realizing the Dialog component already exists.
I'll leave what I implemented here anyways as I think its a good template.
The component is in typescript. Make sure to have #types/react-native updated otherwise you might see some "No overload matches this call" errors.
import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {Button, Modal, Portal} from 'react-native-paper';
interface Action {
action: () => void;
text: string;
}
interface Props {
closePressed: () => void;
negativeAction?: Action;
positiveAction?: Action;
title?: string;
text: string;
visible: boolean;
}
export const Dialog: React.FC<Props> = ({
closePressed,
negativeAction,
positiveAction,
title,
text,
visible,
}) => {
return (
<Portal>
<Modal
visible={visible}
onDismiss={closePressed}
contentContainerStyle={styles.modalContainer}>
<View style={styles.header}>
{title && (
<Text style={{fontWeight: 'bold', fontSize: 18, marginBottom: 10}}>
{title}
</Text>
)}
<Text style={styles.contentText}>{text}</Text>
</View>
<View style={styles.buttonContainer}>
{negativeAction && (
<Button mode="text" onPress={negativeAction.action}>
{negativeAction.text}
</Button>
)}
{positiveAction && (
<Button mode="text" onPress={positiveAction.action}>
{positiveAction.text}
</Button>
)}
</View>
</Modal>
</Portal>
);
};
const styles = StyleSheet.create({
modalContainer: {
borderRadius: 5,
backgroundColor: 'white',
padding: 10,
margin: 20,
},
header: {padding: 20},
contentText: {color: 'grey'},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'flex-end',
paddingTop: 20,
},
});
Here is my perfectly working solution
MODAL CODE:
const ListInModal = ({ showListModal, onClose, data, onSelectItem }) => {
return (
<Modal animationType="none" transparent={true} visible={showListModal} onRequestClose={() => onClose(false)}>
<TouchableOpacity onPress={() => onClose(false)} style={styles.centeredView}>
<View style={styles.modalView}>
<ScrollView showsVerticalScrollIndicator={false}>
{data.map((item, index) => (
<>
<TouchableOpacity
onPress={() => {
onSelectItem(item);
onClose(false);
}}
style={{ height: 43, justifyContent: 'center' }}
>
<Text style={styles.itemLabel}>{item.label}</Text>
</TouchableOpacity>
{index <= data.length - 2 && (
<View
style={{
borderBottomColor: colors.white,
opacity: 0.2,
borderWidth: 1,
marginHorizontal: (24 / 375) * screenWidth,
}}
/>
)}
</>
))}
</ScrollView>
</View>
</TouchableOpacity>
</Modal>
);
};
STYLING:
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#00000099',
},
modalView: {
marginHorizontal: wp('5%'),
marginVertical: hp('10%'),
backgroundColor: colors.popupBlack,
borderRadius: 20,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
itemLabel: {
fontSize: wp('5%'),
color: colors.white,
paddingHorizontal: (24 / 375) * screenWidth,
},
});
USAGE:
<ListInModal
data={projectState?.lvApplicationTypeList}
showListModal={showListModal}
onClose={(bool) => setshowListModal(bool)}
onSelectItem={(item) => onPressApplicationType(item.label)}
/>
I made it like this.
<Modal
visible={isVisible}
onRequestClose={() => setIsVisible(false)}
transparent={true}
>
<Pressable style={{
flex:1,
backgroundColor:'transparent',
}}
onPress={()=>setIsVisible(false)}
/>
{/* Here comes your component*/}
</Modal>
make your component with position:absoute so Pressable can cover the whole background.
#idiosync/react-native-modal is a hook based modal system.
https://www.npmjs.com/package/#idiosync/react-native-modal
You can add a onBackgroundPress function to the config object to achieve what you want:
useModal(
{
// all config params are optional apart from renderModal
renderModal: () => <MyModal onClose={() => setModalIsVisible(false)} someProp={someProp} />,
onBackgroundPress: () => setModalIsVisible(false),
animationTypeIn: AnimationType.SLIDE_TOP,
},
modalIsVisible,
[someProp] // dependencies array to trigger rerenders. Empty array is passed by default
)
2022 Answer - Using React Native Hooks
to Close a React Native Modal by clicking an Overlay is best done by using a Pressable button and TouchableOpacity
Example Below...
Import the Pressable and others from react-native
import React, { useState } from 'react';
import {
Pressable,
View,
Text,
TouchableOpacity,
ScrollView,
Modal,
TextInput,
StyleSheet,
} from 'react-native';
Then Set a State
const [modalVisible, setModalVisible] = useState(false);
Then Write and Design Your Modal
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}
>
<TouchableOpacity
onPress={() => setModalVisible(false)}
style={styles.modalContainerStyle}
>
<Pressable
onPress={() => setModalVisible(true)}
style={styles.modalContent}
>
<ScrollView>
<View>
<Text style={styles.modalTitle}>TITLE HERE</Text>
<Text style={styles.modalText}>
OTHER SMALLER TEXT HERE
</Text>
<Text style={styles.modalFormText}>AN INPUT</Text>
<TextInput
style={styles.modalInput}
value={inputValue}
onChangeText={(inputValue) => handleInputValue(inputValue)}
placeholder={'example#email.com'}
/>
<View style={styles.btnView}>
<TouchableOpacity
onPress={() => setModalVisible(!modalVisible)}
underlayColor="white"
>
<View style={styles.buttonProceed}>
<Text style={styles.buttonText}>NEXT</Text>
</View>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</Pressable>
</TouchableOpacity>
</Modal>
Now To Open Your Modal (Using a Text Button)
<Pressable onPress={() => setModalVisible(!modalVisible)}>
<Text style={styles.openModal}>Open Modal</Text>
</Pressable>
Finally Style Your Modal
modalContainerStyle: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'flex-end',
backgroundColor: 'rgba(0, 0, 0, 0.3)',
},
modalContent: {
borderTopLeftRadius: 27,
borderTopRightRadius: 27,
width: '100%',
height: '60%',
backgroundColor: '#FFFFFF',
paddingLeft: 33,
paddingRight: 33,
overflow: 'hidden',
},
modalTitle: {
marginTop: 43,
marginBottom: 14,
fontSize: 19,
lineHeight: 21,
color: 'black',
fontFamily: 'GraphikBold',
},
modalText: {
fontSize: 15,
lineHeight: 25,
marginBottom: 38,
color: 'black',
fontFamily: 'GraphikRegular',
},
modalFormText: {
marginBottom: 10,
fontSize: 15,
color: 'black',
fontFamily: 'GraphikMedium',
},
modalInput: {
marginBottom: 99,
height: 44,
paddingLeft: 17,
paddingRight: 17,
fontSize: 16,
borderRadius: 10,
marginBottom: 20,
borderWidth: 1,
fontFamily: 'GraphikRegular',
borderColor: 'rgba(118, 118, 118, 0.17)',
},
openModal: {
marginRight: 5,
textAlign: 'right',
color: '#ED2125',
fontFamily: 'GraphikMedium',
},
recently faced the issue, tried solving it using a two touchable opacity,
you can also turn off the closing on tapping anywhere behaviour by passing the isDismissable value to false.
import { View, Text, Modal, StyleSheet, TouchableOpacity } from 'react-native';
import React from 'react';
type PropType = {
open: boolean;
onClose: () => void;
isDismissable?: boolean;
children: JSX.Element | JSX.Element[];
};
const styles = StyleSheet.create({
modalRootContainer: {
flexGrow: 1,
},
outerContainer: {
height: '100%',
},
});
const BottomModal = ({
open,
onClose,
isDismissable = true,
children,
}: PropType) => {
return (
<Modal
visible={open}
transparent
onRequestClose={onClose}
animationType='slide'
>
<TouchableOpacity
style={styles.outerContainer}
activeOpacity={1}
onPress={() => {
if (isDismissable) onClose();
}}
>
<View style={styles.modalRootContainer}>
<TouchableOpacity activeOpacity={1}>{children}</TouchableOpacity>
</View>
</TouchableOpacity>
</Modal>
);
};
export default BottomModal;
*it's a very simple and effective solution I just applied it to my app and its works as I wanted.
just wrap your child component into TouchableOpacity
EXAMPLE:
<Modal
animationType="slide"
transparent={true}
visible={IsCamaraShow}
onRequestClose={() => {
setIsCamaraShow(!IsCamaraShow);
}}>
<View style={Styles.centeredView1}>
<TouchableOpacity style={Styles.centeredView1}
onPress={()=> setIsCamaraShow(!IsCamaraShow)}
activeOpacity={1}
>
<View style={Styles.modalView1}>
{cosnole.log( 'rest of your code') }
</View>
</TouchableOpacity>
</View>
</Modal>
Would recommend the following which worked well for me:
Use two <Pressable> components inside the modal
First one with opacity-1 and flex-1, no content and onPress that closes the modal
Second one with style according to how you want the modal content to show and text content inside
<Modal
animationType="slide"
transparent
visible={isModalVisible}
onRequestClose={() => setIsModalVisible(false)
>
<Pressable
onPress={() => setIsModalVisible(false)}
style={{flex: 1, opacity: 0}}
/>
<Pressable
style{{height: '25%', width: '100%', position: 'absolute', bottom: 0, justify-content: 'center', align-items: 'center'}}
>
<Text>Content of modal</Text>
</Pressable>
</Modal>
I have solved this in react native 0.64 by given below code
<Modal
isVisible={ModalVisible}
onBackdropPress={()=>{setModalVisible(false)}}
>
.
.
.
</Modal>
Hi I am using a lightweight popup react-native-easypopup it also closing itself when you tap out of popup.
npm i react-native-easypopup

Categories