I'm creating a custom react native bottom sheet using GestureDetector
from react-native-gesture-handler
and i want to pass a child
Flatlist
to the sheet. but having an issue with the scrolling. The scroll of the flatlist
consumes the gesture handler of the sheet and the sheet does not move up instead the flatlist scrolls in place. I want the sheet to behave normally when attempting to scroll the flatlist
area the sheet scrolls instead. I want it so that the flat list scrolls to the maximum top destination and then for the flat list to carry on scrolling in one continues scroll
gesture. i tired to use an Animated.flatlist
from react-native-reanimated
and make use of the onScroll
event
. but I'am still not able to achieve the desired outcome.
Code I have attempted:
import React, { useCallback, useEffect, useRef } from "react";
import { Dimensions, FlatList, Text, View } from "react-native";
import { Gesture, GestureDetector, GestureHandlerRootView } from "react-native-gesture-handler";
import Animated, {
Extrapolate,
interpolate,
useAnimatedRef,
useAnimatedScrollHandler,
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
const { height: SCREEN_HEIGHT } = Dimensions.get("window");
const data = [
{ id: "1", title: "Item 1" },
{ id: "2", title: "Item 2" },
{ id: "3", title: "Item 3" },
{ id: "4", title: "Item 4" },
{ id: "5", title: "Item 5" },
{ id: "6", title: "Item 6" },
{ id: "7", title: "Item 7" },
{ id: "8", title: "Item 8" },
{ id: "9", title: "Item 9" },
{ id: "10", title: "Item 10" },
{ id: "11", title: "Item 11" },
{ id: "12", title: "Item 12" },
{ id: "13", title: "Item 13" },
{ id: "14", title: "Item 14" },
{ id: "15", title: "Item 15" },
{ id: "16", title: "Item 16" },
{ id: "17", title: "Item 17" },
{ id: "18", title: "Item 18" },
{ id: "19", title: "Item 19" },
{ id: "20", title: "Item 20" },
{ id: "21", title: "Item 21" },
{ id: "22", title: "Item 22" },
{ id: "23", title: "Item 23" },
{ id: "24", title: "Item 24" },
{ id: "25", title: "Item 25" },
];
export const MyBottomSheet: React.FC = () => {
const insets = useSafeAreaInsets();
// const listRef = useAnimatedRef<FlatList>();
const listRef = useRef<FlatList>(null);
const MAX_TRANSLATE_Y = -SCREEN_HEIGHT + insets.top;
const translationY = useSharedValue(0);
const context = useSharedValue({ y: 0 });
const scrollTo = useCallback(
(destination: number) => {
"worklet";
translationY.value = withSpring(destination, { damping: 50 });
},
[translationY],
);
const gesture = Gesture.Pan()
.onStart(() => {
context.value = { y: translationY.value };
})
.onUpdate((event) => {
translationY.value = event.translationY + context.value.y;
translationY.value = Math.max(translationY.value, MAX_TRANSLATE_Y);
})
.onEnd(() => {
if (translationY.value - SCREEN_HEIGHT + 200) {
scrollTo(-SCREEN_HEIGHT + 200);
}
if (translationY.value < -SCREEN_HEIGHT + 100) {
scrollTo(MAX_TRANSLATE_Y);
}
});
const reanimatedBottomSheetStyle = useAnimatedStyle(() => {
const borderRadius = interpolate(translationY.value, [MAX_TRANSLATE_Y + insets.top, MAX_TRANSLATE_Y], [20, 0], Extrapolate.CLAMP);
if (translationY.value === MAX_TRANSLATE_Y) {
listRef.current?.setNativeProps({ scrollEnabled: true });
}
return {
borderRadius,
transform: [{ translateY: translationY.value }],
};
});
useEffect(() => {
scrollTo(-SCREEN_HEIGHT + 200);
}, [listRef, scrollTo]);
const Item = ({ title }) => (
<View style={{ padding: 10 }}>
<Text>{title}</Text>
</View>
);
const onScroll = useAnimatedScrollHandler((event) => {
const offsetY = event.contentOffset.y;
// scrollTo(-offsetY + -SCREEN_HEIGHT + 200);
});
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<GestureDetector gesture={gesture}>
<Animated.View
style={[reanimatedBottomSheetStyle, { height: SCREEN_HEIGHT, top: SCREEN_HEIGHT, borderRadius: 25, backgroundColor: "white" }]}
>
<Animated.FlatList
style={{ margin: 20, marginTop: 50 }}
data={data}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={(item) => item.id}
onScroll={onScroll}
/>
</Animated.View>
</GestureDetector>
</GestureHandlerRootView>
);
};