computerjazz / react-native-draggable-flatlist

A drag-and-drop-enabled FlatList for React Native
MIT License
1.92k stars 402 forks source link

Having conflicts with parent ScrollView #184

Closed bcklup closed 2 years ago

bcklup commented 4 years ago

Describe the bug When you nest this component inside a ScrollView, dragging around the area of this component won't scroll the ScrollView.

To Reproduce Have a DraggableFlatlist component inside a View with the ScrollView as it's parent (ScrollView -> View -> DraggableFlatList). When you try to scroll the ScrollView around the region of the DraggableFlatList it will not respond.

Platform & Dependencies Please list any applicable dependencies in addition to those below (react-navigation etc).

Additional context This used to work on the pre-RN 0.60 versions of this component so I'm confused why the existing functionality no longer works.

computerjazz commented 4 years ago

Duplicate of https://github.com/computerjazz/react-native-draggable-flatlist/issues/106 But keeping this one as it has the issue template filled out

user14045 commented 4 years ago

any update on this? This lib is great. And it gonna be very great if this issue solved. I found another library, https://github.com/thomasrovayaz/react-native-draggable-dynamic-flatlist. I can scroll inside the scrollview. But, the drag-drop not working

Here code that can be reproduced in snack.expo.io

import React, { Component } from "react";
import { ScrollView, TouchableOpacity, Text } from "react-native";
import DraggableFlatList from "react-native-draggable-flatlist";

const exampleData = [...Array(20)].map((d, index) => ({
  key: `item-${index}`, // For example only -- don't use index as your key!
  label: index,
  backgroundColor: `rgb(${Math.floor(Math.random() * 255)}, ${index *
    5}, ${132})`
}));

class Example extends Component {
  state = {
    data: exampleData
  };

  renderItem = ({ item, index, drag, isActive }) => {
    return (
      <TouchableOpacity
        style={{
          height: 100,
          backgroundColor: isActive ? "blue" : item.backgroundColor,
          alignItems: "center",
          justifyContent: "center"
        }}
        onLongPress={drag}
      >
        <Text
          style={{
            fontWeight: "bold",
            color: "white",
            fontSize: 32
          }}
        >
          {item.label}
        </Text>
      </TouchableOpacity>
    );
  };

  render() {
    return (
      <ScrollView>
        <DraggableFlatList
          data={this.state.data}
          renderItem={this.renderItem}
          keyExtractor={(item, index) => `draggable-item-${item.key}`}
          onDragEnd={({ data }) => this.setState({ data })}
        />
      </ScrollView>
    );
  }
}

export default Example;
ralppppy commented 4 years ago

Is there any update on this? I broke my layout because of this issue. I've already tried react-native-draggable-dynamic-flatlist but it is not as good as this. I think the list should be scrollable when you are not dragging, and when you start dragging that's the time to disable scroll.

computerjazz commented 4 years ago

The underlying issue is that a react-native-gesture-handler PanGestureHandler captures the gesture from the parent ScrollView. I haven't had time to investigate and it'd be great if someone else could.

https://github.com/software-mansion/react-native-gesture-handler/issues/1070

JosephCon1998 commented 4 years ago

I'm using a workaround that works but its probably not ideal and might not work for everyones use case.

So anything I want to be able to scroll as well as drag and I am putting inside the ListHeaderComponent for the FlatList

<DraggableFlatList
        ListHeaderComponent={() => (
           <>
              // Every component above the draggable list I want to scroll
           </>
        )}
        renderItem={({ item, index, drag, isActive }) => (
          <MyDraggableComponent />
        )}
       ListFooterComponent={() => (
           <>
              // Every component below the draggable list i want to scroll
           </>
       )}
/>

This works for me with how my app is using the draggable list, but again its not ideal for everybody but hope it helps someone.

Also make sure to get rid of the parent scrollview

rednebmas commented 4 years ago

My cells have a "drag handle" and my workaround for this issue was to add a dragHitSlop property to DraggableFlatList and pass that down to the PanGestureHandler as hitSlop in render .

bill-pairaktaridis commented 4 years ago

My cells have a "drag handle" and my workaround for this issue was to add a dragHitSlop property to DraggableFlatList and pass that down to the PanGestureHandler as hitSlop in render .

Could you elaborate on that?

ds-karpov commented 4 years ago

My cells have a "drag handle" and my workaround for this issue was to add a dragHitSlop property to DraggableFlatList and pass that down to the PanGestureHandler as hitSlop in render .

Dear rednebmas, could you please add detailed instruction of your solution? Thanks

voidrender commented 4 years ago

I've found a workaround, but it seems to sometimes block vertical scrolling. Set the activationDistance prop (on DraggableFlatList) to a value greater than zero. In my testing, even 1 is enough to allow swiping horizontally within the parent scroll view without interfering with drag and drop functionality, but 10 provides more consistent scrolling within the DraggableFlatList.

mtalhagaoglu commented 4 years ago

hi. i have the same bug. when there is too much item for screen height it hiding. i can't scroll to bottom. i tried activationDistance but if i do that i am not able to drag items.

JairZijp commented 3 years ago

Yeah same problem as @mtalhagaoglu here. When I add activationDistance the scrolling works but the dragging doesn't

mtalhagaoglu commented 3 years ago

Yeah same problem as @mtalhagaoglu here. When I add activationDistance the scrolling works but the dragging doesn't

my problem caused because parent scrollview. when i remove that it starts to work as should be.

Svarto commented 3 years ago

@bill-pairaktaridis @ds-karpov created pull requests adding in hitSlop parameter to allow for @rednebmas solution. #232 #231

This allows for controlling what part of the DraggableFlatlist responds to gesture events. Meaning if you disable it, the parent ScrollView will take precedence and continue to work. I have solved it by creating a handle that activates with a toggle, if the toggle is false then the parent ScrollView scrolls normally, if it is true then the drag handle appears and allows for dragging the item, while the rest of the DraggableFlatlist item still responds to the parent ScrollView (because hitSlop is set to ignore gesture events on all parts of the item except the drag handle).

      <DraggableFlatList
        data={data}
        dragItemOverflow={true}
        renderItem={renderItem}
        dragHitSlop={
          active ? { right: -(width * 0.95 - 20) } : { right: -width }
        }
        keyExtractor={(item, index) => `draggable-item-${item.order}`}
        }}
      />
creambyemute commented 3 years ago

Another option for me was to use the following approach:

Update libraries:

Together with the following inside ScrollViews.

<DraggableFlatList
    style={{
        maxHeight:
            Platform.OS === "ios"
                ? this.props.rootStore.commonStore.windowHeight / 1.15
                : this.props.rootStore.commonStore.windowHeight / 1.17
    }}

@Svarto I gotta try your approach!

pedro-lb commented 3 years ago

I'm using a workaround that works but its probably not ideal and might not work for everyones use case.

So anything I want to be able to scroll as well as drag and I am putting inside the ListHeaderComponent for the FlatList

<DraggableFlatList
        ListHeaderComponent={() => (
           <>
              // Every component above the draggable list I want to scroll
           </>
        )}
        renderItem={({ item, index, drag, isActive }) => (
          <MyDraggableComponent />
        )}
       ListFooterComponent={() => (
           <>
              // Every component below the draggable list i want to scroll
           </>
       )}
/>

This works for me with how my app is using the draggable list, but again its not ideal for everybody but hope it helps someone.

Also make sure to get rid of the parent scrollview

This works fine and is by far the most straightforward solution out there, albeit not so clean

sanduluca commented 3 years ago

I've found a workaround, but it seems to sometimes block vertical scrolling. Set the activationDistance prop (on DraggableFlatList) to a value greater than zero. In my testing, even 1 is enough to allow swiping horizontally within the parent scroll view without interfering with drag and drop functionality, but 10 provides more consistent scrolling within the DraggableFlatList.

I have a ScrollView with some content inside. It also has a horizontal DraggableFlatList. The issue was that I wasn't able to scroll vertically when the touch started from the DraggableFlatList area. Setting activationDistance={10} solves the problem with scrolling. I want to mention that the dragging is also working fine. This is basically all I have:

<ScrollView>
// some content goes here (Views, Text, Images, Inputs)
<DraggableFlatList
          data={this.props.images}
          keyExtractor={(item) => item}
          renderItem={this.renderGalleryItem}
          onDragEnd={({ data }) => this.onDragEnd(data)}
          ItemSeparatorComponent={this.renderItemSeparator}
          ListHeaderComponent={this.renderImagePicker}
          horizontal={true}
          activationDistance={10}
        />
// some content goes here (Views, Text, Images, Inputs)
<ScrollView/>

Its a photo carousel image

midimurphdesigns commented 3 years ago

if my list gets too long, I need to have vertical scroll. None of the solutions provided are working for me. Any other insight?

daybreaker commented 3 years ago

Same, would really like a solution as this is one of the only drag and drop libraries I've found that doesn't require the elements to have a fixed height.

For now I'll just have to make it a 3 step process of: 1) click a button to enable dragHitSlops on the elements, 2) let user rearrange things, 3) hit a button to "save" and turn off the dragHitSlop

Donhv commented 3 years ago

Is there any update on this? I broke my layout because of this issue. :(((

Daniel-Griffiths commented 3 years ago

@bill-pairaktaridis @ds-karpov created pull requests adding in hitSlop parameter to allow for @rednebmas solution. #232 #231

This allows for controlling what part of the DraggableFlatlist responds to gesture events. Meaning if you disable it, the parent ScrollView will take precedence and continue to work. I have solved it by creating a handle that activates with a toggle, if the toggle is false then the parent ScrollView scrolls normally, if it is true then the drag handle appears and allows for dragging the item, while the rest of the DraggableFlatlist item still responds to the parent ScrollView (because hitSlop is set to ignore gesture events on all parts of the item except the drag handle).

      <DraggableFlatList
        data={data}
        dragItemOverflow={true}
        renderItem={renderItem}
        dragHitSlop={
          active ? { right: -(width * 0.95 - 20) } : { right: -width }
        }
        keyExtractor={(item, index) => `draggable-item-${item.order}`}
        }}
      />

This answer worked for me, thank you!

ashutosh-stha23 commented 2 years ago

dragHitSlop={ active ? { right: -(width * 0.95 - 20) } : { right: -width } }

how are you toggling active to be true or false?

Svarto commented 2 years ago

dragHitSlop={ active ? { right: -(width * 0.95 - 20) } : { right: -width } }

how are you toggling active to be true or false?

Just a standard useState tied to a button ("Edit order")

ashutosh-stha23 commented 2 years ago

dragHitSlop={ active ? { right: -(width * 0.95 - 20) } : { right: -width } }

how are you toggling active to be true or false?

Just a standard useState tied to a button ("Edit order")

Thanks. I don't have any button for that. So thought could we do it with onDragBegin and onDragEnd. It was not working properly.

Svarto commented 2 years ago

dragHitSlop={ active ? { right: -(width * 0.95 - 20) } : { right: -width } }

how are you toggling active to be true or false?

Just a standard useState tied to a button ("Edit order")

Thanks. I don't have any button for that. So thought could we do it with onDragBegin and onDragEnd. It was not working properly.

Aha, did you try using a useRef instead of useState?

MaheshGawhane11 commented 2 years ago

I'm using a workaround that works but its probably not ideal and might not work for everyones use case. So anything I want to be able to scroll as well as drag and I am putting inside the ListHeaderComponent for the FlatList

<DraggableFlatList
        ListHeaderComponent={() => (
           <>
              // Every component above the draggable list I want to scroll
           </>
        )}
        renderItem={({ item, index, drag, isActive }) => (
          <MyDraggableComponent />
        )}
       ListFooterComponent={() => (
           <>
              // Every component below the draggable list i want to scroll
           </>
       )}
/>

This works for me with how my app is using the draggable list, but again its not ideal for everybody but hope it helps someone. Also make sure to get rid of the parent scrollview

This works fine and is by far the most straightforward solution out there, albeit not so clean

Thank you! It works for me.

computerjazz commented 2 years ago

Nesting support added in 3.1.0: https://github.com/computerjazz/react-native-draggable-flatlist/releases/tag/v3.1.0