Version
v5
Reanimated Version
v3
Gesture Handler Version
v2
Platforms
Android
What happened?
Text is flickering when using the react native TextInput inside the bottom sheet.
Screen.Recording.2025-04-09.at.12.44.56.PM.mov
Reproduction steps
1.--> Opening the bottom sheet where i have putted some input fields on button click.
2.-->Created one common component like base modal.
3.-->Calling the base modal in my component where i am showing the input boxes and buttons.
4.-->Inside the basemodal component i have used the bottom sheet.
import {
Pressable,
Image,
TouchableOpacity,
TextInput,
BackHandler,
Platform,
Keyboard,
} from "react-native";
import React, {
useCallback,
useRef,
useState,
memo,
useMemo,
useEffect,
} from "react";
import {
BaseModal,
HStack,
Icon,
Spacer,
VStack,
Text,
CollectionVisibility,
CollectionCoverImageTooltip,
GlobalButton,
} from "@components ";
import {
ADD_COLLECTION_ICON,
CLOSE_ICON,
CREATE_COLLECTION_BG,
DELETE_ICON,
ICON_CROP,
INFO_ICON,
REACTANGLE_DOTTED_SQUARE,
UPLOAD_IMAGE_ICON,
} from "src/assets/svg";
import {
Colors,
CustomSpacing,
dimensions,
phoneType,
safeAreaHeight,
} from "@Styles ";
import styles from "./CreateWishListCollection.style";
import ToggleSwitch from "toggle-switch-react-native";
import ImagePicker from "react-native-image-crop-picker";
import { useNavigation } from "@react-navigation/native";
import { observer } from "mobx-react-lite";
import { useStores } from "@store/root.store";
import LinearGradient from "react-native-linear-gradient";
import i18n from "@services/i18n";
const CollectionsName = memo(
({ labeltitle, onCollectionNamePress, isActive }) => (
<TouchableOpacity
onPress={() => onCollectionNamePress("collectionName", labeltitle)}
>
{labeltitle}
)
);
const CreateWishListCollection = observer(
({ innerRef, open, close, type = "", navigation }) => {
const { newsfeedStore, shopStore, authStore } = useStores();
const [keyboardHeight, setKeyboardHeight] = useState(0);
if (!navigation) {
navigation = useNavigation();
}
const coverImageTooltipRef = useRef();
const visibilityTooltipRef = useRef();
const [isPrivate, setPrivete] = useState(false);
const [coverImage, setCoverImage] = useState(null);
const [collectionData, setCollectionData] = useState({
collectionName: "",
collectionDescription: "",
});
const collectionNames = useMemo(
() => ["Makeup Daily", "Skincare Routine", "Exfoliate"],
[]
);
const updateCollectionData = (key, text) => {
setCollectionData((prevData) => ({
...prevData,
[key]: text,
}));
};
const handleKeyboardDidShow = (event) => {
setKeyboardHeight(event.endCoordinates.height);
};
const handleKeyboardDidHide = () => {
setKeyboardHeight(0);
};
useEffect(() => {
const showListener = Keyboard.addListener(
"keyboardDidShow",
handleKeyboardDidShow
);
const hideListener = Keyboard.addListener(
"keyboardDidHide",
handleKeyboardDidHide
);
return () => {
showListener.remove();
hideListener.remove();
};
}, []);
useEffect(() => {
resetData();
}, [newsfeedStore.createCollectionData]);
const resetData = () => {
setCollectionData({
collectionName: "",
collectionDescription: "",
});
setCoverImage(null);
if (type === "wishlist") {
setPrivete(true);
} else {
setPrivete(false);
}
};
const handleClose = () => {
resetData();
shopStore.setWishlistedProduct(null);
shopStore.setSelectedProductToCollection(false);
setTimeout(() => {
close();
}, 100);
};
useEffect(() => {
if (type === "wishlist") {
setPrivete(true);
}
}, [type]);
useEffect(() => {
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
handleBackButton
);
return () => backHandler.remove();
}, []);
const handleBackButton = () => {
close();
};
const toggleSwitch = () => setPrivete((previousState) => !previousState);
const hanldeUploadImage = async () => {
try {
const image = await ImagePicker.openPicker({
cropping: true,
width: dimensions.screenWidth,
height: dimensions.screenWidth / 3,
showCropFrame: true,
cropping: true,
}).then((image) => {
const data = {
name: "",
filename: image.filename,
filepath: image.path,
filetype: image.mime,
sourceURL: image?.sourceURL,
};
return data;
});
setCoverImage(image);
} catch (e) {}
};
const openCropImage = () => {
ImagePicker.openCropper({
path:
Platform.OS === "android"
? coverImage?.filepath
: coverImage?.sourceURL,
width: dimensions.screenWidth,
height: dimensions.screenWidth / 3,
})
.then((image) => {
const data = {
name: "",
filename: image.filename,
filepath: image.path,
filetype: image.mime,
sourceURL: image?.sourceURL,
};
setCoverImage(data);
})
.catch((err) => {});
};
const handleNext = () => {
newsfeedStore.updateCreateCollectionData({
is_private: isPrivate,
description: collectionData?.collectionDescription,
url: coverImage || "",
title: collectionData?.collectionName,
});
if (Platform.OS === "ios") {
newsfeedStore.setCreateCollectionRef(innerRef);
}
resetData();
close();
if (Platform.OS === "android") {
navigation.navigate("CollectionAddProduct", {
type: type,
});
} else {
setTimeout(() => {
navigation.navigate("CollectionAddProduct", {
type: type,
});
}, 500);
}
};
const openVisibilityToolTip = useCallback(() => {
visibilityTooltipRef.current.present();
}, []);
const closeVisibilityToolTip = useCallback(() => {
visibilityTooltipRef.current.close();
}, []);
const openCoverImageToolTip = useCallback(() => {
coverImageTooltipRef.current.present();
}, []);
const closeCoverImageToolTip = useCallback(() => {
coverImageTooltipRef.current.close();
}, []);
return (
<BaseModal
innerRef={innerRef}
enableDynamicSizing
scrollable={true}
onClose={handleClose}
withToast={false}
topInset={safeAreaHeight[phoneType()].top}
keyboardShouldPersistTaps={"handled"}
scrollViewcontentContainerStyle={{
paddingBottom: Platform.OS === "ios" ? keyboardHeight : 0,
}}
applyKeyboardAvoidingView={false}
disablePandown={true}
>
<VStack>
<HStack justifyContent="space-between" horizontal={CustomSpacing(16)}>
<Text style={styles.heading}>
{type === "wishlist"
? "create collection on wishlist"
: i18n.t("CREATE_COLLECTION")}
</Text>
<Pressable onPress={handleClose}>
<Icon
xml={CLOSE_ICON}
height={16}
width={16}
fill={Colors.BLACK_NEUTRAL_900}
/>
</Pressable>
</HStack>
</VStack>
<Spacer height={CustomSpacing(24)} />
<VStack>
{!(type === "wishlist") && (
<LinearGradient
colors={[Colors.LAVENDER_BLUSH_12, Colors.SEASHELL, Colors.WHITE]}
style={styles.gradient}
/>
)}
<VStack horizontal={CustomSpacing(16)}>
{!(type === "wishlist") && (
<VStack style={styles.headerView}>
<VStack style={{ position: "absolute", top: 0 }}>
<Icon
xml={CREATE_COLLECTION_BG}
height={72}
width={dimensions.screenWidth - CustomSpacing(32)}
/>
</VStack>
<HStack>
<VStack style={styles.collectionBg}>
<Icon xml={ADD_COLLECTION_ICON} height={27} width={27} />
</VStack>
<Spacer width={CustomSpacing(16)} />
<Text style={styles.subText}>
{i18n.t("CREATE_COLLECTION_BANNER")}
</Text>
</HStack>
</VStack>
)}
<HStack justifyContent="space-between">
<Text style={styles.inputHeadingText}>
{i18n.t("Collection Name")}
</Text>
<HStack alignment="center">
<Text
style={
collectionData?.collectionName?.length >= 100
? [styles.countText, { color: Colors.NOTIF_RED }]
: styles.countText
}
>
{collectionData?.collectionName?.length}/
</Text>
<Text
style={
collectionData?.collectionName?.length >= 100
? [styles.countText, { color: Colors.NOTIF_RED }]
: collectionData?.collectionName?.length > 0
? [styles.countText, { color: Colors.NOTIF_GREEN }]
: styles.countText
}
>
100
</Text>
</HStack>
</HStack>
<Spacer height={CustomSpacing(4)} />
<TextInput
style={styles.collectionInput}
containerStyle={styles.textInputContainer}
onChangeText={(text) =>
updateCollectionData("collectionName", text)
}
value={collectionData.collectionName}
maxLength={100}
/>
{collectionData.collectionName &&
collectionData.collectionName?.length < 4 && (
<Text style={styles.errortext}>
{i18n.t("Title Min 4 Char")}
</Text>
)}
{!(type === "wishlist") && (
<HStack top={CustomSpacing(8)}>
{collectionNames.map((name) => (
<CollectionsName
key={name}
labeltitle={name}
onCollectionNamePress={updateCollectionData}
isActive={collectionData.collectionName === name}
/>
))}
</HStack>
)}
<Spacer height={CustomSpacing(24)} />
<HStack justifyContent="space-between">
<Text style={styles.inputHeadingText}>
{i18n.t("Description")}
</Text>
<HStack alignment="center">
<Text
style={
collectionData?.collectionDescription?.length >= 500
? [styles.countText, { color: Colors.NOTIF_RED }]
: styles.countText
}
>
{collectionData?.collectionDescription?.length}/
</Text>
<Text
style={
collectionData?.collectionDescription?.length >= 500
? [styles.countText, { color: Colors.NOTIF_RED }]
: collectionData?.collectionDescription?.length > 0
? [styles.countText, { color: Colors.NOTIF_GREEN }]
: styles.countText
}
>
500
</Text>
</HStack>
</HStack>
<Spacer height={CustomSpacing(4)} />
<TextInput
placeholder={i18n.t("Description Placeholder")}
placeholderTextColor={Colors.CHARCOAL_200}
style={styles.descriptionInput}
containerStyle={styles.textInputContainer}
multiline={true}
numberOfLines={4}
textAlignVertical={"top"}
defaultValue={collectionData.collectionDescription}
onChange={({ nativeEvent }) =>
updateCollectionData("collectionDescription", nativeEvent.text)
}
maxLength={500}
/>
{collectionData.collectionDescription &&
collectionData.collectionDescription?.length < 4 && (
<Text style={styles.errortext}>
{i18n.t("Description Min 4 Char")}
</Text>
)}
</VStack>
{authStore.user?.is_expert_reviewer && (
<>
<Spacer height={CustomSpacing(24)} />
<VStack horizontal={CustomSpacing(16)}>
<HStack alignment="center">
<Text style={styles.inputHeadingText}>
{i18n.t("Cover Image (optional)")}
</Text>
<Spacer width={CustomSpacing(4)} />
<Pressable onPress={openCoverImageToolTip}>
<VStack top={CustomSpacing(3)}>
<Icon
xml={INFO_ICON}
height={15}
width={15}
fill={Colors.FLAMINGO_400}
/>
</VStack>
</Pressable>
</HStack>
<Spacer height={CustomSpacing(2)} />
<VStack>
<HStack alignment="flex-start">
{coverImage ? (
<VStack>
<Image
style={styles.image}
source={{
uri: coverImage?.filepath,
}}
/>
</VStack>
) : (
<VStack style={styles.imageBox}>
<VStack style={styles.iconStyle}>
<Icon
xml={REACTANGLE_DOTTED_SQUARE}
height={50}
width={140}
/>
</VStack>
<Icon xml={UPLOAD_IMAGE_ICON} height={20} width={24} />
</VStack>
)}
<Spacer width={CustomSpacing(16)} />
<VStack>
{coverImage ? (
<HStack>
<Pressable
style={styles.uploadImage}
onPress={hanldeUploadImage}
>
<Text style={styles.uploadText}>
{i18n.t("CHANGE IMAGE")}
</Text>
</Pressable>
<Spacer width={CustomSpacing(8)} />
<Pressable onPress={openCropImage}>
<Icon xml={ICON_CROP} height={17} width={17} />
</Pressable>
<Spacer width={CustomSpacing(8)} />
<Pressable onPress={() => setCoverImage(null)}>
<Icon xml={DELETE_ICON} height={15} width={12} />
</Pressable>
</HStack>
) : (
<Pressable
style={styles.uploadImage}
onPress={hanldeUploadImage}
>
<Text style={styles.uploadText}>
{i18n.t("Upload Image")}
</Text>
</Pressable>
)}
<Spacer height={CustomSpacing(6)} />
<Text style={styles.countText}>
{i18n.t("Ratio Image")} 3:1
</Text>
</VStack>
</HStack>
</VStack>
</VStack>
</>
)}
<Spacer height={CustomSpacing(24)} />
<VStack horizontal={CustomSpacing(16)}>
<HStack alignment="center">
<Text style={styles.inputHeadingText}>
{i18n.t("Visibility")}
</Text>
<Spacer width={CustomSpacing(5)} />
<Pressable onPress={openVisibilityToolTip}>
<Icon
xml={INFO_ICON}
height={15}
width={15}
fill={Colors.FLAMINGO_400}
/>
</Pressable>
</HStack>
<Spacer height={CustomSpacing(8)} />
<HStack>
<ToggleSwitch
onColor={Colors.ROSSIE_300}
offColor={Colors.NEUTRAL_200}
onToggle={toggleSwitch}
isOn={!isPrivate}
thumbOnStyle={{
backgroundColor: Colors.FLAMINGO_700,
}}
thumbOffStyle={{
backgroundColor: Colors.CHARCOAL_400,
}}
/>
<Spacer width={CustomSpacing(8)} />
<Text style={styles.privateText}>
{!isPrivate ? i18n.t("Public") : i18n.t("Private")}
</Text>
</HStack>
</VStack>
<VStack style={styles.bottom}>
<GlobalButton
label={i18n.t("NEXT")}
type={"primary"}
onPress={handleNext}
disabled={
!collectionData.collectionName ||
!collectionData.collectionDescription ||
collectionData.collectionName?.length < 4 ||
collectionData.collectionName?.length > 100 ||
collectionData.collectionDescription?.length < 4 ||
collectionData.collectionDescription?.length > 500
}
/>
</VStack>
<CollectionVisibility
innerRef={visibilityTooltipRef}
close={closeVisibilityToolTip}
isVN={authStore?.country === "Vietnam"}
/>
<CollectionCoverImageTooltip
innerRef={coverImageTooltipRef}
close={closeCoverImageToolTip}
/>
</VStack>
</BaseModal>
);
}
);
export default CreateWishListCollection;
import React from "react";
import BottomSheet from "./BottomSheet";
import BottomSheetV2 from "./BottomSheetV2";
import { Text } from "@components ";
// import PropTypes from "prop-types";
/**
@author Chandra Santoso chandra.santoso@sociolla.com
@name BaseModal
@Property {string} type - type of modal "bottom" | ...soon will update @todo @chandra
@Property {object} innerRef - useRef from source screen
@Property {array} snapList - array of snap size list
@Property {any} children - any react component to pass
@Property {boolean} enableDynamicSizing - enableDynamicSizing for children component
@Property {boolean} disablePandown - disablePandown for children component
@example
const exampleRef = useRef(null)
<BaseModal innerRef={exampleRef} snapList={["25%","50%""]} enableDynamicSizing={true}>
Example Children
*/
const BaseModal = ({
type = "bottom",
innerRef,
snapList = ["50%"],
enableDynamicSizing = false,
children = Empty Modal,
disablePandown = false,
footerComponent,
scrollable,
buttonParams,
headerParams,
isLoading,
onClose,
onDismiss,
stickyHeaderIndices,
showsVerticalScrollIndicator,
bottomInset,
bottomRerender,
headerSection,
handleIndicatorStyle,
footerButton,
backgroundStyle,
handleStyle,
disableBackdropPress = false,
withToast = true,
topInset = 0,
onOpen = () => {},
bottomSafeAreaHeight,
keyboardShouldPersistTaps,
automaticallyAdjustKeyboardInsets,
scrollViewcontentContainerStyle,
applyKeyboardAvoidingView,
alwaysBounceVertical,
bounces,
disableHardwareBackPress = false,
...props
}) => {
if (type === "bottom") {
return (
<BottomSheet
innerRef={innerRef}
enableDynamicSizing={enableDynamicSizing}
snapList={snapList}
disablePandown={disablePandown}
footerComponent={footerComponent}
scrollable={scrollable}
buttonParams={buttonParams}
footerButton={footerButton}
headerParams={headerParams}
isLoading={isLoading}
handleClose={onClose}
handleDismiss={onDismiss}
headerSection={headerSection}
stickyHeaderIndices={stickyHeaderIndices}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
bottomInset={bottomInset}
bottomRerender={bottomRerender}
handleIndicatorStyle={handleIndicatorStyle}
backgroundStyle={backgroundStyle}
handleStyle={handleStyle}
disableBackdropPress={disableBackdropPress}
withToast={withToast}
topInset={topInset}
onOpen={onOpen}
bottomSafeAreaHeight={bottomSafeAreaHeight}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
automaticallyAdjustKeyboardInsets={automaticallyAdjustKeyboardInsets}
scrollViewcontentContainerStyle={scrollViewcontentContainerStyle}
applyKeyboardAvoidingView={applyKeyboardAvoidingView}
alwaysBounceVertical={alwaysBounceVertical}
bounces={bounces}
disableHardwareBackPress={disableHardwareBackPress}
{...props}
>
{children}
);
} else if (type === "v2") {
return (
<BottomSheetV2
innerRef={innerRef}
enableDynamicSizing={enableDynamicSizing}
snapList={snapList}
disablePandown={disablePandown}
footerComponent={footerComponent}
scrollable={scrollable}
buttonParams={buttonParams}
footerButton={footerButton}
headerParams={headerParams}
isLoading={isLoading}
handleClose={onClose}
headerSection={headerSection}
stickyHeaderIndices={stickyHeaderIndices}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
bottomInset={bottomInset}
bottomRerender={bottomRerender}
handleIndicatorStyle={handleIndicatorStyle}
backgroundStyle={backgroundStyle}
handleStyle={handleStyle}
disableBackdropPress={disableBackdropPress}
withToast={withToast}
topInset={topInset}
onOpen={onOpen}
bottomSafeAreaHeight={bottomSafeAreaHeight}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
automaticallyAdjustKeyboardInsets={automaticallyAdjustKeyboardInsets}
scrollViewcontentContainerStyle={scrollViewcontentContainerStyle}
applyKeyboardAvoidingView={applyKeyboardAvoidingView}
{...props}
>
{children}
);
}
};
// BaseModal.defaultProps = {
// type: "bottom",
// snapList: ["50%"],
// children: Empty Modal,
// enableDynamicSizing: false,
// disablePandown: false,
// onOpen: () => {},
// };
// BaseModal.propTypes = {
// type: PropTypes.string,
// innerRef: PropTypes.object.isRequired,
// snapList: PropTypes.array,
// children: PropTypes.any.isRequired,
// enableDynamicSizing: PropTypes.bool,
// disablePandown: PropTypes.bool,
// footerComponent: PropTypes.any,
// scrollable: PropTypes.bool,
// buttonParams: PropTypes.shape({
// buttonStyle: PropTypes.object,
// typeButton: PropTypes.oneOf([
// "outline",
// "secondary",
// "outline",
// "filter",
// "filter_active",
// "form_footer",
// "disabled",
// "primary",
// ]),
// onPress: PropTypes.func,
// disabled: PropTypes.bool,
// labelStyle: PropTypes.object,
// labelButton: PropTypes.string,
// }),
// headerParams: PropTypes.shape({
// title: PropTypes.string,
// }),
// isLoading: PropTypes.bool,
// onClose: PropTypes.func,
// onDismiss: PropTypes.func,
// onOpen: PropTypes.func,
// stickyHeaderIndices: PropTypes.array,
// showsVerticalScrollIndicator: PropTypes.bool,
// bottomInset: PropTypes.number,
// bottomRerender: PropTypes.array,
// handleIndicatorStyle: PropTypes.object,
// backgroundStyle: PropTypes.object,
// bottomSafeAreaHeight: PropTypes.bool,
// };
export default BaseModal;
import React, { useMemo, useCallback, useState, useEffect } from "react";
import {
BottomSheetModal,
BottomSheetBackdrop,
BottomSheetView,
BottomSheetScrollView,
BottomSheetFooter,
ANIMATION_CONFIGS,
SCREEN_WIDTH,
} from "@gorhom/bottom-sheet";
import { ReduceMotion } from "react-native-reanimated";
import {
GlobalButton,
HStack,
Icon,
LoadingOverlay,
Spacer,
Text,
VStack,
} from "@components ";
import { Colors, CustomSpacing } from "@Styles ";
import {
BackHandler,
Keyboard,
KeyboardAvoidingView,
Platform,
Pressable,
SafeAreaView,
} from "react-native";
import { CLOSE_ICON } from "src/assets/svg";
import Toast from "react-native-toast-message";
import toastConfig from "../Toast/toastConfig";
import { FONT_FAMILY, FONT_SIZE } from "@styles/fontConstants";
import RenderHTML from "react-native-render-html";
// import PropTypes from "prop-types";
/**
@author Chandra Santoso chandra.santoso@sociolla.com
@name BottomSheet
@Property {object} innerRef - useRef from source screen
@Property {array} snapList - array of snap size list
@Property {any} children - any react component to pass
@Property {boolean} enableDynamicSizing - enableDynamicSizing for children component
@Property {boolean} disablePandown - disablePandown for children component
@example
const exampleRef = useRef(null)
<BottomSheet innerRef={exampleRef} snapList={["25%","50%""]} enableDynamicSizing={true}>
Example Children
*/
const bottomSheetAnimationConfig = {
...ANIMATION_CONFIGS,
reduceMotion: ReduceMotion.Never,
};
const tagStyle = {
li: {
top: CustomSpacing(-2),
},
p: {
fontSize: CustomSpacing(16),
fontFamily: FONT_FAMILY.LATO_BOLD,
lineHeight: CustomSpacing(22),
padding: 0,
margin: 0,
marginBottom: CustomSpacing(4),
},
b: {
fontFamily: FONT_FAMILY.LATO_BOLD,
fontSize: CustomSpacing(16),
},
};
const defaultTextProps = {
numberOfLines: 1,
};
const HTML = ({ data, withEllipsis = true, baseStyle = {} }) => {
let url = data;
if (withEllipsis) {
const splitDataTitle = data?.split("
");
if (splitDataTitle?.length > 1) {
url =
${splitDataTitle[0]}...</p>;
}
}
if (!url) {
return null;
}
return (
<RenderHTML
systemFonts={[FONT_FAMILY.LATO_BOLD]}
defaultTextProps={defaultTextProps}
tagsStyles={tagStyle}
contentWidth={SCREEN_WIDTH}
source={{ html: url }}
baseStyle={baseStyle}
/>
);
};
const BottomSheet = ({
innerRef,
snapList = ["50%"],
enableDynamicSizing = false,
disablePandown = false,
children = Empty Modal,
footerComponent,
scrollable = false,
isLoading = false,
buttonParams = null,
footerButton,
handleClose,
handleDismiss,
stickyHeaderIndices = [],
showsVerticalScrollIndicator = true,
bottomInset = 20,
bottomRerender = [],
handleIndicatorStyle = {},
backgroundStyle = {},
headerParams,
headerSection,
handleStyle = {},
disableBackdropPress,
keyboardBehavior = "fillParent",
withToast,
topInset,
onOpen,
bottomSafeAreaHeight,
keyboardShouldPersistTaps = "never",
mockedPaddingToModal = false,
automaticallyAdjustKeyboardInsets = false,
toastPosition = "top",
scrollViewcontentContainerStyle = {},
applyKeyboardAvoidingView = true,
bounces = true,
alwaysBounceVertical = true,
disableHardwareBackPress = false,
...props
}) => {
const [currentIndex, setCurrentIndex] = useState(-1);
const snapPoints = useMemo(() => snapList, []);
const hideBottomSheet = () => {
Keyboard.dismiss();
innerRef.current?.dismiss();
if (handleClose) {
handleClose();
}
};
const onBackPress = () => {
if (innerRef !== null && !disableHardwareBackPress) {
innerRef.current?.dismiss();
return true;
}
};
useEffect(() => {
if (currentIndex !== -1) {
if (onOpen) {
onOpen();
}
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
onBackPress
);
return () => backHandler.remove();
}
}, [currentIndex]);
const onBackClose = (index) => {
setCurrentIndex(index);
};
const renderToastConfig = () => {
return (
<VStack style={{ zIndex: 99 }}>
<Toast
config={toastConfig}
topOffset={toastConfig === "top" ? 0 : CustomSpacing(8)}
visibilityTime={2000}
/>
);
};
const renderBackdrop = useCallback(
(props) => (
<BottomSheetBackdrop
{...props}
pressBehavior={"collapse"}
disappearsOnIndex={-1}
appearsOnIndex={0}
onPress={!disableBackdropPress ? hideBottomSheet : null}
opacity={0.5}
keyboardBehavior={keyboardBehavior}
>
{withToast && toastPosition === "top" && renderToastConfig()}
),
[disableBackdropPress]
);
const renderButton = () => {
if (footerButton) {
return footerButton();
} else if (buttonParams) {
return (
<>
</>
);
}
};
const renderFooter = useCallback(
(props) => (
<BottomSheetFooter {...props} bottomInset={bottomInset}>
{footerComponent}
),
[...bottomRerender, footerComponent]
);
const renderHeaderSection = () => {
if (headerSection) {
return headerSection;
}
};
return (
<BottomSheetModal
animationConfigs={bottomSheetAnimationConfig}
ref={innerRef}
index={0}
snapPoints={enableDynamicSizing ? [] : snapPoints}
backdropComponent={renderBackdrop}
enableDynamicSizing={enableDynamicSizing}
enablePanDownToClose={disablePandown ? false : true}
footerComponent={renderFooter}
handleIndicatorStyle={[
{ height: CustomSpacing(3), backgroundColor: Colors.BRIGHT_GRAY },
handleIndicatorStyle,
]}
backgroundStyle={backgroundStyle}
handleStyle={handleStyle}
topInset={topInset}
onChange={onBackClose}
keyboardBehavior={Platform.OS === "ios" ? "interactive" : "height"}
keyboardBlurBehavior="restore"
onDismiss={handleDismiss}
{...props}
>
{headerParams && (
<HStack justifyContent={"space-between"}>
{headerParams?.useHtml ? (
) : (
<Text
numberOfLines={1}
style={{ ...{ letterSpacing: 0 }, ...headerParams?.titleStyle }}
type="readmoreModal"
>
{headerParams?.title || ""}
)}
<Icon
fill={headerParams?.iconColor || Colors.BLACK_NEUTRAL_900}
xml={CLOSE_ICON}
width={16}
height={16}
/>
)}
{withToast && toastPosition === "bottom" && renderToastConfig()}
{renderHeaderSection()}
{scrollable ? (
applyKeyboardAvoidingView ? (
<>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<BottomSheetScrollView
stickyHeaderIndices={stickyHeaderIndices}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
scrollEnabled={!mockedPaddingToModal}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
automaticallyAdjustKeyboardInsets={
automaticallyAdjustKeyboardInsets
}
contentContainerStyle={scrollViewcontentContainerStyle}
>
{children}
{mockedPaddingToModal && (
<>
{headerParams && }
{buttonParams && }
</>
)}
{renderButton()}
</>
) : (
<>
<BottomSheetScrollView
stickyHeaderIndices={stickyHeaderIndices}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
scrollEnabled={!mockedPaddingToModal}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
automaticallyAdjustKeyboardInsets={
automaticallyAdjustKeyboardInsets
}
contentContainerStyle={scrollViewcontentContainerStyle}
bounces={bounces}
alwaysBounceVertical={false}
>
{children}
{mockedPaddingToModal && (
<>
{headerParams && }
{buttonParams && }
</>
)}
{renderButton()}
</>
)
) : (
<BottomSheetView>
{children}
{renderButton()}
{bottomSafeAreaHeight && <Spacer bottomSafeAreaHeight />}
</BottomSheetView>
)}
</BottomSheetModal>
);
};
// BottomSheet.defaultProps = {
// snapList: ["50%"],
// children: Empty Modal,
// enableDynamicSizing: false,
// disablePandown: false,
// scrollable: false,
// buttonParams: null,
// isLoading: false,
// stickyHeaderIndices: [],
// showsVerticalScrollIndicator: true,
// bottomInset: 20,
// bottomRerender: [],
// handleIndicatorStyle: {},
// backgroundStyle: {},
// handleStyle: {},
// scrollViewcontentContainerStyle: {},
// };
// BottomSheet.propTypes = {
// innerRef: PropTypes.object.isRequired,
// snapList: PropTypes.array.isRequired,
// children: PropTypes.any.isRequired,
// enableDynamicSizing: PropTypes.bool,
// disablePandown: PropTypes.bool,
// footerComponent: PropTypes.any,
// scrollable: PropTypes.bool,
// isLoading: PropTypes.bool,
// onClose: PropTypes.func,
// onOpen: PropTypes.func,
// handleDismiss: PropTypes.func,
// buttonParams: PropTypes.shape({
// buttonStyle: PropTypes.object,
// typeButton: PropTypes.oneOf([
// "outline",
// "secondary",
// "outline",
// "filter",
// "filter_active",
// "form_footer",
// "disabled",
// "primary",
// ]),
// }),
// handleClose: PropTypes.func,
// stickyHeaderIndices: PropTypes.array,
// showsVerticalScrollIndicator: PropTypes.bool,
// bottomInset: PropTypes.number,
// bottomRerender: PropTypes.array,
// handleIndicatorStyle: PropTypes.object,
// backgroundStyle: PropTypes.object,
// headerParams: PropTypes.shape({
// title: PropTypes.string,
// }),
// keyboardBehavior: PropTypes.string,
// bottomSafeAreaHeight: PropTypes.bool,
// };
export default BottomSheet;
-----package.json----
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.22.15",
"@callstack/react-theme-provider": "^3.0.9",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-regular-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@gorhom/bottom-sheet": "^5.1.2",
"@kichiyaki/react-native-barcode-generator": "^0.6.7",
"@likashefqet/react-native-image-zoom": "^4.1.0",
"@native-html/iframe-plugin": "^2.6.1",
"@notifee/react-native": "^9.1.8",
"@openspacelabs/react-native-zoomable-view": "^2.1.6",
"@react-native-assets/slider": "^7.2.1",
"@react-native-async-storage/async-storage": "^1.19.3",
"@react-native-camera-roll/camera-roll": "^7.9.0",
"@react-native-clipboard/clipboard": "^1.16.2",
"@react-native-community/image-editor": "^4.2.0",
"@react-native-community/netinfo": "^11.3.2",
"@react-native-community/push-notification-ios": "^1.11.0",
"@react-native-firebase/analytics": "^19.2.2",
"@react-native-firebase/app": "^19.2.2",
"@react-native-firebase/crashlytics": "^19.2.2",
"@react-native-firebase/messaging": "^19.2.2",
"@react-native-firebase/remote-config": "^19.2.2",
"@react-native-masked-view/masked-view": "^0.3.2",
"@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/native": "^6.1.18",
"@react-navigation/native-stack": "^6.x",
"@react-navigation/stack": "^6.4.1",
"@shopify/flash-list": "^1.7.5",
"axios": "^1.5.0",
"babel-plugin-inline-import": "^3.0.0",
"google-libphonenumber": "^3.2.40",
"i18next": "^23.5.1",
"mobx": "^6.10.2",
"mobx-persist": "^0.4.1",
"mobx-react-lite": "^4.0.4",
"numbro": "^2.4.0",
"patch-package": "^8.0.0",
"postinstall-postinstall": "^2.1.0",
"prop-types": "^15.8.1",
"react": "18.3.1",
"react-i18next": "^13.2.2",
"react-native": "0.77.1",
"react-native-animatable": "^1.3.3",
"react-native-animated-dots-carousel": "^1.0.2",
"react-native-applifecycle": "^1.0.0",
"react-native-avoid-softinput": "^7.0.1",
"react-native-awesome-slider": "^2.5.3",
"react-native-branch": "6.2.1",
"react-native-calendars": "^1.1303.0",
"react-native-collapsible": "^1.6.1",
"react-native-config": "^1.4.6",
"react-native-contacts": "^8.0.4",
"react-native-copilot": "^3.3.3",
"react-native-date-picker": "5.0.10",
"react-native-device-info": "^14.0.4",
"react-native-error-boundary": "^1.2.4",
"react-native-fast-image": "^8.6.3",
"react-native-fbsdk-next": "^13.0.0",
"react-native-fs": "2.16.1",
"react-native-geolocation-service": "^5.3.1",
"react-native-gesture-handler": "2.24.0",
"react-native-image-crop-picker": "^0.41.2",
"react-native-image-picker": "^7.1.0",
"react-native-image-viewing": "^0.2.2",
"react-native-insider": "^6.4.5-nh",
"react-native-keychain": "^8.1.3",
"react-native-linear-gradient": "^2.8.3",
"react-native-localize": "^3.3.0",
"react-native-maps": "1.14.0",
"react-native-network-logger": "^1.15.0",
"react-native-pager-view": "^6.7.0",
"react-native-permissions": "^3.9.2",
"react-native-popover-view": "^6.1.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-reanimated": "^3.17.1",
"react-native-reanimated-zoom": "^0.3.3",
"react-native-render-html": "^6.3.4",
"react-native-safe-area-context": "^5.3.0",
"react-native-screens": "4.9.2",
"react-native-shake": "^4.0.2",
"react-native-share": "^12.0.9",
"react-native-size-matters": "^0.4.0",
"react-native-skeleton-placeholder": "^5.2.4",
"react-native-sms": "^1.11.0",
"react-native-splash-screen": "^3.3.0",
"react-native-svg": "^15.11.2",
"react-native-svg-transformer": "^1.1.0",
"react-native-tab-view": "^4.0.8",
"react-native-toast-message": "^2.2.0",
"react-native-tracking-transparency": "^0.1.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-user-agent": "^2.3.1",
"react-native-video": "^6.11.0",
"react-native-video-duration": "^0.1.2",
"react-native-view-shot": "^3.8.0",
"react-native-vision-camera": "^4.6.4",
"react-native-webview": "^13.13.4",
"react-native-youtube-iframe": "^2.3.0",
"rn-fetch-blob": "^0.12.0",
"rn-placeholder": "^3.0.3",
"sanitize-html": "^2.13.0",
"socket.io-client": "^2.3.1",
"text-clipper": "^2.2.0",
"toggle-switch-react-native": "^3.3.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.16.5",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli": "15.0.1",
"@react-native-community/cli-platform-android": "15.0.1",
"@react-native-community/cli-platform-ios": "15.0.1",
"@react-native/babel-preset": "0.77.1",
"@react-native/eslint-config": "0.77.1",
"@react-native/metro-config": "0.77.1",
"@react-native/typescript-config": "0.77.1",
"@tsconfig/react-native": "^3.0.0",
"@types/jest": "^29.5.13",
"@types/react": "^18.2.6",
"@types/react-test-renderer": "^18.0.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^29.6.3",
"babel-plugin-module-resolver": "^5.0.0",
"eslint": "^7.32.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.3.0",
"eslint-config-sociolla": "git+ssh://git@bitbucket.org /Sociolla/eslint-config-sociolla.git",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-react-native": "^4.0.0",
"husky": "^8.0.0",
"jest": "^29.6.3",
"lint-staged": "^15.5.0",
"prettier": "2.4.1",
"react-native-flipper-performance-plugin": "^0.4.0",
"react-test-renderer": "18.3.1",
"reactotron-react-native": "^5.1.12",
"typescript": "5.0.4"
},
"engines": {
"node": ">=18"
},
Reproduction sample
NA
Relevant log output
Version
v5
Reanimated Version
v3
Gesture Handler Version
v2
Platforms
Android
What happened?
Text is flickering when using the react native TextInput inside the bottom sheet.
Screen.Recording.2025-04-09.at.12.44.56.PM.mov
Reproduction steps
import {
Pressable,
Image,
TouchableOpacity,
TextInput,
BackHandler,
Platform,
Keyboard,
} from "react-native";
import React, {
useCallback,
useRef,
useState,
memo,
useMemo,
useEffect,
} from "react";
import {
BaseModal,
HStack,
Icon,
Spacer,
VStack,
Text,
CollectionVisibility,
CollectionCoverImageTooltip,
GlobalButton,
} from "@components";
import {
ADD_COLLECTION_ICON,
CLOSE_ICON,
CREATE_COLLECTION_BG,
DELETE_ICON,
ICON_CROP,
INFO_ICON,
REACTANGLE_DOTTED_SQUARE,
UPLOAD_IMAGE_ICON,
} from "src/assets/svg";
import {
Colors,
CustomSpacing,
dimensions,
phoneType,
safeAreaHeight,
} from "@Styles";
import styles from "./CreateWishListCollection.style";
import ToggleSwitch from "toggle-switch-react-native";
import ImagePicker from "react-native-image-crop-picker";
import { useNavigation } from "@react-navigation/native";
import { observer } from "mobx-react-lite";
import { useStores } from "@store/root.store";
import LinearGradient from "react-native-linear-gradient";
import i18n from "@services/i18n";
const CollectionsName = memo(
({ labeltitle, onCollectionNamePress, isActive }) => (
<TouchableOpacity
onPress={() => onCollectionNamePress("collectionName", labeltitle)}
>
{labeltitle}
)
);
const CreateWishListCollection = observer(
({ innerRef, open, close, type = "", navigation }) => {
const { newsfeedStore, shopStore, authStore } = useStores();
const [keyboardHeight, setKeyboardHeight] = useState(0);
if (!navigation) {
navigation = useNavigation();
}
const coverImageTooltipRef = useRef();
const visibilityTooltipRef = useRef();
const [isPrivate, setPrivete] = useState(false);
const [coverImage, setCoverImage] = useState(null);
}
);
export default CreateWishListCollection;
import React from "react";
import BottomSheet from "./BottomSheet";
import BottomSheetV2 from "./BottomSheetV2";
import { Text } from "@components";
// import PropTypes from "prop-types";
/**
*/
const BaseModal = ({
type = "bottom",
innerRef,
snapList = ["50%"],
enableDynamicSizing = false,
children = Empty Modal,
disablePandown = false,
footerComponent,
scrollable,
buttonParams,
headerParams,
isLoading,
onClose,
onDismiss,
stickyHeaderIndices,
showsVerticalScrollIndicator,
bottomInset,
bottomRerender,
headerSection,
handleIndicatorStyle,
footerButton,
backgroundStyle,
handleStyle,
disableBackdropPress = false,
withToast = true,
topInset = 0,
onOpen = () => {},
bottomSafeAreaHeight,
keyboardShouldPersistTaps,
automaticallyAdjustKeyboardInsets,
scrollViewcontentContainerStyle,
applyKeyboardAvoidingView,
alwaysBounceVertical,
bounces,
disableHardwareBackPress = false,
...props
}) => {
if (type === "bottom") {
return (
<BottomSheet
innerRef={innerRef}
enableDynamicSizing={enableDynamicSizing}
snapList={snapList}
disablePandown={disablePandown}
footerComponent={footerComponent}
scrollable={scrollable}
buttonParams={buttonParams}
footerButton={footerButton}
headerParams={headerParams}
isLoading={isLoading}
handleClose={onClose}
handleDismiss={onDismiss}
headerSection={headerSection}
stickyHeaderIndices={stickyHeaderIndices}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
bottomInset={bottomInset}
bottomRerender={bottomRerender}
handleIndicatorStyle={handleIndicatorStyle}
backgroundStyle={backgroundStyle}
handleStyle={handleStyle}
disableBackdropPress={disableBackdropPress}
withToast={withToast}
topInset={topInset}
onOpen={onOpen}
bottomSafeAreaHeight={bottomSafeAreaHeight}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
automaticallyAdjustKeyboardInsets={automaticallyAdjustKeyboardInsets}
scrollViewcontentContainerStyle={scrollViewcontentContainerStyle}
applyKeyboardAvoidingView={applyKeyboardAvoidingView}
alwaysBounceVertical={alwaysBounceVertical}
bounces={bounces}
disableHardwareBackPress={disableHardwareBackPress}
{...props}
>
{children}
);
} else if (type === "v2") {
return (
<BottomSheetV2
innerRef={innerRef}
enableDynamicSizing={enableDynamicSizing}
snapList={snapList}
disablePandown={disablePandown}
footerComponent={footerComponent}
scrollable={scrollable}
buttonParams={buttonParams}
footerButton={footerButton}
headerParams={headerParams}
isLoading={isLoading}
handleClose={onClose}
headerSection={headerSection}
stickyHeaderIndices={stickyHeaderIndices}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
bottomInset={bottomInset}
bottomRerender={bottomRerender}
handleIndicatorStyle={handleIndicatorStyle}
backgroundStyle={backgroundStyle}
handleStyle={handleStyle}
disableBackdropPress={disableBackdropPress}
withToast={withToast}
topInset={topInset}
onOpen={onOpen}
bottomSafeAreaHeight={bottomSafeAreaHeight}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
automaticallyAdjustKeyboardInsets={automaticallyAdjustKeyboardInsets}
scrollViewcontentContainerStyle={scrollViewcontentContainerStyle}
applyKeyboardAvoidingView={applyKeyboardAvoidingView}
{...props}
>
{children}
);
}
};
// BaseModal.defaultProps = {
// type: "bottom",
// snapList: ["50%"],
// children: Empty Modal,
// enableDynamicSizing: false,
// disablePandown: false,
// onOpen: () => {},
// };
// BaseModal.propTypes = {
// type: PropTypes.string,
// innerRef: PropTypes.object.isRequired,
// snapList: PropTypes.array,
// children: PropTypes.any.isRequired,
// enableDynamicSizing: PropTypes.bool,
// disablePandown: PropTypes.bool,
// footerComponent: PropTypes.any,
// scrollable: PropTypes.bool,
// buttonParams: PropTypes.shape({
// buttonStyle: PropTypes.object,
// typeButton: PropTypes.oneOf([
// "outline",
// "secondary",
// "outline",
// "filter",
// "filter_active",
// "form_footer",
// "disabled",
// "primary",
// ]),
// onPress: PropTypes.func,
// disabled: PropTypes.bool,
// labelStyle: PropTypes.object,
// labelButton: PropTypes.string,
// }),
// headerParams: PropTypes.shape({
// title: PropTypes.string,
// }),
// isLoading: PropTypes.bool,
// onClose: PropTypes.func,
// onDismiss: PropTypes.func,
// onOpen: PropTypes.func,
// stickyHeaderIndices: PropTypes.array,
// showsVerticalScrollIndicator: PropTypes.bool,
// bottomInset: PropTypes.number,
// bottomRerender: PropTypes.array,
// handleIndicatorStyle: PropTypes.object,
// backgroundStyle: PropTypes.object,
// bottomSafeAreaHeight: PropTypes.bool,
// };
export default BaseModal;
import React, { useMemo, useCallback, useState, useEffect } from "react";
import {
BottomSheetModal,
BottomSheetBackdrop,
BottomSheetView,
BottomSheetScrollView,
BottomSheetFooter,
ANIMATION_CONFIGS,
SCREEN_WIDTH,
} from "@gorhom/bottom-sheet";
import { ReduceMotion } from "react-native-reanimated";
import {
GlobalButton,
HStack,
Icon,
LoadingOverlay,
Spacer,
Text,
VStack,
} from "@components";
import { Colors, CustomSpacing } from "@Styles";
import {
BackHandler,
Keyboard,
KeyboardAvoidingView,
Platform,
Pressable,
SafeAreaView,
} from "react-native";
import { CLOSE_ICON } from "src/assets/svg";
import Toast from "react-native-toast-message";
import toastConfig from "../Toast/toastConfig";
import { FONT_FAMILY, FONT_SIZE } from "@styles/fontConstants";
import RenderHTML from "react-native-render-html";
// import PropTypes from "prop-types";
/**
*/
const bottomSheetAnimationConfig = {
...ANIMATION_CONFIGS,
reduceMotion: ReduceMotion.Never,
};
const tagStyle = {
li: {
top: CustomSpacing(-2),
},
p: {
fontSize: CustomSpacing(16),
fontFamily: FONT_FAMILY.LATO_BOLD,
lineHeight: CustomSpacing(22),
padding: 0,
margin: 0,
marginBottom: CustomSpacing(4),
},
b: {
fontFamily: FONT_FAMILY.LATO_BOLD,
fontSize: CustomSpacing(16),
},
};
const defaultTextProps = {
numberOfLines: 1,
};
const HTML = ({ data, withEllipsis = true, baseStyle = {} }) => {
let url = data;
if (withEllipsis) {
");const splitDataTitle = data?.split("
if (splitDataTitle?.length > 1) {
url =
${splitDataTitle[0]}...</p>;}
}
if (!url) {
return null;
}
return (
<RenderHTML
systemFonts={[FONT_FAMILY.LATO_BOLD]}
defaultTextProps={defaultTextProps}
tagsStyles={tagStyle}
contentWidth={SCREEN_WIDTH}
source={{ html: url }}
baseStyle={baseStyle}
/>
);
};
const BottomSheet = ({
innerRef,
snapList = ["50%"],
enableDynamicSizing = false,
disablePandown = false,
children = Empty Modal,
footerComponent,
scrollable = false,
isLoading = false,
buttonParams = null,
footerButton,
handleClose,
handleDismiss,
stickyHeaderIndices = [],
showsVerticalScrollIndicator = true,
bottomInset = 20,
bottomRerender = [],
handleIndicatorStyle = {},
backgroundStyle = {},
headerParams,
headerSection,
handleStyle = {},
disableBackdropPress,
keyboardBehavior = "fillParent",
withToast,
topInset,
onOpen,
bottomSafeAreaHeight,
keyboardShouldPersistTaps = "never",
mockedPaddingToModal = false,
automaticallyAdjustKeyboardInsets = false,
toastPosition = "top",
scrollViewcontentContainerStyle = {},
applyKeyboardAvoidingView = true,
bounces = true,
alwaysBounceVertical = true,
disableHardwareBackPress = false,
...props
}) => {
const [currentIndex, setCurrentIndex] = useState(-1);
const snapPoints = useMemo(() => snapList, []);
const hideBottomSheet = () => {
Keyboard.dismiss();
innerRef.current?.dismiss();
if (handleClose) {
handleClose();
}
};
const onBackPress = () => {
if (innerRef !== null && !disableHardwareBackPress) {
innerRef.current?.dismiss();
return true;
}
};
useEffect(() => {
if (currentIndex !== -1) {
if (onOpen) {
onOpen();
}
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
onBackPress
);
}, [currentIndex]);
const onBackClose = (index) => {
setCurrentIndex(index);
};
const renderToastConfig = () => {
return (
<VStack style={{ zIndex: 99 }}>
<Toast
config={toastConfig}
topOffset={toastConfig === "top" ? 0 : CustomSpacing(8)}
visibilityTime={2000}
/>
);
};
const renderBackdrop = useCallback(
(props) => (
<BottomSheetBackdrop
{...props}
pressBehavior={"collapse"}
disappearsOnIndex={-1}
appearsOnIndex={0}
onPress={!disableBackdropPress ? hideBottomSheet : null}
opacity={0.5}
keyboardBehavior={keyboardBehavior}
>
{withToast && toastPosition === "top" && renderToastConfig()}
),
[disableBackdropPress]
);
const renderButton = () => {
if (footerButton) {
return footerButton();
} else if (buttonParams) {
return (
<>
</>
);
}
};
const renderFooter = useCallback(
(props) => (
<BottomSheetFooter {...props} bottomInset={bottomInset}>
{footerComponent}
),
[...bottomRerender, footerComponent]
);
const renderHeaderSection = () => {
if (headerSection) {
return headerSection;
}
};
return (
<BottomSheetModal
animationConfigs={bottomSheetAnimationConfig}
ref={innerRef}
index={0}
snapPoints={enableDynamicSizing ? [] : snapPoints}
backdropComponent={renderBackdrop}
enableDynamicSizing={enableDynamicSizing}
enablePanDownToClose={disablePandown ? false : true}
footerComponent={renderFooter}
handleIndicatorStyle={[
{ height: CustomSpacing(3), backgroundColor: Colors.BRIGHT_GRAY },
handleIndicatorStyle,
]}
backgroundStyle={backgroundStyle}
handleStyle={handleStyle}
topInset={topInset}
onChange={onBackClose}
keyboardBehavior={Platform.OS === "ios" ? "interactive" : "height"}
keyboardBlurBehavior="restore"
onDismiss={handleDismiss}
{...props}
>
{headerParams && (
<HStack justifyContent={"space-between"}>
{headerParams?.useHtml ? (
) : (
<Text
numberOfLines={1}
style={{ ...{ letterSpacing: 0 }, ...headerParams?.titleStyle }}
type="readmoreModal"
>
{headerParams?.title || ""}
)}
<Icon
fill={headerParams?.iconColor || Colors.BLACK_NEUTRAL_900}
xml={CLOSE_ICON}
width={16}
height={16}
/>
)}
{withToast && toastPosition === "bottom" && renderToastConfig()}
{renderHeaderSection()}
{scrollable ? (
applyKeyboardAvoidingView ? (
<>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<BottomSheetScrollView
stickyHeaderIndices={stickyHeaderIndices}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
scrollEnabled={!mockedPaddingToModal}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
automaticallyAdjustKeyboardInsets={
automaticallyAdjustKeyboardInsets
}
contentContainerStyle={scrollViewcontentContainerStyle}
>
{children}
{mockedPaddingToModal && (
<>
{headerParams && }
{buttonParams && }
</>
)}
{renderButton()}
</>
) : (
<>
<BottomSheetScrollView
stickyHeaderIndices={stickyHeaderIndices}
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
scrollEnabled={!mockedPaddingToModal}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
automaticallyAdjustKeyboardInsets={
automaticallyAdjustKeyboardInsets
}
contentContainerStyle={scrollViewcontentContainerStyle}
bounces={bounces}
alwaysBounceVertical={false}
>
{children}
{mockedPaddingToModal && (
<>
{headerParams && }
{buttonParams && }
</>
)}
);
};
// BottomSheet.defaultProps = {
// snapList: ["50%"],
// children: Empty Modal,
// enableDynamicSizing: false,
// disablePandown: false,
// scrollable: false,
// buttonParams: null,
// isLoading: false,
// stickyHeaderIndices: [],
// showsVerticalScrollIndicator: true,
// bottomInset: 20,
// bottomRerender: [],
// handleIndicatorStyle: {},
// backgroundStyle: {},
// handleStyle: {},
// scrollViewcontentContainerStyle: {},
// };
// BottomSheet.propTypes = {
// innerRef: PropTypes.object.isRequired,
// snapList: PropTypes.array.isRequired,
// children: PropTypes.any.isRequired,
// enableDynamicSizing: PropTypes.bool,
// disablePandown: PropTypes.bool,
// footerComponent: PropTypes.any,
// scrollable: PropTypes.bool,
// isLoading: PropTypes.bool,
// onClose: PropTypes.func,
// onOpen: PropTypes.func,
// handleDismiss: PropTypes.func,
// buttonParams: PropTypes.shape({
// buttonStyle: PropTypes.object,
// typeButton: PropTypes.oneOf([
// "outline",
// "secondary",
// "outline",
// "filter",
// "filter_active",
// "form_footer",
// "disabled",
// "primary",
// ]),
// }),
// handleClose: PropTypes.func,
// stickyHeaderIndices: PropTypes.array,
// showsVerticalScrollIndicator: PropTypes.bool,
// bottomInset: PropTypes.number,
// bottomRerender: PropTypes.array,
// handleIndicatorStyle: PropTypes.object,
// backgroundStyle: PropTypes.object,
// headerParams: PropTypes.shape({
// title: PropTypes.string,
// }),
// keyboardBehavior: PropTypes.string,
// bottomSafeAreaHeight: PropTypes.bool,
// };
export default BottomSheet;
-----package.json----
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.22.15",
"@callstack/react-theme-provider": "^3.0.9",
"@fortawesome/fontawesome-svg-core": "^6.4.2",
"@fortawesome/free-regular-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@gorhom/bottom-sheet": "^5.1.2",
"@kichiyaki/react-native-barcode-generator": "^0.6.7",
"@likashefqet/react-native-image-zoom": "^4.1.0",
"@native-html/iframe-plugin": "^2.6.1",
"@notifee/react-native": "^9.1.8",
"@openspacelabs/react-native-zoomable-view": "^2.1.6",
"@react-native-assets/slider": "^7.2.1",
"@react-native-async-storage/async-storage": "^1.19.3",
"@react-native-camera-roll/camera-roll": "^7.9.0",
"@react-native-clipboard/clipboard": "^1.16.2",
"@react-native-community/image-editor": "^4.2.0",
"@react-native-community/netinfo": "^11.3.2",
"@react-native-community/push-notification-ios": "^1.11.0",
"@react-native-firebase/analytics": "^19.2.2",
"@react-native-firebase/app": "^19.2.2",
"@react-native-firebase/crashlytics": "^19.2.2",
"@react-native-firebase/messaging": "^19.2.2",
"@react-native-firebase/remote-config": "^19.2.2",
"@react-native-masked-view/masked-view": "^0.3.2",
"@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/native": "^6.1.18",
"@react-navigation/native-stack": "^6.x",
"@react-navigation/stack": "^6.4.1",
"@shopify/flash-list": "^1.7.5",
"axios": "^1.5.0",
"babel-plugin-inline-import": "^3.0.0",
"google-libphonenumber": "^3.2.40",
"i18next": "^23.5.1",
"mobx": "^6.10.2",
"mobx-persist": "^0.4.1",
"mobx-react-lite": "^4.0.4",
"numbro": "^2.4.0",
"patch-package": "^8.0.0",
"postinstall-postinstall": "^2.1.0",
"prop-types": "^15.8.1",
"react": "18.3.1",
"react-i18next": "^13.2.2",
"react-native": "0.77.1",
"react-native-animatable": "^1.3.3",
"react-native-animated-dots-carousel": "^1.0.2",
"react-native-applifecycle": "^1.0.0",
"react-native-avoid-softinput": "^7.0.1",
"react-native-awesome-slider": "^2.5.3",
"react-native-branch": "6.2.1",
"react-native-calendars": "^1.1303.0",
"react-native-collapsible": "^1.6.1",
"react-native-config": "^1.4.6",
"react-native-contacts": "^8.0.4",
"react-native-copilot": "^3.3.3",
"react-native-date-picker": "5.0.10",
"react-native-device-info": "^14.0.4",
"react-native-error-boundary": "^1.2.4",
"react-native-fast-image": "^8.6.3",
"react-native-fbsdk-next": "^13.0.0",
"react-native-fs": "2.16.1",
"react-native-geolocation-service": "^5.3.1",
"react-native-gesture-handler": "2.24.0",
"react-native-image-crop-picker": "^0.41.2",
"react-native-image-picker": "^7.1.0",
"react-native-image-viewing": "^0.2.2",
"react-native-insider": "^6.4.5-nh",
"react-native-keychain": "^8.1.3",
"react-native-linear-gradient": "^2.8.3",
"react-native-localize": "^3.3.0",
"react-native-maps": "1.14.0",
"react-native-network-logger": "^1.15.0",
"react-native-pager-view": "^6.7.0",
"react-native-permissions": "^3.9.2",
"react-native-popover-view": "^6.1.0",
"react-native-qrcode-svg": "^6.2.0",
"react-native-reanimated": "^3.17.1",
"react-native-reanimated-zoom": "^0.3.3",
"react-native-render-html": "^6.3.4",
"react-native-safe-area-context": "^5.3.0",
"react-native-screens": "4.9.2",
"react-native-shake": "^4.0.2",
"react-native-share": "^12.0.9",
"react-native-size-matters": "^0.4.0",
"react-native-skeleton-placeholder": "^5.2.4",
"react-native-sms": "^1.11.0",
"react-native-splash-screen": "^3.3.0",
"react-native-svg": "^15.11.2",
"react-native-svg-transformer": "^1.1.0",
"react-native-tab-view": "^4.0.8",
"react-native-toast-message": "^2.2.0",
"react-native-tracking-transparency": "^0.1.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-user-agent": "^2.3.1",
"react-native-video": "^6.11.0",
"react-native-video-duration": "^0.1.2",
"react-native-view-shot": "^3.8.0",
"react-native-vision-camera": "^4.6.4",
"react-native-webview": "^13.13.4",
"react-native-youtube-iframe": "^2.3.0",
"rn-fetch-blob": "^0.12.0",
"rn-placeholder": "^3.0.3",
"sanitize-html": "^2.13.0",
"socket.io-client": "^2.3.1",
"text-clipper": "^2.2.0",
"toggle-switch-react-native": "^3.3.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.16.5",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli": "15.0.1",
"@react-native-community/cli-platform-android": "15.0.1",
"@react-native-community/cli-platform-ios": "15.0.1",
"@react-native/babel-preset": "0.77.1",
"@react-native/eslint-config": "0.77.1",
"@react-native/metro-config": "0.77.1",
"@react-native/typescript-config": "0.77.1",
"@tsconfig/react-native": "^3.0.0",
"@types/jest": "^29.5.13",
"@types/react": "^18.2.6",
"@types/react-test-renderer": "^18.0.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^29.6.3",
"babel-plugin-module-resolver": "^5.0.0",
"eslint": "^7.32.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.3.0",
"eslint-config-sociolla": "git+ssh://git@bitbucket.org/Sociolla/eslint-config-sociolla.git",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-react-native": "^4.0.0",
"husky": "^8.0.0",
"jest": "^29.6.3",
"lint-staged": "^15.5.0",
"prettier": "2.4.1",
"react-native-flipper-performance-plugin": "^0.4.0",
"react-test-renderer": "18.3.1",
"reactotron-react-native": "^5.1.12",
"typescript": "5.0.4"
},
"engines": {
"node": ">=18"
},
Reproduction sample
NA
Relevant log output