Skip to content
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

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

Closed
echarbeto opened this issue Oct 3, 2023 · 17 comments · Fixed by #2788
Closed

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

echarbeto opened this issue Oct 3, 2023 · 17 comments · Fixed by #2788
Assignees
Labels
Platform: Web Repro provided A reproduction with a snack or repo is provided

Comments

@echarbeto
Copy link

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

@github-actions github-actions bot added Platform: Web Repro provided A reproduction with a snack or repo is provided labels Oct 3, 2023
@hokich
Copy link

hokich commented Oct 3, 2023

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

@m-bert
Copy link
Contributor

m-bert commented Oct 9, 2023

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.

@m-bert m-bert self-assigned this Oct 9, 2023
@echarbeto
Copy link
Author

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

@m-bert
Copy link
Contributor

m-bert commented Oct 10, 2023

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
Copy link
Contributor

m-bert commented Oct 12, 2023

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
Copy link
Author

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
Copy link

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
Copy link
Contributor

m-bert commented Oct 25, 2023

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
Copy link
Author

@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
Copy link

@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
Copy link
Contributor

m-bert commented Nov 9, 2023

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
Copy link

@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
Copy link

@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
Copy link
Contributor

m-bert commented Nov 20, 2023

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

@GuestInCorle
Copy link

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

94f74117-fa2f-4d43-84c1-2800e276d479.mp4

@LucasLFurini
Copy link

LucasLFurini commented Feb 27, 2024

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
Copy link
Contributor

m-bert commented Mar 5, 2024

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?

m-bert added a commit that referenced this issue Mar 7, 2024
## Description

This is a follow-up PR to #2787 that is meant to fix scrolling of swipeable elements. It overrides `shouldHandlerBeCancelledBy` method so that **_active_** `NativeViewGestureHandler` that is _**not a button**_, will cancel other handler. 

Keep in mind that on web, if scroll has already started we cannot cancel it by calling `preventDefault`, hence it makes sense to cancel other handlers in that case (but we may want to limit it just to `Pan`).

Fixes #2617

## Test plan

Tested on

- Swipeable example in our example app
- Transformations example with added text to achieve scrolling
<details>
<summary> Modified code from #2617 </summary>

```jsx
import React from 'react';
import { View, Text } from 'react-native';
import { FlatList, GestureHandlerRootView } from 'react-native-gesture-handler';
import Swipeable from 'react-native-gesture-handler/Swipeable';

export default function Home() {
  type ItemProps = {
    item: {
      text: string;
    };
  };

  const data = Array.from({ length: 50 }, (_, i) => ({
    id: i,
    text: `Item ${i}`,
  }));

  const Item = ({ item }: ItemProps) => {
    return (
      <View style={{ margin: 10 }}>
        <Swipeable
          renderRightActions={() => (
            <View
              style={{
                justifyContent: 'center',
              }}>
              <Text style={{ color: 'white', textAlign: 'center' }}>
                Delete
              </Text>
            </View>
          )}>
          <View
            style={{
              height: 50,
              backgroundColor: 'white',
              justifyContent: 'center',
            }}>
            <Text>{item.text}</Text>
          </View>
        </Swipeable>
      </View>
    );
  };

  return (
    <GestureHandlerRootView>
      <FlatList
        data={data}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => <Item item={item} />}
        style={{ maxHeight: 400 }}
      />
    </GestureHandlerRootView>
  );
}

```

</details>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Platform: Web Repro provided A reproduction with a snack or repo is provided
Projects
None yet
5 participants