react-native-modal: ScrollView inside swipeable modal is not working

Just recently did an update and have confirmed that v7 stops the ability to scroll inside the modal whereas v6 was still working (swapped between the versions)

I looked at the diff from 6.5.0 up to 7.0.2 and the only thing I can see that may effect the scroll view would be this line.

I’ve tried adding the Touchable... around the views as other closed issues have suggested to no luck.

I’ve been stumped on this for a couple days now and not sure what could be wrong.

The modal is something of this structure:

<Modal>
    <ScrollView horiztonal>
        <FlatList ... />
       ...
    </ScrollView>
</Modal>

Any idea what changes from v6.5.0 to v7.0.2 could be causing the scrolling to not work?

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 53
  • Comments: 92 (18 by maintainers)

Most upvoted comments

I’ve been facing this issue as well. Tried your solution @rewieer but it didn’t work for me. Found a workaround for now. Hope it helps. <Modal> <ScrollView> <TouchableOpacity> ... </TouchableOpacity> </ScrollView> <Modal>

You can do this if you would like to use View instead of TouchableWithoutFeedback

NOTE: If you are not using swipeDirection

<Modal>
  <ScrollView>
    <View flex={1} onStartShouldSetResponder={() => true}> // or flexGrow={1}
       YOUR CONTENT HERE
    </View>
  </ScrollView>
</Modal>

WORKING SOLUTION FOR ME:

        <Modal
            onShow={onShowModal}
            isVisible={showModal}
            onSwipeComplete={closeModal}
            swipeDirection={['down']}
            propagateSwipe={true}
            style={styles.modal}>
            <View
                style={styles.scrollableModal}>
                <View style={styles.tabWhite} />
                <ScrollView>
                    <View flex={1} onStartShouldSetResponder={() => true}>
                        <FlatList
                            data={DATA}
                            renderItem={renderItem}
                            ListHeaderComponent={ListHeaderComponent}
                            ItemSeparatorComponent={ItemSeparatorComponent}
                            keyExtractor={(item, index) => index + ''}
                            ListFooterComponent={() => <View style={{ height: hp(20) }} />}
                        />
                    </View>
                </ScrollView>

            </View>
        </Modal>

Hello I found the following code in the repo swipe_error I don’t understand why we need to check this.props.scrollTo and scrollOffset> 0 But if I remove this checks everything is fine. also you can add for your modal scrollTo = {() => {}} and scrollOffset = {randomNumber} It helped me.

<Modal
         ...
        propagateSwipe={true}
        scrollTo={() => {}}
        scrollOffset={1}
        >
        <ScrollView> ... </ScrollView>
</Modal>

When I use swipeDirection my scrollView does not work even with the propagateSwipe, it does work when I do not include swipeDirection. I did update to v7.1.0-beta.1

I’ve been facing this issue as well. Tried your solution @rewieer but it didn’t work for me. Found a workaround for now. Hope it helps. <Modal> <ScrollView> <TouchableOpacity> ... </TouchableOpacity> </ScrollView> <Modal>

Wrapping the internal elements in a <TouchableWithoutFeedback> worked for me!

I created a component that intent to fix this scroll/swipe behaviour: https://github.com/jeremybarbet/react-native-modalize

It’s a common design pattern nowadays, and I had too many issues trying to make it work with react-native-modal, so I implemented from scratch.

You can do this if you would like to use View instead of TouchableWithoutFeedback

NOTE: If you are not using swipeDirection

<Modal>
  <ScrollView>
    <View flex={1} onStartShouldSetResponder={() => true}> // or flexGrow={1}
       YOUR CONTENT HERE
    </View>
  </ScrollView>
</Modal>

it works this solution, thanks dude.

I’ve been facing this issue as well. Tried your solution @rewieer but it didn’t work for me. Found a workaround for now. Hope it helps. <Modal> <ScrollView> <TouchableOpacity> ... </TouchableOpacity> </ScrollView> <Modal>

Wrapping the internal elements in a <TouchableWithoutFeedback> worked for me!

It seems like this solution only work for Android 😕

EDIT: Adding propagateSwipe={true} to Modal component solved my issue.

nothing work for me

use property propagateSwipe={true} in Modal and ScrollView and TouchableOpacity in your component.

<Modal testID={'modal'} isVisible={true} propagateSwipe={true} swipeDirection={[]}> <MapModal /> </Modal>

MapModal.js

<View> <ScrollView> <TouchableOpacity> ... </TouchableOpacity> </ScrollView> </View>

I’ve been facing this issue as well. Tried your solution @rewieer but it didn’t work for me. Found a workaround for now. Hope it helps. <Modal> <ScrollView> <TouchableOpacity> ... </TouchableOpacity> </ScrollView> <Modal>

Wrapping the internal elements in a <TouchableWithoutFeedback> worked for me!

This has solved the issue for me as well. I wrapped the elements inside the ScrollView and together with propagateSwipe this provided the correct output -> swipe to close still works and scrollview works as well! Thanks!

WORKING SOLUTION FOR ME:

        <Modal
            onShow={onShowModal}
            isVisible={showModal}
            onSwipeComplete={closeModal}
            swipeDirection={['down']}
            propagateSwipe={true}
            style={styles.modal}>
            <View
                style={styles.scrollableModal}>
                <View style={styles.tabWhite} />
                <ScrollView>
                    <View flex={1} onStartShouldSetResponder={() => true}>
                        <FlatList
                            data={DATA}
                            renderItem={renderItem}
                            ListHeaderComponent={ListHeaderComponent}
                            ItemSeparatorComponent={ItemSeparatorComponent}
                            keyExtractor={(item, index) => index + ''}
                            ListFooterComponent={() => <View style={{ height: hp(20) }} />}
                        />
                    </View>
                </ScrollView>

            </View>
        </Modal>

Thank you, it work for me

You can do this if you would like to use View instead of TouchableWithoutFeedback

NOTE: If you are not using swipeDirection

<Modal>
  <ScrollView>
    <View flex={1} onStartShouldSetResponder={() => true}> // or flexGrow={1}
       YOUR CONTENT HERE
    </View>
  </ScrollView>
</Modal>

This solution works for me! Tested in IOS, also I noticed that without the onStartShouldSetResponder={() => true} my Flatlist was only working when using 2 fingers to scroll the list inside the modal.

Heyoooooo, not the prettiest or ideal but it works on Android in lieu of my ScrollView. None of the above solutions worked for me unfortunately include removingClippedSubViews={false} and adding propagateSwipe={true} but this did the trick:

 <FlatList
        data={[{}]}
        horizontal={false}
        onEndThreshold={0}
        renderItem={({ item, index }) => (<View>Scrollable Content</View>)}
/>

The only way i was able to get it to work was by making the renderItem return a TouchableOpacity or TouchableWithoutFeedback

I’ve been facing this issue as well. Tried your solution @rewieer but it didn’t work for me. Found a workaround for now. Hope it helps. <Modal> <ScrollView> <TouchableOpacity> ... </TouchableOpacity> </ScrollView> <Modal>

@estebanmino Thanks dude… and you can add activeOpacity={1} to <TouchableOpacity> to avoid click effect when scroll starts.

propagateSwipe doesn’t work for me, I have a WebView inside a Modal

When I use swipeDirection my scrollView does not work even with the propagateSwipe, it does work when I do not include swipeDirection.

In my case, I was using react-native-snap-carousel and used a similar solution to @Mukhammadali

<Carousel
  data={songs}
  renderItem={renderSongPlayerImage}
/>

const renderSongPlayerImage = ({ item }: { item: Song }) => {
  return (
    <View style={{ flex: 1 }} onStartShouldSetResponder={() => true}>
      <Image
        source={{ uri: item.thumbnailUrl }}
        style={songPlayerFullStyles.image}
        resizeMode="contain"
      />
    </View>
 );
}

I’ve been facing this issue as well. Tried your solution @rewieer but it didn’t work for me. Found a workaround for now. Hope it helps. <Modal> <ScrollView> <TouchableOpacity> ... </TouchableOpacity> </ScrollView> <Modal>

Thank you this really helped, nice workaround 👍

I used React.memo to solve my issue.

@Mukhammadali @mguay22 Can someone please explain to me why onStartShouldSetResponder={() => true} works?? Shouldn’t it just scroll?

I created a component that intent to fix this scroll/swipe behaviour: https://github.com/jeremybarbet/react-native-modalize

It’s a common design pattern nowadays, and I had too many issues trying to make it work with react-native-modal, so I implemented from scratch.

@jeremybarbet Dude, just wanted to say great work here. I was able to just drop in your library as a replacement and it does exactly what I needed. Thank you!

Hi, I have the same problem and after a long search I found the following. Think that’s the right one 😃 https://reactnativeexample.com/a-modal-that-loves-scrollable-content/

Hi, everyone. I also came across this issue when trying to present a horizontal FlatList as part of the modal’s content. Despite trying the props mentioned, the thing that finally got it working was to wrap each rendered item in the list in a TouchableOpacity. For the sake of testing the layout quickly, I was rendering each item first as a View and these weren’t scrollable.

Probably not quite as good of a solution as above, but I forked the repo and made it such that the client gets to choose whether the pan responder should start or not. Here is the commit: https://github.com/zsaraf/react-native-modal/commit/9ddebe9e1d258c6a4d9aed45e41f49c70abef1f0

An example usage might look like:

onStartShouldSetPanResponder={(evt, gestureState) => (evt.nativeEvent.pageY < screenHeight - (MODAL_CONTENT_HEIGHT))}

This would make it such that the modal can only be swiped down from above the content. Just one example of how you could use it! (This example was for a modal that comes up from the bottom)

I need to look at this a little closer to understand. It seems like swiping in a direction other than the swipeDirection should have no effect on the modal and the swipe event should propagate to the children components (which is what seems to happen when I remove the line you asked about). I’ll try to think of the best way to solve this 😃

Yes I have a modal where swipeDirection is left with a vertically scrollable menu inside

A bit hacky, but I added an empty view below my content with marginBottom: 120 and it works on both iOS and Android.

<Modal>
  <ScrollView>
    YOUR CONTENT HERE
    <View style={{marginBottom: 120}}></View>
  </ScrollView>
</Modal>

You can do this if you would like to use View instead of TouchableWithoutFeedback

NOTE: If you are not using swipeDirection

<Modal>
  <ScrollView>
    <View flex={1} onStartShouldSetResponder={() => true}> // or flexGrow={1}
       YOUR CONTENT HERE
    </View>
  </ScrollView>
</Modal>

This works for me for fixing horizontal scroll problem. The crucial part in this solution is to set the onStartShouldSetResponder={() => true} prop for the View component.

The following 3 things fixed it for me!

<Modal
      ...,
      propagateSwipe // 1
>
  <ThemedScrollView style={styles.scrollContainer} propagateSwipe> // 2
    <View onStartShouldSetResponder={() => true}> // 3
      {content}
    </View>
  </ThemedScrollView>
</Modal>

styles.scrollContainer is just

  scrollContainer: {
    flex: 1,
    width: PAGE_WIDTH,
    alignSelf: "center",
  },

If you’re using a ScrollView or FlatList, what worked for me is changing:

contentContainerStyle={{ height: "100%" }}

to

style={{ height: "100%" }}

on the FlatList/ScrollView

for me i just remove the “useNativeDriverForBackdrop” from modal and add “propagateSwipe={true}” in modal and it’s work for me.

You can do this if you would like to use View instead of TouchableWithoutFeedback NOTE: If you are not using swipeDirection

<Modal>
  <ScrollView>
    <View flex={1} onStartShouldSetResponder={() => true}> // or flexGrow={1}
       YOUR CONTENT HERE
    </View>
  </ScrollView>
</Modal>

it works this solution, thanks dude.

thanks man, it works for me!, i’ve been trying all the above solutions here and nothing worked

I used React.memo to solve my issue.

Hi there! How did you do it? Just tried and it didn’t work for me… I’m using swipeDirection

Hi! You decided ? I can not use swipDirection and ScrollView at the time

On iOS found that if you wrap the content of the modal in a TouchableOpacity, the scroll functionality is enabled, even if you have swipeDirection props.

Good luck!

I also have the same issue.

Maybe my issue similar to this. I have react-native-modal and react-native-looped-carousel inside (fullscreen. Means that carousel takes all screen and listen events everywhere).

    <Modal backdropOpacity={1} swipeDirection={['up', 'down']} scrollHorizontal>
        <Carousel autoplay={false} style={size}>
        ....
        </Carousel>
    </Modal>

This code works mostly fine on Android. I can swipe carousel to left/right. And close model with up/down gesture. But on iOS carousel still not working. I can only close modal

@Hirbod Yes, it’s possible. Check out this example and this props. If you have any further question, you can create an issue on the repository itself 👍

Same issue, actually I am using propagateSwipe and it doesn’t work 😦

@mmazzarolo checkout https://github.com/chohonest/MyModalProj.git and let me know if you are having the issue when you click one of the cards that open the modal and try to swipe the horizontal cards

Yep, @mmazzarolo works great, thanks for pushing this out!

Ahh, you are right. It does have an issue if I just remove that line. I haven’t had any issues where the tap wasn’t working, but rather swiping down on the modal only worked on parts of the modal that aren’t touchable.

What do you think of propagateSwipe or allowChildSwipe as names for such a prop? Or is hasScrollView more understandable?

@cjroth thank you for the feedback.
That check is needed to make the modal content respond correctly to the swipes. Are you using supplying the swipeDirection to the modal? 🤔 Unless the modal is swipeable it shouldn’t run that check.

Hi @quachsimon ! Thank you for reporting the issue 👍 If you have some time could you check if removing return Math.abs(gestureState.dx) >= 4 || Math.abs(gestureState.dy) >= 4; fixes your issue?

@mmazzarolo removing that line fixes it for me.