You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 04:25:02