react-native-simple-bottom-sheet底部弹窗滚动异常问题求助
React Native Simple Bottom Sheet 滚动事件穿透问题
问题详情
使用react-native-simple-bottom-sheet库时,点击首页工具栏的“Truminds”文本唤起底部弹窗后,滚动弹窗内容时,滚动事件会触发背景首页滚动,弹窗本身无响应。尝试用ScrollView包裹弹窗内容无效。
解决方案
1. 拦截弹窗触摸事件
给弹窗最外层容器添加onStartShouldSetResponder={() => true},强制弹窗捕获所有触摸事件,阻止事件透传到背景页面:
<View style={styles.bottomSheet} onStartShouldSetResponder={() => true}> {/* 弹窗内部内容 */} </View>
2. 启用嵌套滚动
对弹窗内的嵌套FlatList设置nestedScrollEnabled={true},确保内部滚动事件被正确识别:
<FlatList data={expandedData} nestedScrollEnabled={true} // 其他属性 />
3. 限制弹窗滚动容器高度
将外层FlatList替换为ScrollView并设置固定高度,确保滚动容器的可滚动区域计算正确:
<ScrollView style={{ height: bottomSheetHeight - 120 }}> {data.map((item, index) => renderItem({ item, index }))} </ScrollView>
4. 控制背景页面滚动状态
在父组件中维护弹窗的打开状态,当弹窗打开时,禁用背景页面的滚动容器(如FlatList/ScrollView):
// 父组件示例 <FlatList scrollEnabled={!isBottomSheetOpen} // 其他属性 />
修改后的完整代码
import React, { useRef, useState } from 'react'; import { View, Text, TouchableOpacity, StyleSheet, FlatList, Dimensions, ScrollView } from 'react-native'; import BottomSheet from 'react-native-simple-bottom-sheet'; import { LayoutAnimation } from 'react-native'; import { fontFamily } from '../../../assets/styles/FontStyle'; import DownArrowIcon from "../../../assets/img/icons/bottom_sheet_down_arrow.svg"; import UpArrowIcon from "../../../assets/img/icons/bottom_sheet_up_arrow.svg"; import RightArrowIcon from "../../../assets/img/icons/bottom_sheet_right_arrow.svg"; import CloseIcon from "../../../assets/img/icons/bottom_sheet_close_icon.svg"; type SpaceBottomSheetProps = { onClose: () => void; isOpen: boolean; } const SpaceBottomSheet = ({ onClose, isOpen }: SpaceBottomSheetProps) => { const bottomSheetRef = useRef<{ togglePanel: () => void; } | null>(); const [expandedItem, setExpandedItem] = useState<number | null>(null); const screenHeight = Dimensions.get('screen').height; const bottomSheetHeight = screenHeight * 0.85; const closeBottomSheet = () => { if (bottomSheetRef.current) { bottomSheetRef.current.togglePanel(); onClose(); } }; const data = [ { label: '2D Test' }, { label: '2D Testing 2' }, { label: '2D Testing 3' }, { label: '2D Testing 1' }, { label: '2D Testzaffar' }, { label: '2D TestZaffar' }, ]; const expandedData = [ { label: 'Dashboard' }, { label: 'List of Subspaces' }, { label: 'View Space Details' }, { label: 'Create Subspace' }, ]; const renderItem = ({ item, index }: { item: { label: string }, index: number }) => { const isExpanded = expandedItem === index; const handlePress = () => { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); setExpandedItem(isExpanded ? null : index); }; return ( <View style={styles.itemContainer}> <TouchableOpacity onPress={handlePress}> <View style={styles.labelContainer}> <Text style={styles.label}>{item.label}</Text> {isExpanded ? ( <UpArrowIcon width={12} height={10} style={styles.arrowIcon} /> ) : ( <DownArrowIcon width={12} height={10} style={styles.arrowIcon} /> )} </View> </TouchableOpacity> {isExpanded && ( <FlatList data={expandedData} renderItem={({ item }) => ( <> <TouchableOpacity style={styles.rowContainer}> <View style={styles.row}> <Text style={styles.rowLabel}>{item.label}</Text> <RightArrowIcon style={styles.rowArrowIcon} /> </View> </TouchableOpacity> {item.label === 'List of Subspaces' && <View style={styles.separator} />} </> )} keyExtractor={(item) => item.label} nestedScrollEnabled={true} /> )} </View> ); }; return ( <View style={styles.container}> <BottomSheet ref={bottomSheetRef} sliderMaxHeight={bottomSheetHeight} sliderMinHeight={0}> <View style={styles.bottomSheet} onStartShouldSetResponder={() => true}> <TouchableOpacity onPress={() => closeBottomSheet()} style={styles.closeIcon}> <CloseIcon width={14.65} height={14.65} /> </TouchableOpacity> <View style={styles.header}> <Text style={styles.title}>Truminds</Text> <Text style={[styles.create, { color: expandedItem !== null ? '#0F8D48' : '#4D4D4D' }]}> + Create Subspace </Text> </View> <View style={styles.separator} /> <ScrollView style={{ height: bottomSheetHeight - 120 }}> {data.map((item, index) => renderItem({ item, index }))} </ScrollView> </View> </BottomSheet> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 0, paddingTop: 28, paddingBottom: 12, }, itemContainer: { backgroundColor: '#f2f2f2', padding: 10, borderRadius: 10, marginBottom: 17, }, title: { fontSize: 24, fontWeight: '800', color: '#4D4D4D', fontFamily: fontFamily.theme.EXTRA_BOLD }, create: { fontSize: 12, fontWeight: '600', }, bottomSheet: { backgroundColor: 'white', borderTopLeftRadius: 20, borderTopRightRadius: 20, paddingHorizontal: 5, paddingBottom: 10, marginTop: -20, }, separator: { height: 1, backgroundColor: '#ddd', marginVertical: 10, }, list: { marginTop: 10, }, listItem: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 10, }, labelContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingBottom: 3, paddingTop: 3, }, label: { fontSize: 16, fontWeight: '600', color: '#4D4D4D', }, arrowIcon: { marginLeft: 10, color: '#000000', }, closeIcon: { flex: 1, alignItems: 'flex-end', }, rowContainer: { paddingHorizontal: 4, paddingVertical: 10, }, row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, rowLabel: { fontSize: 12, fontWeight: '500', color: '#4D4D4D', }, rowArrowIcon: { marginLeft: 10, color: '#000000', }, }); export default SpaceBottomSheet;
内容的提问来源于stack exchange,提问作者Satyam
相关产品推荐
相关产品推荐

