0

I wanted to create a generic and simple tab component that can accept a list of ReactNode and to be able to swipe horizontally through 3 full screen pages. It worked well until I needed to pass 3 FlatList (one on each page) to this component. The problem come from the fact that I got horizontal swipe that are caught by the Pan handler and I need to pass the vertical swipe to the child (FlatList), currently only the first one in the DOM can scroll vertically.

My question is, is there a way to disable the first FlatList to let the next one catch the vertical swipe event ?

Here's the code of my Tab component :

const Tab = forwardRef(({
  pages, onIndexChange, style, keys,
}: TabProps, ref) => {
  const index = useSharedValue(0);

  const numberOfItem = pages?.length ?? 0;

  const startingPosition = 0;
  const { width } = useWindowDimensions();
  const x = useSharedValue(startingPosition);

  function onChange(currentIndex: number) {
    'worklet';

    runOnJS(onIndexChange)(currentIndex);
  }

  const panGesture = Gesture.Pan()
    .activeOffsetX([-30, 30])
    .failOffsetY([-30, 30])
    .onUpdate((event) => {
      if (index.value === 0 && startingPosition + event.translationX > 0) { // Block swipe to left
        x.value = startingPosition;
      } else if (index.value === (numberOfItem - 1) && Math.abs(startingPosition - (index.value * width) + event.translationX) > (index.value * width)) { // Block swipe to right
        x.value = startingPosition - (index.value * width);
      } else { // Default swipe
        x.value = startingPosition - (index.value * width) + event.translationX;
      }
    })
    .onEnd(() => {
      const direction = Math.abs(x.value) < index.value * width ? 1 : -1; // 1 to the left, -1 to the right
      const pixelSwiped = Math.abs(Math.abs(x.value) - (width * index.value));
      const percentageSwiped = (pixelSwiped * 100) / width;

      if (percentageSwiped >= THRESHOLD_SWIPE) {
        index.value -= direction; // if direction = 1 => should make index - 1 otherwise if direction = -1 => should make index + 1
        onChange(index.value);
        x.value = withSpring((index.value * width) * -1, { overshootClamping: true });
      } else { // Stay on the same view
        x.value = withSpring((index.value * width) * -1, { overshootClamping: true });
      }
    });

  const uas = useAnimatedStyle(() => ({
    transform: [{ translateX: x.value }],
  }));

  // Exposed method through ref
  useImperativeHandle(ref, () => ({
    goToIndex(indexToGo: number) {
      if (indexToGo >= 0 && indexToGo < numberOfItem) {
        index.value = indexToGo;
        onChange(indexToGo);
        x.value = withSpring((indexToGo * width) * -1, { overshootClamping: true });
      }
    },
  }), [x]);

  return (
    <GestureDetector gesture={panGesture}>
      <Animated.View style={[TabStyle.container, style, uas]}>
        {pages ? pages.map((page, indexPage) => (
          <View style={[TabStyle.pageContainer, PageContainerWidthStyle(width).pageContainer]} key={keys[indexPage]}>
            {page}
          </View>
        )) : null}
      </Animated.View>
    </GestureDetector>
  );
});

And the code where i Call my Tab component :

      <Tab
        ref={tabRef}
        keys={['1', '2', '3']}
        onIndexChange={onIndexChanged}
        pages={[
          <FlatList scrollEnabled={isListFocused(1)}/>,
          <FlatList scrollEnabled={isListFocused(2)}/>,
          <FlatList scrollEnabled={isListFocused(3)}/>,
        ]}
      />

I want my Tab component to be able to swipe horizontally in order to display 3 FlatList in a row, and the problem I encounter is that the second and third FlatList aren't scrollable vertically, but the first one is fine.

Thibaut
  • 1
  • 1

0 Answers0