For
"react-native": "^0.70.5"
Requirement:
Flatlist
as an overlay above Clickable elementsFlatlist
header has a transparent area, with pointerEvents="none"
to make the elements below clickable and yet allow the Flatlist
to scroll.
Issues with some possible approaches
pointerEvents="none"
doesn't work with Flatlist
, as internally how Flatlist is built it will block the events at all values of pointerEvents
. It's the same with Scrollview
as well.ScrollableView
, as pointerEvents
with View
work well. With this adding pointerEvents
to none
on parts of the children, lets the touch event to propagate to elements below.import React, { useState, useRef } from 'react';
import { View, PanResponder, Animated } from 'react-native';
const ScrollableView = ({children, style, onScroll}) => {
const scrollY = useRef(new Animated.Value(0)).current;
const lastScrollY = useRef(0);
const scrollYClamped = Animated.diffClamp(scrollY, 0, 1000);
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (_, gestureState) => {
scrollY.setValue(lastScrollY.current + gestureState.dy);
},
onPanResponderRelease: (_, { vy, dy }) => {
lastScrollY.current += dy;
Animated.spring(scrollY, {
toValue: lastScrollY.current,
velocity: vy,
tension: 2,
friction: 8,
useNativeDriver: false,
}).start();
},
})
).current;
const combinedStyle = [
{
transform: [{ translateY: scrollYClamped }],
},
style
];
return (
<Animated.View
{...panResponder.panHandlers}
pointerEvents="box-none"
style={combinedStyle}
>
{children}
</Animated.View>
);
};
export default ScrollableView;
Any solution to any of the above three approaches is appreciated.
https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheetflatlist
import React, { useCallback, useRef, useMemo } from "react";
import { StyleSheet, View, Text, Button } from "react-native";
import BottomSheet, { BottomSheetFlatList } from "@gorhom/bottom-sheet";
const App = () => {
// hooks
const sheetRef = useRef<BottomSheet>(null);
// variables
const data = useMemo(
() =>
Array(50)
.fill(0)
.map((_, index) => `index-${index}`),
[]
);
const snapPoints = useMemo(() => ["25%", "50%", "90%"], []);
// callbacks
const handleSheetChange = useCallback((index) => {
console.log("handleSheetChange", index);
}, []);
const handleSnapPress = useCallback((index) => {
sheetRef.current?.snapToIndex(index);
}, []);
const handleClosePress = useCallback(() => {
sheetRef.current?.close();
}, []);
// render
const renderItem = useCallback(
({ item }) => (
<View style={styles.itemContainer}>
<Text>{item}</Text>
</View>
),
[]
);
return (
<View style={styles.container}>
<Button title="Snap To 90%" onPress={() => handleSnapPress(2)} />
<Button title="Snap To 50%" onPress={() => handleSnapPress(1)} />
<Button title="Snap To 25%" onPress={() => handleSnapPress(0)} />
<Button title="Close" onPress={() => handleClosePress()} />
<BottomSheet
ref={sheetRef}
snapPoints={snapPoints}
onChange={handleSheetChange}
>
<BottomSheetFlatList
data={data}
keyExtractor={(i) => i}
renderItem={renderItem}
contentContainerStyle={styles.contentContainer}
/>
</BottomSheet>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 200,
},
contentContainer: {
backgroundColor: "white",
},
itemContainer: {
padding: 6,
margin: 6,
backgroundColor: "#eee",
},
});
export default App;