software-mansion / react-native-gesture-handler

Declarative API exposing platform native touch and gesture system to React Native.
https://docs.swmansion.com/react-native-gesture-handler/
MIT License
6.12k stars 979 forks source link

`FlatList` component does not scroll vertically on touch in mobile responsive mode #2617

Closed echarbeto closed 8 months ago

echarbeto commented 1 year ago

Description

Hello, I'm experiencing an issue with the FlatList component from react-native-gesture-handler when it's used in conjunction with the Swipeable component. Specifically, the FlatList component does not scroll vertically on touch in mobile responsive mode.

Steps to reproduce

  1. Clone the branch with the development from the public repository: https://github.com/echarbeto/tamagui_test/tree/TEST/flatlist_with_swipeable_elements
  2. Install the necessary libraries. Please note that this is an Expo SDK 49 project.
  3. Run the project using the command npm run start.
  4. Access the project from your browser at localhost:8081.
  5. If accessing from a desktop, switch to mobile view.
  6. Try to scroll vertically.

The issue occurs when trying to scroll vertically in mobile view. The FlatList component does not respond to vertical scrolling actions. Screenshot_20231002_235335

Snack or a link to a repository

https://github.com/echarbeto/tamagui_test/tree/TEST/flatlist_with_swipeable_elements

Gesture Handler version

2.13.1

React Native version

0.72.5

Platforms

Web

JavaScript runtime

None

Workflow

Expo managed workflow

Architecture

None

Build type

Debug mode

Device

None

Device model

No response

Acknowledgements

Yes

hokich commented 1 year ago

I have the same problem with GetureDetector and FlatList/ScrollList on the Web

m-bert commented 1 year ago

Hi! Thanks for submitting this issue. I've managed to identify what causes this problem. We still have to find good way to fix it though.

echarbeto commented 1 year ago

Hello @m-bert ! Let me know if you need any help with the tests. Thanks!

m-bert commented 1 year ago

Sure, if we find good way how to deal with this problem I'll definitely ask for help with tests! As for now, I can tell what causes it.

So the problem comes from this line. Swipeable consists of Pan and Tap. Since both of them have touchAction set to none, you cannot trigger interaction with scroll. The problem is, we cannot fully drop setting touchAction: none, because it results in weird situations, like moving element along with scrolling.

Moreover, swipeable components have their own Animated.View under the hood, so if you check how it looks like in inspector, you'll see that those views with touchAction: none cover whole Flatlist. This means that even if you leave space between your items (like using margin) you won't be able to scroll (unless you wrap whole swipeable into view with margin - then you'll be able to use scroll in between).

m-bert commented 1 year ago

Hi @echarbeto! Just to let you know, we are close to solving this problem. I've created a draft PR that should fix this issue by replacing touchAction: none with e.preventDefault(). It still needs some tests though.

I'll get back to you when PR will be ready so you will have opportunity to test if it helps!

echarbeto commented 1 year ago

Hi @m-bert ,

You're right. If I add a margin to the containerStyle of Swipeable, and touch outside of the container, I can scroll vertically.

I'll wait for your instructions to test. Once done, I'll upload the changes to the test project I used and let you know how the tests turn out.

Thanks!

GuestInCorle commented 1 year ago

This is what it looks like. Is there any patch or quick fix? We got a blocker when trying to upgrade to Expo 49.

m-bert commented 1 year ago

Since my PR is no longer a draft, you can check if it works and doesn't break anything in your app. I'd like to merge it soon and it would be great to confirm that it solves issue and does not introduce any regression.

echarbeto commented 1 year ago

@m-bert, I installed the version github:software-mansion/react-native-gesture-handler#@mbert/fix-scroll-interactions which includes your proposed solution for mobile web. It works well. Both scrolling and swiping are functioning as expected. https://github.com/echarbeto/tamagui_test/tree/TEST/flatlist_with_swipeable_elements

localhost_8081_

Thanks!

GuestInCorle commented 1 year ago

@m-bert The fix you suggest doesn't work in Firefox (Linux & Android). However it works in Chrome. Are there any plans to support Firefox? Does the bug in Firefox belong to this issue? The video of the bug in Firefox on Linux (sensor input emulation is ON).

m-bert commented 1 year ago

Hi @GuestInCorle! Thanks for pointing this out! We do want to support all browsers, especially major ones. Dealing with scroll isn't that simple though, and it seems that somehow firefox implements it in a bit different way than chrome.

Right now I'm testing slightly different approach, maybe this will help with other browsers.

GuestInCorle commented 1 year ago

@m-bert The fix seems to only work in Chrome. We've received reports that Safari is having the same scrolling issue as Firefox.

GuestInCorle commented 1 year ago

@m-bert There is also a problem with Chrome. In the same case reported in the original issue, the swipe gesture has now become significantly more difficult to perform with the mouse. The real touch screen works fine.

m-bert commented 12 months ago

Hi @GuestInCorle! I've made some changes in my PR, could you please check if scrolling works on safari?

GuestInCorle commented 12 months ago

@m-bert Scrolling and swiping now work in Safari. I've tested the latest commit in @mbert/fix-scroll-interactions.

https://github.com/software-mansion/react-native-gesture-handler/assets/28631281/b7a6291d-017e-445c-8f55-898ad7b252f6

LucasLFurini commented 8 months ago

I'm having the same issue using FlatList on my app, if I use an ScrollView with Gesture.Pan().simultaneousWithExternalGesture(scrollViewRef), the swipe and the list scroll works, but when using same code but with a Flatlist, the list scroll only works if pressed outside from the swipeable component, I'm using my own swipeable item component

export const SwipeableItem = ({ item, onDismiss, scrollRef }: any) => {
    const { width: SCREEN_WIDTH } = useWindowDimensions()
    const TRANSLATE_X_THRESHOLD = -SCREEN_WIDTH * 0.3
    const translateX = useSharedValue(0)
    const itemHeight = useSharedValue(70)
    const itemMarginVertical = useSharedValue(10)
    const actionOpacity = useSharedValue(1)

    const panGesture = Gesture
      .Pan()
      .simultaneousWithExternalGesture(scrollRef)
      .onChange((event) => {
        translateX.value = event.translationX
      })
      .onEnd(() => {
        const shouldDismiss = translateX.value < TRANSLATE_X_THRESHOLD
        if (shouldDismiss) {
          translateX.value = withTiming(-SCREEN_WIDTH)
          itemHeight.value = withTiming(0)
          itemMarginVertical.value = withTiming(0)
          actionOpacity.value = withTiming(0, undefined, (isFinished) => {
            if (isFinished) {
              runOnJS(onDismiss)(item.id)
            }
          })
        } else {
          translateX.value = withTiming(0)
        }
      })

    const reanimatedStyle = useAnimatedStyle(() => ({
      transform: [{ translateX: translateX.value }]
    }))

    const reanimatedIconContainerStyle = useAnimatedStyle(() => {
      const opacity = withTiming(translateX.value < TRANSLATE_X_THRESHOLD ? 1 : 0)
      return { opacity }
    })

    const reanimatedTaskContainerStyle = useAnimatedStyle(() => {
      return {
        height: itemHeight.value,
        marginVertical: itemMarginVertical.value,
        opacity: actionOpacity.value
      }
    })

        return (
          <Container style={[reanimatedTaskContainerStyle]}>
            <Animated.View style={[{ width: 60, height: 70, backgroundColor: "red", position: "absolute", right: "10%" }, reanimatedIconContainerStyle]}>
              <Text>Icon</Text>
            </Animated.View>
            <GestureDetector gesture={panGesture}>
              <Animated.View style={[{ width: "90%", height: 70, backgroundColor: "blue" }, reanimatedStyle]}>
                <Text>{item.title}</Text>
              </Animated.View>
            </GestureDetector>
          </Container>
        )
   }
m-bert commented 8 months ago

Hi @echarbeto, @GuestInCorle, @LucasLFurini! I know it's been a while, but we decided to change our approach to this problem and hopefully this time we will be able to make everything work fine. Could you please check if #2788 fixes this problem?