Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tap outside to close #11

Closed
Doko-Demo-Doa opened this issue Mar 29, 2017 · 87 comments
Closed

Tap outside to close #11

Doko-Demo-Doa opened this issue Mar 29, 2017 · 87 comments

Comments

@Doko-Demo-Doa
Copy link

Is there a way to implement that function? Right now when I tap on the darkened region, the modal doesn't close.

@mmazzarolo
Copy link
Member

Hey!
It should be possible by toying a bit with the touchable options of this View.
Would you mind giving it a try and submitting a PR?
Otherwise I'll take a look at it in my spare time :)

@DavidKongDesheng
Copy link

😆 pls add this function~~ thanks

@Doko-Demo-Doa
Copy link
Author

Thank you @mmazzarolo , actually I fixed using exactly same method you mentioned. It works, but since the first image in your preview images (on README.md) does it correctly, I thought it was implemented already, wasn't it?

@DavidKongDesheng
Copy link

@Doko-Demo-Doa according to the source code, haven't implemented yet, there is no close event on backdrop view or you can do sth like this, not so good but should be work.

<TouchableWithoutFeedback onPress={() => this.setState({ visible: false })}>
        <Modal isVisible={this.state.visible}  ...>
            <TouchableWithoutFeedback onPress={() => {}}>
                    ... your custom view
            </TouchableWithoutFeedback
       </Modal>
</TouchableWithoutFeedback>

@mmazzarolo
Copy link
Member

mmazzarolo commented Mar 29, 2017

@Doko-Demo-Doa oh wow, honestly I don't remember how I implemented it for that screenshot (I did it in a customized version of this lib 😞 ).

@DavidKongDesheng the problem with that solution is that clicking everywhere on the modal will close it.
In my opinion the right solution would be using onStartShouldSetResponderCapture (or something similar in the right place.
The perfect solution would be that:

  • The backdrop should respond to the user touch (end by default trigger onClose or onBackdropPress);
  • The view content should not respond to the backdrop touch, of course;
  • The view content can have another independent button/touchable component inside.

What do you think guys?

Also, thank you @Doko-Demo-Doa and @DavidKongDesheng for the discussion, any feedback is of course warmly welcomed! 😸

@Doko-Demo-Doa could you share with us your solution?

@mmazzarolo
Copy link
Member

mmazzarolo commented Mar 29, 2017

I played a bit with it.
It seems that the right way to handle it is by applying something like this:

onStartShouldSetResponder={() => {
  console.log('You pressed the backdrop!');
  return true;
}}

to the backdrop view.

The problem with this approach is that it won't work correctly if you're centering the modal content using this:

content: {
    flex: 1,
    justifyContent: 'center'
  }

...which is the default style applied to the content.

I guess we should wait until react-native 0.43 stable lands and try changing that style to the new margin: auto, which I hope will make the backdrop press work correctly.

I'm obviously open to discussions on the matter :)

@DavidKongDesheng
Copy link

@mmazzarolo On my solution I added two touch events, one for Modal itself which will close the modal, the other one is on the Modal content view which will do nothing 😸, so only when you click the backdrop will close the modal.
<TouchableWithoutFeedback onPress={() => this.setState({ visible: false })}>
<Modal isVisible={this.state.visible} ...>
<TouchableWithoutFeedback onPress={() => {}}>
... your custom view
</TouchableWithoutFeedback

Yup, the right solution should be make the backdrop respond to user touch.

@mmazzarolo
Copy link
Member

Oh, nice! @DavidKongDesheng did you test your solution?
Do you think it works correctly in most cases?
Would you mind pasting here your complete snippet?
Thanks!

@DavidKongDesheng
Copy link

@mmazzarolo Just added touch event to your example code, you can download and try, but this is not the best way to implement.
app.js.zip

@Doko-Demo-Doa
Copy link
Author

It seems that RN's modal component is pretty meh. If there's something like Dialog on Android or UIAlertView on iOS, it would be much better in this case :(

@davidroman0O
Copy link

davidroman0O commented Apr 20, 2017

Hi,

I did exactly the same thing as @DavidKongDesheng but still don't working on my side !

<TouchableWithoutFeedback onPress={() => this.setState({ modalVisible: false })}>
  <Modal transparent={true} ref="modal" visible={this.state.modalVisible} onRequestClose={this.close.bind(this)} animationType={this.state.animationType}>
    {this.renderOptionList()}
  </Modal>
</TouchableWithoutFeedback>

Or maybe i don't make it well... no ?

react-native 0.42.3

@emalorenzo
Copy link

emalorenzo commented Apr 20, 2017

Hi! For me the solution of @DavidKongDesheng works great 🔥
This is my code:

`
constructor(props) {
super(props);
this.state = {
isModalVisible: false
}
}

_showModal() {
this.setState({ isModalVisible: true })
}

_hideModal() {
this.setState({ isModalVisible: false })
}

render() {
const { alerts, showAlert } = this.props;
return (

    <TouchableWithoutFeedback onPress={() => this._hideModal() }>
      <Modal style={{margin: 0}} isVisible={this.state.isModalVisible}>
        <AccountModal />
      </Modal>
    </TouchableWithoutFeedback>

    ....`

Hope to be helpfull!

@davidroman0O
Copy link

@emalorenzo Thanks! But it doesn't work :(

I don't know why it doesn't triggered the background :/

@DavidKongDesheng
Copy link

@warka0 Can't reproduce it according the attached codes, but it looks like you are using Modal from 'react-native' not from 'react-native-modal'

Or maybe try

  1. Remove onRequestClose, use onBackButtonPress instead
  2. Set transparent={false} and set 'red' as background color of your option list view, and check if really click on the background or somewhere else.
  3. Check your renderOptionList().

Or more codes attach...

@jm90m
Copy link

jm90m commented Apr 21, 2017

@emalorenzo Thanks for your solution, this worked for me 👏 👍

@kdenz
Copy link

kdenz commented Apr 22, 2017

@DavidKongDesheng Your solution worked for me, thanks! I hope this functionality is added soon to react-native-modal, it's such an important function.

@davidroman0O
Copy link

I'll test it in two days. Cannot do it right now, i'm between two airports. :p

@mmazzarolo
Copy link
Member

Hey, I'll try to take a look at it soon (PRs are obviously warmly welcome) :)
I still haven't tried the touchable wrapper but... doesn't it swallow the click buttons inside the modal (if any)? 🤔

@davidroman0O
Copy link

Ok, i've tried and it's working, thanks!

@mmazzarolo
Copy link
Member

@warka0, does it also work if you add a button inside the modal? Doesn't the TouchableWithoutFeedback wrapper on the modal swallow any touch to components inside it?

@jm90m
Copy link

jm90m commented Apr 27, 2017

@mmazzarolo So the modal is wrapped in TouchableWithoutFeedback but inside the modal I have a bunch of buttons wrapped around in TouchableOpacity and it works perfectly 👍

@mmazzarolo
Copy link
Member

It was easier than I thought then :) Anyone willing to submit a PR?

@jm90m
Copy link

jm90m commented Apr 27, 2017

Sure I'll submit an example

jm90m pushed a commit to jm90m/react-native-modal that referenced this issue Apr 27, 2017
@mmazzarolo
Copy link
Member

@jm90m thank you for the PRs!
However...
Guys, with @emalorenzo 's solution the modal is closed wherever you tap, while the issue was focused on closing the modal only when clicking on the backdrop 😿

@jm90m
Copy link

jm90m commented Apr 27, 2017

@mmazzarolo ohh, yeah looks like the main issue is more complex than it seems. I see, if I ever come across a solution, I'll submit a PR if no one has done so yet.

@jzhan-canva
Copy link

jzhan-canva commented Apr 28, 2017

You guys can take a look at my repo, the idea is surrounding modal with 4 touchables. My initial solution was use a screen size touchable to perform tapToClose, a modal size touchable wrapping the modal to prevent the tap event goes up(no onPress). But then I found I can't use scrollView inside the touchable. In the end I came with up the solution I used in my repo

@mmazzarolo
Copy link
Member

Released on npm on 3.0.0, I'll add the release notes this evening.
Anyone willing to give it a try?

@mmazzarolo
Copy link
Member

mmazzarolo commented Aug 21, 2017

The release notes of v3.0.0 are here.
I already submit two other fixes (current release:v3.0.2) and we should be stable now, any other test will be super appreciated.
Thanks to anyone who contributed on this issue!

@alexkuc
Copy link

alexkuc commented Aug 22, 2017

I am going to give it a shot shortly. Just wanted to clarify that I use the setup properly. Previously, I was wrapping inside of Modal with <TouchableWithoutFeedback>. With #11 being fixed, does it mean I no longer need to use <TouchableWithoutFeedback>?

@mmazzarolo
Copy link
Member

mmazzarolo commented Aug 22, 2017

@alexkuc, yep, just set the visibility of the modal to false in the onBackdropPress callback

@Doko-Demo-Doa
Copy link
Author

Yesssssss
Since the issue is solved. I think it can be closed now.

@mmazzarolo
Copy link
Member

It took so long but we did it! 🎈

Feel free to open another issue if needed.

@tomasreimers
Copy link
Contributor

@mmazzarolo , I mean the bug was literally in compiler (or interpreter in this case): the optimizer of React Native's Java's ShadowNode tree. I think it's fair this one took a while to solve lol 😂

Now just to get react-native to accept their fix so we can remove our hacky fix. If you want to jump on and +1 the fix or add context, that would be much appreciated: facebook/react-native#15529

facebook-github-bot pushed a commit to facebook/react-native that referenced this issue Sep 12, 2017
Summary:
Currently, having the property `pointerEvents={'box-only'}` marks a view as layoutOnly. For optimization reasons, layoutOnly nodes are [not added to the node hierarchy](https://github.com/facebook/react-native/blob/b103903ec869bc48dfcaf001dc926957d0b5200a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java#L99) The problem is that should this box ever need to be [transitioned to not-layout-only](https://github.com/facebook/react-native/blob/b103903ec869bc48dfcaf001dc926957d0b5200a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java#L394) (for example, because you add the a CSS transform property),  it must be added to the hierarchy. To add it to the hierarchy the [React Styles Diff Map](https://github.com/facebook/react-native/blob/b103903ec869bc48dfcaf001dc926957d0b5200a/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyOptimizer.java#L396) is passed along with the backing shadowNode to create the node. The problem is that at no point were the original `pointerEvents` and so the new node will be created with `pointerEvents =  'auto'`.

A more correct fix to this problem might be to have shadowNodes carry around their pointerEvents, although this is likely a greater design decision which is why am I proposing the quick fix now.

Will also resolve: react-native-modal/react-native-modal#11
Closes #15529

Reviewed By: AaaChiuuu

Differential Revision: D5792012

Pulled By: tomasreimers

fbshipit-source-id: 625242c53e16cb293c64811a57f6c3905b3483e0
@hustlerism
Copy link

Hi I'm still having this issue.

_hideModal = () => { this.setState({ isModalVisible: false }) }

Passed a function as prop to a custom component <ModalBox />

onPress={this._hideModal}

In <ModalBox />

<Modal isVisible={this.props.isModalVisible} onBackdropPress={this.props.onPress} >

It wont close after tapping on the backdrop. But when i use it in <Button /> inside the modal, it works.

"react-native": "0.44.2",
"react-native-modal": "3.1.1",

@mmazzarolo
Copy link
Member

Are you using Expo by any mean?
Could you try updating react-native?

@hustlerism
Copy link

@mmazzarolo

Nope, i'm not using Expo.

Btw, the problem solved. The react-native-modal package didnt update properly. My bad.

Thanks for the quick response.

@jbyrneie
Copy link

Hi Guys

I tried the above suggestions but they dont work for me

When I click outside the modal my _hideModal() is not fired.

I am using RN 0.47.2 and react-native-modal 3.0.2

import React, { Component } from 'react'
import {StyleSheet, Text, TouchableOpacity, TouchableWithoutFeedback, View} from 'react-native'
import Modal from 'react-native-modal'

class ConfirmSchedule extends Component {
constructor(props) {
super(props);
}

_hideModal() {
alert('hideModal')
}

render () {
return (
<TouchableWithoutFeedback onPress={() => this._hideModal() }>



Some text




)
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
modalContent: {
backgroundColor: 'white',
padding: 22,
borderRadius: 4,
borderColor: 'rgba(0, 0, 0, 0.1)',
},
bottomModal: {
margin: 20,
},
})

module.exports = ConfirmSchedule;

@nahuelr
Copy link

nahuelr commented Oct 4, 2017

That works greats for me, only use one layer behind options.
you have to make touchable the content of modal, not the modal per se.

                <View>
                    <Modal
                        animationType="slide"
                        transparent={true}
                        visible={this.state.modalVisible}
                        onRequestClose={() => {
                            this.closeModal();
                        }}>
                        <TouchableWithoutFeedback //this touchable closes modal
                                onPress={() => {
                                    this.closeModal();
                                }}>
                            <View style={{flex:2,backgroundColor:'#6666669c'}}>//layer behind modal options
                                {this.renderOptionList()}
                            </View>
                        </TouchableWithoutFeedback>
                    </Modal>
                </View>

@jbaek7023
Copy link

jbaek7023 commented Oct 16, 2017

Hi guys,

I just found this issue and tried hard to read all of your comments and suggestions.
Here is what I've tried

  • Adding onBackdropPress(() => {this.props.hideModal()})
  • Adding TouchableWithoutFeedback inside and outside of components
  • other approaches...

Here is my the screen where I want to show my modal.

render() {
return (
<View style={{flex: 1}}>
        <ScrollView>
          // CONTENT HERE
          {this._renderModal()} //rendering modal here
          <FABs onFABsPress={this._showModal} /> // I open Modal when I press the FABs button
        </ScrollView>
      </View>));

_renderModal = () => {
    return (
      <CameraImageSelectModal
        hideModal={this._hideModal}
        isModalVisible={this.state.isModalVisible}
        navigation={this.props.navigation}
      />
    )
  }

Here is modal component : CameraImageSelectModal.js

  render() {
    let { isModalVisible } = this.props;
    return (
      <View>
        <Modal
          isVisible={isModalVisible}
          onBackdropPress={() => {console.log('hey')}}>
          transparent={true}>
          <View style={styles.modalContainer}>
            <View style={styles.modalTitleTextContainer}>
              <Text style={styles.modalTitleText}>Hello World</Text>
            </View>
            <View style={styles.modalContentTextContainer}>
              <Text style={styles.modalContentText}></Text>
            </View>
            <View style={styles.modalButtonContainer}>
              <Button transparent onPress={this._handleCameraPress}>
                <Text style={[styles.modalText, styles.black]}>Camera</Text>
              </Button>
              <Button transparent onPress={this._handleAlbumPress}>
                <Text style={styles.modalText}>Album</Text>
              </Button>
            </View>
          </View>
        </Modal>
      </View>

Thanks!!

@jbaek7023
Copy link

Nevermind. I just updated the react-native-modal to the newer version and it solved my problem. Thanks!!

(I was using 2.5.0 version lol)

@adirzoari
Copy link

@jbaek7023 onBackdropPress works for u?

@CezarCobuz
Copy link

@Doko-Demo-Doa according to the source code, haven't implemented yet, there is no close event on backdrop view or you can do sth like this, not so good but should be work.

<TouchableWithoutFeedback onPress={() => this.setState({ visible: false })}>
        <Modal isVisible={this.state.visible}  ...>
            <TouchableWithoutFeedback onPress={() => {}}>
                    ... your custom view
            </TouchableWithoutFeedback
       </Modal>
</TouchableWithoutFeedback>

What if there is scrollView inside?

@darshan-vineeth
Copy link

@Doko-Demo-Doa according to the source code, haven't implemented yet, there is no close event on backdrop view or you can do sth like this, not so good but should be work.

<TouchableWithoutFeedback onPress={() => this.setState({ visible: false })}>
        <Modal isVisible={this.state.visible}  ...>
            <TouchableWithoutFeedback onPress={() => {}}>
                    ... your custom view
            </TouchableWithoutFeedback
       </Modal>
</TouchableWithoutFeedback>

What if there is scrollView inside?

I think this solution would help you.
https://stackoverflow.com/a/40483972/4994493

@Einere
Copy link

Einere commented Sep 3, 2020

I implements using @DavidKongDesheng 's solution.

const constainerStyle = StyleSheet.create({
  ...
})

export function MyModal(props) {
  const {modalVisible = false, onClose, children, preset = 'nonClose'} = props;

  return preset === 'nonClose' ? (
    <Modal animationType="fade" transparent={true} visible={modalVisible}>
      <View style={containerStyle.centeredView}>
        <View style={containerStyle.modalView}>{children}</View>
      </View>
    </Modal>
  ) : (
    <Modal animationType="fade" transparent={true} visible={modalVisible}>
      <TouchableWithoutFeedback onPress={onClose}> // when trigger click modal outside
        <View style={containerStyle.centeredView}>
          <TouchableWithoutFeedback onPress={() => {}}> // prevent trigger onClose when click modal inside
            <View style={containerStyle.modalView}>{children}</View>
          </TouchableWithoutFeedback>
        </View>
      </TouchableWithoutFeedback>
    </Modal>
  );
}

@mjgul
Copy link

mjgul commented Oct 15, 2020

THIS WILL WORK 100%

<Modal
animationType="fade"
transparent={true}
visible={visible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
// MODAL WHOLE VIEW

  ---------------------------
  |                                           |
  |       __________________       |
  |       |   //DATA             |      |
  |       |   Nam  JUNAID |      |
  |       |_________________|      |                               
  |                                           | 
  |                                           |
  |___________________________|                                                                                

    <TouchableOpacity
    onPress={() => closeModel()}
      style={{
        height: windowHeight,
        width: windowWidth,
        justifyContent: "center",
        alignItems: "center",
      }}
    >
// DATA THAT WILL SHOW IN MODAL 

<View
style={{
height: windowHeight / 1.5,
backgroundColor: "pink",
borderRadius: 10,
width: windowWidth / 1.1,
}}
> DATA GOES HERE IN THE CENTER

By clicking anywhere on an empty space modal will dismiss.

@ChiragALogicalloop
Copy link

@Doko-Demo-Doa according to the source code, haven't implemented yet, there is no close event on backdrop view or you can do sth like this, not so good but should be work.

<TouchableWithoutFeedback onPress={() => this.setState({ visible: false })}>
        <Modal isVisible={this.state.visible}  ...>
            <TouchableWithoutFeedback onPress={() => {}}>
                    ... your custom view
            </TouchableWithoutFeedback
       </Modal>
</TouchableWithoutFeedback>

It's working.

@mateoLorenzo1
Copy link

Hi! I've tried most of the aproaches above and any of them seemed to solve the main issue. The last ones works but are half incomplete and difficult to understand, so I leave you here an updated easy to read component that solves the issue.

The code 👇👇👇

export const ModalScreen = () => {
  const [isVisible, setIsVisible] = useState(false);

  const showModal = () => {
    setIsVisible(true);
  };

  const hideModal = () => {
    setIsVisible(false);
  };

  return (
    <View style={styles.screenContainer}>
      <Button title="Open Modal" onPress={showModal} />

      <Modal visible={isVisible} transparent>
        <Pressable onPress={hideModal} style={styles.modalContainer}>
          <TouchableWithoutFeedback>
            <View style={styles.modalContent}>
              <Text>Modal Title</Text>
              <Button title="Close Modal" onPress={hideModal} />
            </View>
          </TouchableWithoutFeedback>
        </Pressable>
      </Modal>
    </View>
  );
};

const styles = StyleSheet.create({
  screenContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  modalContainer: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.3)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  modalContent: {
    backgroundColor: 'white',
    borderRadius: 10,
    padding: 40,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

@pbaker0804
Copy link

Hi! I've tried most of the aproaches above and any of them seemed to solve the main issue. The last ones works but are half incomplete and difficult to understand, so I leave you here an updated easy to read component that solves the issue.

Works great! Thanks!

happyboy000916 added a commit to happyboy000916/react-native-modal that referenced this issue May 31, 2023
AP2Topper0127 added a commit to AP2Topper0127/react-native-modals that referenced this issue Aug 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.