Scroll Bug in NestableScrollContainer
This package has external dependencies of react-native-reanimated and react-native-gesture-handler which must be installed separately. Before opening an issue related to animations or gestures please verify that you have completed ALL installation steps, including the changes to MainActivity.
Describe the bug When using NestableScrollContainer with react-native-draggable-flatlist, I noticed that if I try to scroll in a different direction while a scroll motion is still ongoing, it works fine. However, if I wait for the scroll to complete and then attempt to scroll again, depending on the gesture, it often fails to scroll.
To Reproduce This issue can be reproduced in the official Snack: "https://snack.expo.dev/@computerjazz/draggable-flatlist-examples". Navigate to the "Nested" Screen. Try scrolling up and, before the scroll completes, scroll down (or vice versa). It works as expected. However, if you wait for the scroll to complete and then attempt to scroll again, you will encounter the issue.
Platform & Dependencies Please list any applicable dependencies in addition to those below (react-navigation etc).
- react-native-draggable-flatlist version: 4.0.1
- Platform: iOS
- React Native or Expo version: 0.72.3
- Reanimated version: 3.4.2
- React Native Gesture Handler version: 2.12.1
Additional context I have also tried replacing NestableDraggableFlatList with a regular FlatList, and it worked without issues. This leads me to believe that the problem might be specific to NestableDraggableFlatList.
+1
I am having the same issue. The frequency of the bug seems do decrease as the value of the activationDistance prop increases
This seems to be caused by the ScrollView conflicting with the GestureDetector component that DraggableFlatList is using internally. Removing the gesture detector fixes the scroll issue (but breaks everything else obviously)
This issue in the react-native-gesture-handler repo seems relevant. I haven't found a workaround yet but will update here if I do.
I was able to find a workaround -> https://github.com/CarterSimonson/react-native-draggable-flatlist/pull/1
Here's what I ended up doing:
- Update the
PanHandlerinDraggableFlatListto utilize anenabledstate.- Set the pan handler to manual activation mode
- Compose a simultaneous
tapGesturethat replaces the "drag" handler returned to list items- Set the shared
enabledstate to true when thetapGestureis pressed down. - Reset
enabledto false on press up.
- Set the shared
I initially tried controlling the enabled state from within the drag function but was getting some gnarly race conditions.
This results in the following behavior:
- If the user attempts to drag without triggering the
tapGesture, thepanGesturewill fail and allow the regular scroll event. - If the user drags on the tappable area (the drag icon for me in this case), the
tapGesturewill fire simultaneously and set theenabledstate to true. ThepanGesturewill activate and track the pan events.
This works but unfortunately required a breaking change to the API.
Demo:
https://github.com/computerjazz/react-native-draggable-flatlist/assets/31598368/479c7be2-ee7c-42cc-8741-851cfcc56608
Hi @CarterSimonson could you provide some of the code of how you got this working? It doesn't seem your PR fixed this issue by default or I think I'd be seeing my issue resolved unless I need to update react-native-gesture-handler library to the latest. Only solution I've found to this is increasing activationDistance by a gross amount and lowering it when dragging item
Hey @SethCram,
My goal was to only allow the elements to be dragged when a button on each of them was clicked and dragged:
Here's a code snippet of the workaround from my component that wraps this drag button on each of my list items. The only change to the way the library is used is that a tapGesture from RN gesture handler is passed as an argument to renderItem. The drag event will not begin unless the tapGesture is fired first, which prevented the scrolling bug in my case.
import { GestureDetector } from 'react-native-gesture-handler';
// Part of the workaround is that a `tapGesture` is now passed as an argument of the renderItem function:
function renderItem({ item, getIndex, tapGesture }: RenderItemParams<KeyedString>) {
const index = getIndex();
// The dragging logic will only begin if this tapGesture is fired beforehand
const right = (
<GestureDetector gesture={tapGesture}>
<DragIcon />
</GestureDetector>
);
if (index !== undefined) {
return (
<InputListItem
value={item.value}
onChangeText={(value) => updateIngredient(value, index)}
onBlur={() => onBlur(index)}
right={right}
/>
);
}
}
return <NestableDraggableFlatList
data={ingredients}
onDragEnd={({ data }) => onChange(data)}
keyExtractor={(value) => value.id}
renderItem={renderItem}
/>
One limitation of this is that it doesn't prevent the scrolling bug if the entire list item is a clickable area (like it is in this demo). It takes advantage of the fact that I only needed to support drag events on a small section of each list item.
Hopefully that clears some things up, I am happy to elaborate further if not 😄