skip to Main Content

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


  1. Chosen as BEST ANSWER

    Finally, I got a solution for this issue. don't wrap with Ternary operator the bottomsheet. pls reffer below code.

    Before :

    {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>
              )}
            </>
          )}
    

    After fix :

    <BottomSheet
    index={-1}
                  ref={sheetRef}
                  snapPoints={snapPoints}
                  enablePanDownToClose={true}
                  onClose={() => setIsOpen(false)}
                  backgroundStyle={{
                    backgroundColor: COLORS.background.primary,
                    borderColor: COLORS.gray,
                    borderWidth: 2,
                  }}>
                    <BottomSheetView>
                      <ProfilePhotoBottomSheet
                        route={route}
                        navigation={navigation}
                        handleTakePhoto={handleTakePhoto}
                        handleSelectPhoto={handleSelectPhoto}
                        handleClosePress={handleClosePress}
                      />
                    </BottomSheetView>
                </BottomSheet>
        ```
    
    

  2. make sure to wrap your whole app with

    <GestureHandlerRootView style={{flex:1}}>
    {your other components}
    </GestureHandlerRootView>
    

    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.js

    Login or Signup to reply.
  3. Update: 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 the GestureHandlerRootView 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 the GestureHandlerRootView, and swapping these two components fixed the issue for me!

    Before:

    <BottomSheetModalProvider>
      <GestureHandlerRootView style={{ flex: 1 }}>
        <MainNavigation />
      </GestureHandlerRootView>
    </BottomSheetModalProvider>
    

    After:

    <GestureHandlerRootView style={{ flex: 1 }}>
      <BottomSheetModalProvider>
        <MainNavigation />
      </BottomSheetModalProvider>
    </GestureHandlerRootView>
    

    @gorhom does it make sense to update the docs to say that BottomSheetModalProvider must be inside GestureHandlerRootView?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search