Is it possible to close react native modal by clicking on overlay when transparent
option is true
? Documentation doesn\'t provide anything about it
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.
<Modal isVisible={this.state.isVisible}
onBackdropPress={() => this.setState({ isVisible: false })}>
<View style={{ flex: 1 }}>
<Text>I am the modal content!</Text>
</View>
</Modal>
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)}
/>
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
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,
})
}
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.
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;