acidernt
acidernt

Reputation: 2311

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?

Upvotes: 102

Views: 173132

Answers (29)

Gui Herzog
Gui Herzog

Reputation: 5615

If you want to close the modal when the user clicks outside of it, the only solution that I remember is:

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.

Upvotes: 190

Pravin Ghorle
Pravin Ghorle

Reputation: 690

Use TouchableWithoutFeedback and Modal of react-native:

<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

Upvotes: 5

Raju Tripathi
Raju Tripathi

Reputation: 1

In double TouchableOpacity solutions if you are facing fading issues here is what you can do

<TouchableOpacity style={styles.modalContainer} activeOpacity={0} onPress={()=>{props.setModalVisible(false)}}> 
<TouchableOpacity activeOpacity={1} style={styles.modalContent} >

Added activeOpacity on both lines first with 0 and the inner one as 1

Upvotes: 0

Tejpbit
Tejpbit

Reputation: 500

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.

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,
  },
});

Upvotes: 0

Saurabh Chavan
Saurabh Chavan

Reputation: 164

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

Upvotes: 2

TomasB
TomasB

Reputation: 744

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.

Upvotes: 18

Aleksa Radovanovic
Aleksa Radovanovic

Reputation: 271

You need one TouchableOpacity for clicking outside and another TouchableOpacity for actual modal that will do nothing onPress.

import { View, StyleSheet, TouchableOpacity, Modal} from 'react-native';

const Screen = () =>  {
  const [modalVisible, setModalVisible] = useState(false);

  return (
      <View>
          <Modal
              animationType="slide"
              transparent={true}
              visible={modalVisible}
              onRequestClose={() => setModalVisible(false) }
            >
              <TouchableOpacity style={styles.modalContainer} onPress={() => setModalVisible(false)} >
                  <TouchableOpacity style={styles.modal} 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 Screen;   

activeOpacity={1} just removes touchableOpacity fade effect

Upvotes: 27

Andrew
Andrew

Reputation: 5200

Most of the answers here use <TouchableOpacity> and/or <TouchableWithoutFeedback>, which is fine, but as of 2023 there is a simpler implementation using the more modern <Pressable>. Simply:

<Modal>
   <Pressable onPress={() => handleOutsideModalClick()}>
       <Pressable onPress={() => handleModalBodyClicks()}>
            <Button onPress={() => handleButtonClicks()}>Option 1</Button>
            <Button onPress={() => handleButtonClicks()}>Option 2</Button>
       </Pressable>
   </Pressable>
</Modal>

Upvotes: 0

Tom
Tom

Reputation: 133

Modal has a onBackdropPress prop. I found that to be the easiest fix.

Just realised I'm using a different Modal library. This is for react-native-modal

<Modal
    isVisible={isVisible}
    onBackButtonPress={() => closeModal()}
    onBackdropPress={() => closeModal()}
    onSwipeComplete={() => setIsVisible(false)}
    swipeDirection={['down']}
    scrollTo={handleScrollTo}
    scrollOffset={scrollOffset}
    scrollOffsetMax={300 - screenHeight}
    propagateSwipe={true}
    style={styles.modal}>
    <OptionsModalContent
      scrollViewRef={scrollViewRef}
      handleOnScroll={handleOnScroll}
      modalContent={modalContent}
      setIsVisible={setIsVisible}
    />
  </Modal>

Upvotes: 0

Chukwuebuka Nwoba
Chukwuebuka Nwoba

Reputation: 352

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={'[email protected]'}
                  />

                  <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',
  },

Upvotes: 0

fahad991
fahad991

Reputation: 472

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;

Upvotes: 0

Marvin
Marvin

Reputation: 754

@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>
    )
}

Upvotes: 2

Ravi Raj
Ravi Raj

Reputation: 6677

I initial used the concept of wrapping Modal inside a Touchable opacity similar to the answer https://stackoverflow.com/a/40483972/1250311 but this would create touch issues when using ScrollView as a children.

So I moved to the second solution, which is nesting a absolute positioned overlay which covers the entire modal which on press will close the modal and then place the children inside this modal. Similar to https://stackoverflow.com/a/52936928/1250311 . Note, here I wrap the absolute positioned overlay and children, inside a parent View, to position the children to bottom or centre.

export function WFBottomSheetV2({
  onRequestClose,
  children
}) {
  return (
    <Modal
      animationType='fade'
      transparent={true}
      visible
      hardwareAccelerated
      onRequestClose={onRequestClose}
    >
      <View style={styles.container} >
        <TouchableWithoutFeedback onPress={onRequestClose} >
          <View style={styles.modalOverlay} />
        </TouchableWithoutFeedback>
        {children}
      </View>
    </Modal>
  );
}


const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center', // can also use 'flex-end' for bottomsheet style
    alignItems: 'center'
  },
  modalOverlay: {
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    backgroundColor: 'rgba(0,0,0,0.5)'
  },
});

Upvotes: 0

Sherif Samir
Sherif Samir

Reputation: 685

You can install react-native-modal it's way more simpler and it has a property onBackdropPress just for this case

PS: it even has some animations which makes it look way better than the normal modal

Upvotes: 1

Daniel Gibson
Daniel Gibson

Reputation: 11

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>

Upvotes: 0

Abdul haseeb
Abdul haseeb

Reputation: 71

*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>

Upvotes: 0

James Trickey
James Trickey

Reputation: 1447

@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
  )

Upvotes: 0

박제영
박제영

Reputation: 109

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.

Upvotes: 0

sorrat
sorrat

Reputation: 923

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>
    );
  }
}

Upvotes: 67

Awais Qadir
Awais Qadir

Reputation: 47

I have solved this in react native 0.64 by given below code

 <Modal  
     isVisible={ModalVisible}  

      onBackdropPress={()=>{setModalVisible(false)}}
       >
      .
      .
      . 
  </Modal>

Upvotes: -1

Ajmal Hasan
Ajmal Hasan

Reputation: 1569

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)}
/>

Upvotes: 0

Ali H. Kudeir
Ali H. Kudeir

Reputation: 1054

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,
  },
});

Upvotes: 2

bychung
bychung

Reputation: 143

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;
      }}
    >
    ...
  )
}

Upvotes: 0

teckmk
teckmk

Reputation: 487

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;

Upvotes: 2

Michael Wallace
Michael Wallace

Reputation: 432

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.

Upvotes: 8

lostomato
lostomato

Reputation: 1100

<Modal isVisible={this.state.isVisible}
onBackdropPress={() => this.setState({ isVisible: false })}>
<View style={{ flex: 1 }}>
<Text>I am the modal content!</Text>
</View>
</Modal>

Upvotes: 6

Kim HanSol
Kim HanSol

Reputation: 33

        <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.

Upvotes: 3

yowovogu
yowovogu

Reputation: 3

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

Upvotes: -5

Mustafa Tawfig
Mustafa Tawfig

Reputation: 419

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.

Upvotes: 9

Related Questions