React Native中基于Gorhom BottomSheet实现内容自适应底部弹窗
实现Gorhom BottomSheet根据内容自动调整高度
要让Gorhom BottomSheet根据内部内容自动适配高度,核心是利用库内置的CONTENT_HEIGHT常量作为停靠点,同时移除固定的size参数及相关计算,以下是具体改造步骤和完整代码:
关键改造点
- 移除固定
size依赖:删除组件props中的size参数及相关高度计算逻辑 - 使用内容高度作为停靠点:导入
CONTENT_HEIGHT,将snapPoints设置为该值,让弹窗自动适配内容高度 - 调整弹窗状态逻辑:修改弹窗状态变化的回调逻辑,适配新的停靠点配置
- 动态内容适配:如果内容是异步加载的,需主动触发高度重新计算
改造后的完整代码
import BottomSheet, { BottomSheetBackdrop, BottomSheetScrollView, CONTENT_HEIGHT } from '@gorhom/bottom-sheet'; import { Portal } from '@gorhom/portal'; import React, { FC, useCallback, useRef, useEffect } from 'react'; import { Platform } from 'react-native'; import { heightPixel } from '../../styles'; import { customBottomSheetStyles } from './style'; import { CustomBottomSheetPropsType } from './types'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; // 推荐用这个处理安全区域 const CustomBottomSheet: FC<Omit<CustomBottomSheetPropsType, 'size'>> = ({ children, onClose }) => { const insets = useSafeAreaInsets(); // 更可靠的安全区域内边距计算 const bottomInset = insets.bottom; const bottomSheetRef = useRef<BottomSheet>(null); // 内容更新时重新计算弹窗高度(针对动态加载的内容) useEffect(() => { bottomSheetRef.current?.recalculateSnapPoints(); }, [children]); const handleSheetChanges = useCallback( (index: number) => { // 当弹窗完全关闭(index为-1)时触发关闭回调 if (index === -1) { onClose(); } }, [onClose] ); return ( <Portal> <BottomSheet ref={bottomSheetRef} index={0} // 初始停靠在内容高度位置 onClose={onClose} snapPoints={[CONTENT_HEIGHT]} // 自动适配内容高度作为唯一停靠点 onChange={handleSheetChanges} enablePanDownToClose style={customBottomSheetStyles.bottomSheetContainer} bottomInset={bottomInset} backdropComponent={(props) => ( <BottomSheetBackdrop style={customBottomSheetStyles.backdropContainer} {...props} opacity={0.2} appearsOnIndex={0} disappearsOnIndex={-1} /> )} > <BottomSheetScrollView showsVerticalScrollIndicator={false} nestedScrollEnabled={true} // 可选:仅当内容高度超过弹窗可用高度时允许滚动 scrollEnabled={true} > {children} </BottomSheetScrollView> </BottomSheet> </Portal> ); }; export { CustomBottomSheet };
额外优化建议
- 限制最大高度:如果担心内容过长导致弹窗占满屏幕,可以给
snapPoints添加一个最大高度限制,例如:snapPoints={[CONTENT_HEIGHT, '90%']},当内容高度超过屏幕90%时,弹窗会停在90%高度并允许内部滚动 - 安全区域处理:推荐使用
react-native-safe-area-context库来获取安全区域内边距,比直接调用NativeModules.StatusBarManager更稳定且兼容更多场景 - 动态内容适配:如果内容是通过网络请求等异步方式加载的,确保在内容渲染完成后调用
recalculateSnapPoints(),保证弹窗高度准确
内容的提问来源于stack exchange,提问作者Alliyan Waheed
相关产品推荐
相关产品推荐

