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

ScrollView inside swipeable modal is not working #236

Open
quachsimon opened this issue Jan 9, 2019 · 92 comments
Open

ScrollView inside swipeable modal is not working #236

quachsimon opened this issue Jan 9, 2019 · 92 comments

Comments

@quachsimon
Copy link

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?

@mmazzarolo mmazzarolo added the bug label Jan 10, 2019
@mmazzarolo
Copy link
Member

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?

@cjroth
Copy link
Contributor

cjroth commented Feb 4, 2019

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.

@mmazzarolo
Copy link
Member

mmazzarolo commented Feb 4, 2019

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

@cjroth
Copy link
Contributor

cjroth commented Feb 4, 2019

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

@mmazzarolo
Copy link
Member

@cjroth got it, at least it doesn't seem to be a bug then.
To make it work correctly both with a nested ScrollView and with a simple View we might want to control that specific bit of code trough a new prop.
Any suggestion on how to do it/name it? Anyone willing to give it a shot with a PR?

Honestly, I feel like adding props for such cases is not the best pattern... but that's what we get when we try to do too many things in a single component 😁

@cjroth
Copy link
Contributor

cjroth commented Feb 4, 2019

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

@mmazzarolo
Copy link
Member

mmazzarolo commented Feb 4, 2019

To add a bit more context, the following check:

return Math.abs(gestureState.dx) >= 4 || Math.abs(gestureState.dy) >= 4;

Was added to make sure that when you press on a button inside a swipeable modal the touch won't be interpreted as a "swipe". This is probably swallowing your scrollview gesture.

Following your tests, adding a prop like hasScrollView and wrapping that check this way:

if (hasScrollView) {
  return Math.abs(gestureState.dx) >= 4 || Math.abs(gestureState.dy) >= 4;
}

should be enough to fix it, but it's also an unintuitive solution (the best way to handle it would be making the touch propagation work correctly without doing that check... but that's a different story 😀

@cjroth
Copy link
Contributor

cjroth commented Feb 4, 2019

I'm actually finding that it works perfectly without the return Math.abs(gestureState.dx) >= 4 || Math.abs(gestureState.dy) >= 4; check - I suspect that this is because even if taping does get interpreted as a swipe, it isn't visibly noticeable and definitely doesn't come near the threshold that triggers dismissing the modal.

A hasScrollView or similarly named prop would make sense if the check is needed.

@mmazzarolo
Copy link
Member

@cjroth 🤔 thanks for checking.
Did you try with a few buttons in a simple View (not ScrollView) on a real device?
If I recall correctly before the PR was merged I tested with a bunch of buttons in the modal and sometimes the tap was not working correctly (on a real device, not an emulator).

@cjroth
Copy link
Contributor

cjroth commented Feb 5, 2019

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?

@mmazzarolo
Copy link
Member

Hey! @cjroth has been published on v7.1.0-beta.1, willing to give it a shot?

@cjroth
Copy link
Contributor

cjroth commented Feb 7, 2019

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

@chohonest
Copy link

chohonest commented Feb 8, 2019

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

@mmazzarolo
Copy link
Member

@chohonest mind creating a small repo to reproduce the issue?

@mmazzarolo mmazzarolo changed the title Update from v6.5.0 to v7.0.2 breaks scrolling inside modal ScrollView inside swipeable modal is not working Feb 8, 2019
@mmazzarolo mmazzarolo reopened this Feb 8, 2019
@chohonest
Copy link

@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

@mmazzarolo
Copy link
Member

@chohonest yeah, it looks like the fix was not enough :/

@2021debaisen
Copy link

@chohonest I am facing something similar to this, although I am using propagateSwipe. Basically, any fast flicking motion when trying to move my flatList up somewhat triggers my swipe when swipe direction is set to left.

@german970814
Copy link

Same issue, actually I am using propagateSwipe and it doesn't work :(

@lashansam
Copy link

Facing the same problem. Looking for any updates.

@ancyrweb
Copy link
Member

ancyrweb commented Mar 7, 2019

I've published a solution into fix-scroll. I've noticed that we don't handle horizontal scrolls.

Please @german970814 @lashansam @chohonest can you test and tell me if that solves your problem ? Don't forget to that scrollHorizontal={true} into the Modal component and use event.nativeEvent.contentOffset.x in the onScroll prop.

@estebanmino
Copy link

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>

@mazelos
Copy link

mazelos commented Dec 3, 2020

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 👍

@BogdanRad
Copy link

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.

@angellom90
Copy link

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.

@Ajmal0197
Copy link

Ajmal0197 commented Mar 28, 2021

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>

@kamal-mobile-dev
Copy link

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>

Thanks, it's working for me

@mguay22
Copy link

mguay22 commented May 21, 2021

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

@MandipGiri
Copy link

In my case what worked was the combinations of solutions mentioned above.Here is how it worked for me dropping this here if it's help to anyone.

 <Modal
      style={styles.container}
      isVisible={isVisible}
      useNativeDriverForBackdrop
      hasBackdrop={false}
      onSwipeComplete={() => setisVisible(false)}
      swipeDirection={['down']}
      animationOut="fadeOut"
      propagateSwipe={true}>
      <View style={styles.whiteContainer}>
        <View style={styles.groove} />
        <ScrollView showsVerticalScrollIndicator={false}>
          <TouchableOpacity activeOpacity={1}>
            <View
              style={styles.scrollChildWrapper}
              onStartShouldSetResponder={() => true}>
              <FlatList
                style={styles.flatlist}
                showsVerticalScrollIndicator={false}
                keyExtractor={(item, index) => index.toString()}
                data={MOCK_WORKERS}
                renderItem={({item, index}) => <WorkerItem data={item} />}
                contentContainerStyle={styles.contentContainer}
                ItemSeparatorComponent={() => <View style={styles.separator} />}
              />
            </View>
          </TouchableOpacity>
        </ScrollView>
      </View>
    </Modal>

This is for the swipeable modal this also worked without the TouchableOpacity if the child components had enough horizontal padding but this was what worked for me the best.

@Alexyzg
Copy link

Alexyzg commented Jun 4, 2021

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>

@joaorodrs
Copy link

joaorodrs commented Jul 5, 2021

I used React.memo to solve my issue.

I've no idea why, but wrapping my custom Modal component with memo and not setting swipeDirection solved for me.

I just exported as a memoized component: export default React.memo(MyModal), and my Flatlist was working (I didn't try with ScrollView though).

My code

@xotahal
Copy link

xotahal commented Aug 6, 2021

@Alexyzg Also found that weird. I created PR so we'll see ;) #587

@DungP270999
Copy link

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

@Aszurar
Copy link

Aszurar commented May 16, 2022

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

@Malintan2112
Copy link

Malintan2112 commented Aug 9, 2022

its work for me @mmazzarolo

Screen Shot 2022-08-09 at 19 51 51

Screen Shot 2022-08-09 at 19 52 06

@NaitikDartexon
Copy link

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

@adembacajdev
Copy link

for me, adding propagateSwipe fixed the issue

@zenshayan
Copy link

zenshayan commented Oct 12, 2022

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

@YosefOberlander
Copy link

YosefOberlander commented Oct 16, 2022

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

@shmkane
Copy link

shmkane commented Dec 1, 2022

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

@lifeitech
Copy link

lifeitech commented Dec 4, 2022

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.

@itekhi
Copy link

itekhi commented Dec 6, 2022

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

@jamieGardyn
Copy link

I have FlatList with horizontal scroll as true and paging enabled so it works like a carousel, but swiping does not work in the modal. I put the renderItem View container with onStartShouldSetResponder={() => true} which does work in modals with ScrollViews and even FlatList if pagingEnabled=false, but it does not work here. Thoughts?

<Modal
      style={styles.modalBackdrop}
      backdropOpacity={0.5}
      swipeDirection="down"
      propagateSwipe
      onSwipeComplete={closeModal}
      transparent={true}
      isVisible={modalVisible}
    >

        <View style={styles.modal}>
            <View style={[styles.modalView, extraStyles]}>
 <FlatList
        ref={refContainer}
        data={data}
        renderItem={({ item }) => <Slide item={item} />}
        keyExtractor={(item) => item.id + 'key'}
        horizontal={true}
        pagingEnabled={true}
        onViewableItemsChanged={handleVieweableItemsChanged}
        viewabilityConfig={{
          itemVisiblePercentThreshold: 90,
        }}
        showsHorizontalScrollIndicator={false}
        style={styles.slideContainer}
      />
            </View>
        </View>
  </Modal>

@JustinHaut
Copy link

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>

@sebastienlabine
Copy link

Any plans to support this natively? I always end up with a flatlist inside a scroll view
causing this warning

VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead.

@alaughlin
Copy link

all of these fixes break a horizontal swipeDirection. is there any way to allow vertical scrolling inside the modal and allow swiping left/right to close the modal?

@eserbina652
Copy link

Hey everyone, I've been resolve this issue by adding onScroll={(event) => event.preventDefault()} to the ScrollView, and also propagateSwipe={true} to the Modal.

@Yeasirarafat53
Copy link

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>

Thanks , it's worked for me

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.