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.