react-native-gesture-handler: Pan gesture inside a ScrollView blocks scrolling
Description
I have a use case where I have a draggable element which I implement using Pan Gesture. This element is being rendered inside a ScrollView and block scrolling when attempting to scroll in the pan area (of the element)
Assuming that the pan conflicts with the scroll I tried to approach it differently and added a LongPress gesture that once started enables the panning, so as long as the user didn’t long press the element, the pan gesture should not block the scrolling.
I pretty much implemented this example with minor changes https://docs.swmansion.com/react-native-gesture-handler/docs/gesture-composition#race
This is the LongPress gesture
const longPressGesture = Gesture.LongPress()
.onStart(() => {
isDragging.value = true;
});
This is the Pan gesture
const dragGesture = Gesture.Pan()
.onStart(() => {...})
.onUpdate(event => {....})
.onFinalize(() => {
isDragging.value = false;
})
.simultaneousWithExternalGesture(longPressGesture);
And finally
const composedGesture = Gesture.Race(dragGesture, longPressGesture);
<GestureDetector gesture={composedGesture}>
<View reanimated>{props.children}</View>
</GestureDetector>
I was thinking on invoking dragGesture.enabled(false/true)
to enable/disable the panning, but TBH, I’m not sure where to call it.
Any ideas how to approach this?
Platforms
- iOS
- Android
- Web
Screenshots
Steps To Reproduce
- Either use the code mention in the description in order to reproduce the full problem, or use the one from this guide and wrap the component with a ScrollView
- The code snippet below is more focused on the issue with a ScrollView that wraps a pan gesture component and how it blocks the scrolling
Expected behavior
Actual behavior
Snack or minimal code example
This is a small code snippet that demonstrate the general issue of pan/scroll not working together. Scrolling on the DragComponent area will not work, only below it will work
const DragComponent = props => {
const gesture = Gesture.Pan();
return (
<GestureDetector gesture={gesture}>
<View reanimated>{props.children}</View>
</GestureDetector>
);
};
const Main = () => {
return (
<ScrollView>
<DragComponent>
<View style={{width: '100%', height: 400, backgroundColor: 'red'}}/>
</DragComponent>
</ScrollView>
);
}
Package versions
- React: 17.0.2
- React Native: 0.66.4
- React Native Gesture Handler: 2.3.0
- React Native Reanimated: 2.4.1
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 3
- Comments: 16 (4 by maintainers)
Hi! Gestures blocking scrolling when they are placed inside ScrollView is by design, otherwise you wouldn’t be able to use them. The idea of requiring
LongPress
beforePan
can activate is a good way to get around that, but unfortunately, you cannon accomplish this usingRace
andSimultaneous
modifiers. You can accomplish this using touch events:Hey all, just thought I’d let you know I found another way which only requires a single Gesture plus doesn’t require the long press trigger (tested on ios sim + physical device but not Android):
Amazing! Thank you! I ended up combining your suggestion with my implementation.
I wanted to avoid implementing a LongPress behavior so I did the following and it works great!
Oh, sorry I didn’t notice that. I’ve tried Android first and the gestures didn’t work at all without the root view so I figured it must’ve been it 😅.
As for your question, yes you can make
ScrollView
work simultaneously with its children gestures usingsimultaneousHandlers
prop. It accepts an array of references to the gestures so you will need to slightly modify your code.Here's what I did
The bad news is, that you may need to eject to see it working as there is a problem with relations between gestures in the Expo Go app (it will work on the production build and there is a possibility that it will work when using a custom dev client).
ı removed state.fail() and added it to onTouchesUp callback but it also works without state.fail(). I tested it android physical device and fully worked.
Hello @j-piasecki, how do you get this to work with a third-party list library (say flash-list)? These libraries do not support
simultaneousHandlers
prop.Hi everyone, this is my solution. In all previous solutions, I’ve encountered a problem: the action fails if my finger doesn’t move straight. My idea is to check isHorizontalPanning only within 100ms so that your finger can move smoothly without worrying about being perfectly straight
P/S. Give me a reaction if you like this.
@j-piasecki That did the trick! Thank you so much 🎉
Since I got it to work simultaneously using this solution I even removed the long press gesture and only have the pan gesture now which is no longer manually activated. Now the pan and scrolling works perfectly at the same time.
For me this even works in the Expo Go shell but I’ll keep in mind that if gesture logic looks off in Expo Go then I should also try a dev-client build to see if it persists.
Great! And yeah, your solution looks much cleaner. Since you’ve solved the problem, I’ll close the issue.