Sometimes BottomSheet doesn’t showing, I think it happens because of conflict with. When I build release APK and install it on IOS, when you open application first time then bottom sheet doesn’t show but if you close and reopen application then bottom sheet appears. Had anyone this problem? This issue not appear in android, it issue only in IOS
import React, {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
import {
Text,
StyleSheet,
Image,
Linking,
TouchableOpacity,
View,
Platform,
} from 'react-native';
import { Box, AbstractButton } from '../../components';
import { COLORS, assets, FONTS, FONT_WEIGHT, SIZES } from '../../constants';
import { RootStackParamList } from '../../navigation/types';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { useSelector } from 'react-redux';
import { RootState } from '../../state/rootReducer';
import { useAppDispatch } from '../../state/index';
import { setProfilePhoto, setUserId } from '../../state/onboarding';
import { setIsBottomSheet, setIsEnableHeader } from '../../state/generalUtil';
import BottomSheet, { BottomSheetView } from '@gorhom/bottom-sheet';
import ProfilePhotoBottomSheet from './ProfilePhotoBottomSheet';
import { useBackHandler, useDimensions } from '@react-native-community/hooks';
import {
addDoitLater,
createUserOnboarding,
getDoitLater,
getUserDataById,
uploadProfileImage,
} from '../../service/OnboardingService';
import {
setBarStyle,
setStatusbarColor,
setTranslucent,
} from '../../state/generalUtil';
import { CustomCamera } from '../../components/CameraScreen';
// import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
import ApiUtil from '../../util/ApiUtil';
import { CommonActions } from '@react-navigation/native';
import ImagePicker from 'react-native-image-crop-picker';
import PermissionService from '../../service/PermissionService';
import { InfoPopUp } from '../../components/PopUpModal';
import CameraInfoPopup from './CameraInfoPopup';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
const ProfilePhoto = ({
route,
navigation,
}: NativeStackScreenProps<RootStackParamList>) => {
const { profilePhoto, userId } = useSelector(
(state: RootState) => state.onboarding,
);
const { PERMISSIONS_TYPE, checkPermissions, requestPermisssions } =
PermissionService;
const [isSelected, setIsSelected] = useState(false);
const [openCamera, setOpenCamera] = useState(false);
const [visible, setVisible] = useState(false);
const dispatch = useAppDispatch();
const { screen, window } = useDimensions();
// var ImagePicker = NativeModules.ImageCropPicker;
useEffect(() => {
if (openCamera) {
dispatch(setStatusbarColor('#000000'));
dispatch(setBarStyle('light-content'));
dispatch(setIsBottomSheet(true));
dispatch(setTranslucent(false));
dispatch(setIsEnableHeader(false));
} else {
dispatch(setStatusbarColor('#F2F2EF'));
dispatch(setBarStyle('dark-content'));
dispatch(setIsBottomSheet(false));
dispatch(setTranslucent(false));
}
}, [openCamera, dispatch]);
// BottomSheet Hooks
const sheetRef = useRef<BottomSheet>(null);
// Keep Tracking bootom sheet is open or not.
const [isOpen, setIsOpen] = useState(false);
// BottomSheet snap variables
const snapPoints = ['38%'];
// BottomSheet Callbacks
const handleClosePress = useCallback(() => {
sheetRef.current?.close();
}, []);
const handleCloseModal = useCallback(() => {
setVisible(false);
}, []);
const UpdateProfilePhoto = async () => {
const userData: any = await getUserDataById();
if (userData?.basicInfo?.userId) {
await uploadProfileImage(profilePhoto.url, userData?.basicInfo?.userId);
dispatch(setUserId(userData.basicInfo?.userId));
}
};
// BottomSheet Callbacks
const handleSnapPress = useCallback(index => {
sheetRef.current?.snapToIndex(index);
setIsOpen(true);
}, []);
const handleTakePhoto = async () => {
const PermissionStatus = await checkPermissions(PERMISSIONS_TYPE.camera);
if (PermissionStatus === 'blocked') {
setVisible(true);
}
const permission = await requestPermisssions(PERMISSIONS_TYPE.camera);
if (permission === 'granted') {
setOpenCamera(true);
}
};
const handleCloseCam = (image?: any) => {
if (image) {
dispatch(setProfilePhoto(image.uri));
}
setOpenCamera(false);
setIsOpen(false);
};
const handleSelectPhoto = () => {
ImagePicker.openPicker({
width: 300,
height: 400,
mediaType: 'photo',
}).then(async image => {
setIsSelected(true);
dispatch(setProfilePhoto(image.path));
});
};
const handleContinue = () => {
if (userId) {
UpdateProfilePhoto();
createUserOnboarding(userId, 10);
}
if (route?.params?.doItLaterFlag === 10) {
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{ name: 'ThingsToDoScreen' }],
}),
);
} else {
navigation.navigate('ConfirmDetails');
}
};
const doItLater = async () => {
dispatch(setProfilePhoto(''));
if (route?.params?.doItLaterFlag === 10) {
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{ name: 'ThingsToDoScreen' }],
}),
);
} else {
navigation.navigate('ConfirmDetails');
}
};
useBackHandler(
useCallback(() => {
if (isOpen) {
sheetRef.current?.close();
} else {
navigation.goBack();
}
return true;
}, [isOpen]),
);
return (
<>
{openCamera ? (
<Box width={window.width} height={window.height - 40}>
<CustomCamera type={'front'} close={handleCloseCam} />
</Box>
) : (
<>
<Box style={isOpen ? styles.containerOpacity : styles.container}>
<InfoPopUp visible={visible} handleCloseModal={handleCloseModal}>
{<CameraInfoPopup handleCloseModal={handleCloseModal} />}
</InfoPopUp>
<Box>
<Box
pt={145}
flexDirection={'column'}
style={{ alignItems: 'center' }}>
<TouchableOpacity
disabled={isOpen}
onPress={() => {
handleSnapPress(1);
}}>
<View style={styles.photoView}>
{profilePhoto.url ? (
<>
<Box>
<Image
source={{ uri: profilePhoto.url }}
style={styles.image}
/>
</Box>
<Box style={styles.insideImg3}>
<Image
source={assets.EditPen}
style={styles.PlusIconImage}
/>
</Box>
</>
) : (
<>
<Box style={styles.insideImg1}>
<Image
source={assets.Smile}
style={styles.SmileImage}
/>
</Box>
<Box style={styles.insideImg2}>
<Image
source={assets.PlusIcon}
style={styles.PlusIconImage}
/>
</Box>
</>
)}
</View>
</TouchableOpacity>
<Text style={styles.text}>Add profile photo</Text>
<Text style={styles.text1}>
To promote a safe and transparent community, we recommend a
clear photo of yourself
</Text>
</Box>
</Box>
</Box>
<Box style={styles.continueBtn}>
<AbstractButton
disabled={!profilePhoto.url}
onPress={handleContinue}>
Continue
</AbstractButton>
<TouchableOpacity onPress={doItLater}>
<Text style={styles.text2}>Do it later</Text>
</TouchableOpacity>
</Box>
{/* Bottom Sheet */}
{isOpen && (
<BottomSheet
ref={sheetRef}
snapPoints={snapPoints}
enablePanDownToClose={true}
onClose={() => setIsOpen(false)}
backgroundStyle={{
backgroundColor: COLORS.background.primary,
borderColor: COLORS.gray,
borderWidth: 2,
}}>
<GestureHandlerRootView>
<BottomSheetView>
<ProfilePhotoBottomSheet
route={route}
navigation={navigation}
handleTakePhoto={handleTakePhoto}
handleSelectPhoto={handleSelectPhoto}
handleClosePress={handleClosePress}
/>
</BottomSheetView>
</GestureHandlerRootView>
</BottomSheet>
)}
</>
)}
</>
);
};
const styles = StyleSheet.create({
continueBtn: {
position: 'absolute',
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
width: '100%',
paddingHorizontal: 10,
top:
Platform.OS === 'ios'
? SIZES.screen_height / 1.4
: SIZES.screen_height / 1.35,
},
container: {
flex: 1,
paddingHorizontal: 20,
backgroundColor: COLORS.background.primary,
},
containerOpacity: {
flex: 1,
opacity: 0.5,
paddingHorizontal: 20,
// backgroundColor: COLORS.lightGray,
},
container1: {
backgroundColor: COLORS.background.primary,
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 20,
},
SmileImage: {
width: 65,
height: 64,
},
PlusIconImage: {
width: 40,
height: 40,
},
text: {
paddingTop: '10%',
color: COLORS.black,
fontSize: SIZES.extraLarge,
fontFamily: FONTS.PlayfairDisplayBold,
lineHeight: 30,
alignItems: 'center',
},
text1: {
fontSize: SIZES.font,
fontFamily: FONTS.MerriweatherRegular,
lineHeight: 24,
color: '#2B2928',
paddingHorizontal: '15%',
paddingVertical: '4%',
textAlign: 'center',
},
text2: {
paddingTop: '8%',
color: COLORS.black,
fontFamily: FONTS.MerriweatherBold,
fontSize: 13,
lineHeight: 21,
textAlign: 'center',
},
photoView: {
width: 150,
height: 150,
backgroundColor: COLORS.white,
borderRadius: 100,
},
insideImg1: {
top: '30%',
left: '28%',
},
insideImg2: {
top: '30%',
left: '70%',
},
insideImg3: {
top: '-30%',
left: '70%',
},
image: {
width: 150,
height: 150,
borderRadius: 100,
},
});
export default ProfilePhoto;
3
Answers
Finally, I got a solution for this issue. don't wrap with Ternary operator the bottomsheet. pls reffer below code.
Before :
After fix :
make sure to wrap your whole app with
and read the gesture handler docs for installation and setup tips if this doesn’t solve your issue
you might also need to add
import 'react-native-gesture-handler';
at the beginning of your index.jsUpdate: Different similar issue but on Android
I was also having this exact problem, my BottomSheetModals and BottomSheetScrollViews were not responding to touch events on Android.
Root Cause
react-native-gesture-handler
requires a wrapper around the root view to function. In v1 of the library, this was done on the native side, and expo did this for you. In v2 of the library, you must use theGestureHandlerRootView
in your app manually. Upgrading to SDK 44 of expo removes the native RNGH setup.FIX
The
GestureHandlerRootView
must be applied as high as possible in your app’s component tree.In my case, I had my
BottomSheetModalProvider
outside theGestureHandlerRootView
, and swapping these two components fixed the issue for me!Before:
After:
@gorhom
does it make sense to update the docs to say that BottomSheetModalProvider must be inside GestureHandlerRootView?