-
-
Notifications
You must be signed in to change notification settings - Fork 5k
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 in modal prevents modal from dismissing sometimes #7154
Comments
it's not really possible for react-navigation to guess when you may or may not want to close the modal depending on other gestures you have inside of a scene, you need to define that behavior for yourself. you can access the gesture context like this: https://github.com/react-navigation/react-navigation-stack/blob/5f157cbc16f2dd15363304ab3c78663b0ed36de0/example/src/GestureInteraction.js#L55-L56 then you can use react-native-gesture-handler tools to make this behave how you like. |
reopening because i think it's important to have a good example of how to do this |
Hey @skhavari! If you still need help with this, check out this snack: https://snack.expo.io/@rgilbert/scrollable-modal-with-dismissal |
@ryan-gilb many thanks for your solution! I just tried out your snack and maybe I'm mistaken, but does it not allow to dismiss the modal by pulling down in the scroll view itself as iOS 13 would do? As a reference @iamdavidmartin wrote this article that illustrates the problem (see the left demo): https://medium.com/@iamdavidmartin/react-native-navigation-gestures-could-become-a-major-problem-on-ios-13-5180d02a3cc8 Thanks for your help! Edit: Similar to the workaround in the post, with this repo we should be able to circumvent this limitation: https://github.com/osdnk/react-native-reanimated-bottom-sheet |
Hi guys, I have the same issue. Could you find a solution to this problem? I'm in iOS 13 with a modal and a ScrollView and I can't dismiss the modal swiping down. |
Same issue here, is this issue prioritized or should we rely on another lib like https://github.com/osdnk/react-native-reanimated-bottom-sheet ? |
I've been trying to figure something here based on https://snack.expo.io/@rgilbert/scrollable-modal-with-dismissal. Something like: Screen.navigationOptions = ({ navigation }) => ({
gestureEnabled: navigation.getParam('gestureEnabled', true),
gestureResponseDistance: {
vertical: screenHeight
}
}) const isGestureEnabled = getParam('gestureEnabled')
const scrolledTop = useRef(true)
...
const onScroll = useCallback(
({ nativeEvent }) => {
scrolledTop.current = nativeEvent.contentOffset.y <= 0
if (isGestureEnabled !== scrolledTop.current) {
setParams({ gestureEnabled: scrolledTop.current })
}
},
[isGestureEnabled]
) <GestureHandlerRefContext.Consumer>
{ref => (
<FlatList // from 'react-native-gesture-handler'
waitFor={shouldWaitForGesture ? ref : undefined}
... It kinda works (might fail sometimes). Obviously, you still need to lift your finger up and swipe again (which can be ok) to dismiss the modal but also because you need to update Wondering if you figured out something @brentvatne (as you are assigned) or if you feel this is more a |
hey all! we have been working with @osdnk on the slack-pan-sheet-react-native-experiment package to achieve the effect/requirements that yall have been discussing in this thread. https://github.com/osdnk/slack-pan-sheet-react-native-experiment read his twitter thread for more context https://twitter.com/mosdnk/status/1233097798251425795 |
What I ended up doing was set position absolute on the scrollable content and then gave a top to it so the scroll event is not called when the user dismiss the modal from the top |
Hey guys, I'm facing the same problem with modal screens from my stackNavigator. Can somebody help my with a short example of how am I supposed to do in react navigation 5? Many thanks in advance, it is really kind of a blocker... |
Just use DismissableFlatList as normal FLatList // DismissableFlatList.js
import React, { useState, useCallback } from 'react';
import { GestureHandlerRefContext } from '@react-navigation/stack';
import { FlatList } from 'react-native-gesture-handler';
const DismissableFlatList = props => {
const [scrolledTop, setScrolledTop] = useState(true);
const onScroll = useCallback(({ nativeEvent }) => {
const scrolledTop = nativeEvent.contentOffset.y <= 0;
setScrolledTop(scrolledTop);
}, []);
return (
<GestureHandlerRefContext.Consumer>
{ref => (
<FlatList // from 'react-native-gesture-handler'
waitFor={scrolledTop ? ref : undefined}
onScroll={onScroll}
scrollEventThrottle={16}
{...props}
/>
)}
</GestureHandlerRefContext.Consumer>
);
};
export default DismissableFlatList; |
I've been using this with success as a replacement for import {GestureHandlerRefContext} from '@react-navigation/stack';
import React, {PropsWithChildren, useCallback, useState} from 'react';
import {ScrollViewProps} from 'react-native';
import {ScrollView} from 'react-native-gesture-handler';
export const DismissableScrollView = (
props: PropsWithChildren<ScrollViewProps>,
) => {
const [scrolledTop, setScrolledTop] = useState(true);
const onScroll = useCallback(({nativeEvent}) => {
setScrolledTop(nativeEvent.contentOffset.y <= 0);
}, []);
return (
<GestureHandlerRefContext.Consumer>
{(ref) => (
<ScrollView
waitFor={scrolledTop ? ref : undefined}
onScroll={onScroll}
scrollEventThrottle={16}
{...props}
/>
)}
</GestureHandlerRefContext.Consumer>
);
}; |
How are you using this to solve the problem? I get that this sets the scrolledTop to true. But what happens when that value is true? How are you consuming that value? |
After implementing so many libraries, this one seems to provide a fluid scroll effect and close the modal: https://github.com/jeremybarbet/react-native-modalize |
It is work for me. |
I had been racking my brain on this for a while. The following solution works reliably for me, though it still doesn't allow for a nice "pull down" feel (where you can slowly pull the modal closed). While that's mostly a polish issue, this allows you to a) scroll within the modal; b) dismiss the modal by pulling down, provided you're at the top of the modal (the common/standard use case); and c) do it without requiring a gesture handler library:
|
@raffibag the code shared above still works as of React Navigation 6. And it has the nice pull down feel as well. One important thing for everything to work is to set |
same problem with scrollview/flatlist in modal, try to use this code, but my gesture ref always null
what i do wrong? navigation not used react-native-gesture-handler? expo 43 (dev client) |
oh, i see )) |
I used this and worked wonderfully. But now I have multiple content inside and sometimes it just tries to pull down the modal even though I am in the middle of the screen and I am trying to scroll to top. The
|
My working (but non-optimal) solution:
It will close the modal when a drag down event at the top finishes, and the user has to use some extra drag speed to close it, not just bump the top lightly. |
We're using a native stack navigator and can't get the above with |
I can't get it to work either, and I don't really understand it to be honest. If we're waiting on the gesture handler from context when the offset is 0, how is it supposed to be able to scroll down? 🤔 |
The logic behind it is the following:
Number 3 doesn't work if you have a newer version of gesture handler installed since minOffsetY is deprecated. I think the only thing you can do for now is downgrade react-native-gesture-handler (to 2.2.0) or patch it manually in react-navigation/packages/stack/src/views/Stack/Card.tsx And wait for react-navigation to be compatible with react-native-gesture-handler > 2.2.0 Update: I jumped the gun a bit here. It's not mainly the deprecated properties causing this, but the fact that there is no failOffsetY specified. I opened an issue about this here In short, you can fix this by updating lines 392-406 in Card.tsx to the following, and then use something like npm patch package.
|
I tried using the gestureResponseDistance but it doesn't exist in the screen options. I'm using --Packages-- |
You saved my life |
It's a bodgey workaround but this is how I achieved it to make sure it dismiss the window and go back only if you are at the top of the scrollview: const isAtTopOfScrollview = useRef<boolean>(true);
const onScrollBegin = useCallback<NonNullable<ScrollViewProps['onScrollBeginDrag']>>(({ nativeEvent }) => {
isAtTopOfScrollview.current = nativeEvent.contentOffset.y < 30;
}, []);
const onScroll = useCallback<NonNullable<ScrollViewProps['onScroll']>>(
({ nativeEvent }) => {
if (navigation.canGoBack() && nativeEvent.contentOffset.y < -40 && isAtTopOfScrollview.current) {
navigation.goBack();
}
},
[navigation]
);
return (
<ScrollView
onScrollBeginDrag={onScrollBegin}
onScroll={onScroll}
scrollEventThrottle={16}
/>
) |
According to comments above, I've come to solution where you can scroll down from top and back, and close modal screen only when scroll position is reached the top. ...
import { ScrollView } from 'react-native-gesture-handler'
import { GestureHandlerRefContext } from '@react-navigation/stack'
....
const ModalScreen = () => {
...
const [isOnTop, setIsOnTop] = useState(true)
const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
const offset = event.nativeEvent.contentOffset.y
const newValue = offset <= 10
if (isOnTop !== newValue) {
setIsOnTop(newValue)
}
}
....
return (
<View>
...
<GestureHandlerRefContext.Consumer>
{(ref) => (
<ScrollView
simultaneousHandlers={isOnTop ? ref : undefined}
bounces={false}
scrollEventThrottle={16}
onScroll={onScroll}
>
...
</ScrollView>
)}
</GestureHandlerRefContext.Consumer>
</View>
)
} The trick is that |
You are the man! |
Current Behavior
If the scroll view is at the top (offset 0) and use user swipes down over the scroll view, sometimes the gesture triggers an over-scroll and sometimes a dismissal of the modal. A tap and brief hold followed by swipe down triggers a dismissal as expected. A tap and quick swipe down has variable results; sometimes a dismissal, sometimes an overs croll (bounce=true).
Expected Behavior
When the scroll offset is 0, every swipe down over the scroll view should being transitioning to dismiss the modal.
How to reproduce
Simple repro here: https://snack.expo.io/@skhavari/swipe-to-dismiss
Thanks
The text was updated successfully, but these errors were encountered: