I'm trying to create a custom drop down, on IOS its working fine, but on ANDROID can't scroll. Anyone worked with custom drop down before?
I followed every answer to fix this but nothing helped me. There is no drop down package can help me to use it instead of creating a custom one.
const CustomDropDown: React.FC<DropDownProps> = forwardRef(
(
{ label, data, value, setValue, margin, placeholder },
ref: ForwardedRef<TextInput>
) => {
const [isFocused, setIsFocused] = useState<boolean>(false);
const [show, setShow] = useState<boolean>(false);
const { locale } = useContext(I18nContext);
const openPicker = useCallback(() => {
Keyboard.dismiss();
setIsFocused(!isFocused);
setShow(!show);
}, [show]);
const hidePicker = useCallback(
(item: any) => {
setIsFocused(false);
setShow(false);
setValue(item);
},
[show, value]
);
const getListItemStyles = useCallback(
(item: string, index: number): any => {
if (item === value) {
if (index === 0) {
return {
borderTopRightRadius: 15,
borderTopLeftRadius: 15,
backgroundColor: colors.darkBlue,
};
} else if (index === data?.length - 1) {
return {
borderBottomRightRadius: 15,
borderBottomLeftRadius: 15,
backgroundColor: colors.darkBlue,
};
} else {
return {
backgroundColor: colors.darkBlue,
};
}
}
return {}; // Return an empty object if no special styles are needed
},
[value]
);
const ListItem: React.FC<ListItemProps> = memo(
({ item, index, value, hidePicker, getListItemStyles }) => (
<TouchableOpacity
onPress={() => hidePicker(item)}
style={[styles.listItem, getListItemStyles(item, index)]}
>
<TextResponsive
style={[
styles.listItemText,
item === value && { color: colors.white },
]}
>
{item}
</TextResponsive>
</TouchableOpacity>
)
);
const renderItem = useCallback(
({ item, index }: { item: string; index: number }) => (
<ListItem
item={item}
index={index}
value={value}
hidePicker={hidePicker}
getListItemStyles={getListItemStyles}
/>
),
[value, hidePicker, getListItemStyles]
);
return (
<View
style={[{ flex: 1 }, margin && { marginLeft: 10 }]}
className="mb-4"
>
<TextResponsive style={[styles.label, { fontFamily: fonts.bold }]}>
{label}
</TextResponsive>
<TouchableWithoutFeedback
onPress={openPicker}
disabled={data?.length === 0}
>
<View style={[styles.inputWrapper, isFocused && styles.inputFocused]}>
<TextInput
style={[
styles.input,
isFocused && { color: colors.darkBlue },
{ textAlign: locale.includes("ar") ? "right" : "left" },
]}
value={value}
placeholderTextColor={colors.grey}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
editable={false}
placeholder={placeholder}
/>
<View style={styles.icon}>
<AntDesign
name="caretdown"
size={12}
color={isFocused ? colors.darkBlue : colors.grey}
/>
</View>
</View>
</TouchableWithoutFeedback>
{show && (
<View style={styles.listContainer}>
<FlatList
style={styles.list}
data={data}
renderItem={renderItem}
keyExtractor={(item) => item}
showsVerticalScrollIndicator={false}
/>
</View>
)}
</View>
);
}
);
export default CustomDropDown;
const styles = StyleSheet.create({
inputWrapper: {
width: "100%",
backgroundColor: colors.white,
borderWidth: 1,
borderColor: colors.grey,
borderRadius: 7,
marginTop: 5,
},
input: {
paddingVertical: 15,
paddingHorizontal: 10,
color: colors.grey,
},
inputFocused: {
borderColor: colors.darkBlue,
elevation: 1,
shadowColor: colors.lightGrey,
shadowOpacity: 10,
shadowOffset: { width: 1, height: 1 },
overflow: Platform.OS === "android" ? "hidden" : "visible",
},
label: {
marginBottom: 5,
fontSize: 16,
},
list: {
backgroundColor: "white",
elevation: 5,
zIndex: 220,
width: "100%",
position: "absolute",
borderRadius: 15,
marginTop: 10,
height: 200,
},
listItem: {
padding: 10,
},
listItemText: {
fontSize: 16,
},
icon: {
position: "absolute",
right: 10,
top: 20,
},
listContainer: {},
});
I tried to addcontentContainerStyle={{ flexGrow: 1 }}
but didn't solve the problem
I fixed it by doing this, without using any package
import {
FlatList,
Keyboard,
Platform,
StyleSheet,
TextInput,
TouchableOpacity,
TouchableWithoutFeedback,
View,
Modal,
} from "react-native";
import React, {
ForwardedRef,
forwardRef,
useCallback,
useContext,
useState,
memo,
useRef,
} from "react";
import { colors, fonts } from "@/reusableTools/css/css";
import { AntDesign } from "@expo/vector-icons";
import TextResponsive from "./TextResponsive";
import { I18nContext } from "@/Context/I18nContext";
interface DropDownProps {
label: string;
data: string[];
value: string;
setValue: (text: string) => void;
margin?: boolean;
placeholder?: string;
}
interface ListItemProps {
item: string;
index: number;
value: string;
hidePicker: (item: string) => void;
getListItemStyles: (item: string, index: number) => any;
}
const CustomDropDown: React.FC<DropDownProps> = forwardRef(
(
{ label, data, value, setValue, margin, placeholder },
ref: ForwardedRef<TextInput>
) => {
const [isFocused, setIsFocused] = useState<boolean>(false);
const [visible, setVisible] = useState<boolean>(false);
const { locale } = useContext(I18nContext);
const DropdownButton = useRef<any>();
const [dropdownTop, setDropdownTop] = useState(0);
const [dropdownWidth, setDropdownWidth] = useState(0);
const [dropdownLeft, setDropdownLeft] = useState(0);
const openPicker = useCallback(() => {
visible ? setVisible(false) : openDropdown();
Keyboard.dismiss();
setIsFocused(!isFocused);
}, [visible]);
const hidePicker = useCallback(
(item: any) => {
setVisible(false);
setIsFocused(false);
setValue(item);
},
[value, visible]
);
const getListItemStyles = useCallback(
(item: string, index: number): any => {
if (item === value) {
if (index === 0) {
return {
borderTopRightRadius: 15,
borderTopLeftRadius: 15,
backgroundColor: colors.darkBlue,
};
} else if (index === data?.length - 1) {
return {
borderBottomRightRadius: 15,
borderBottomLeftRadius: 15,
backgroundColor: colors.darkBlue,
};
} else {
return {
backgroundColor: colors.darkBlue,
};
}
}
return {}; // Return an empty object if no special styles are needed
},
[value]
);
const ListItem: React.FC<ListItemProps> = memo(
({ item, index, value, hidePicker, getListItemStyles }) => (
<TouchableOpacity
onPress={() => hidePicker(item)}
style={[styles.listItem, getListItemStyles(item, index)]}
>
<TextResponsive
style={[
styles.listItemText,
item === value && { color: colors.white },
]}
>
{item}
</TextResponsive>
</TouchableOpacity>
)
);
const renderItem = useCallback(
({ item, index }: { item: string; index: number }) => (
<ListItem
item={item}
index={index}
value={value}
hidePicker={hidePicker}
getListItemStyles={getListItemStyles}
/>
),
[value, hidePicker, getListItemStyles]
);
const openDropdown = (): void => {
DropdownButton.current.measure(
(_fx: any, _fy: any, _w: any, h: any, _px: any, py: any) => {
setDropdownTop(py + 4);
setDropdownWidth(_w);
setDropdownLeft(_px);
}
);
setVisible(true);
};
return (
<View
style={[{ flex: 1 }, margin && { marginLeft: 10 }]}
className="mb-4"
>
<TextResponsive style={[styles.label, { fontFamily: fonts.bold }]}>
{label}
</TextResponsive>
<TouchableOpacity onPress={openPicker} disabled={data?.length === 0}>
<View
style={[styles.inputWrapper, isFocused && styles.inputFocused]}
ref={DropdownButton}
>
<TextInput
style={[
styles.input,
isFocused && { color: colors.darkBlue },
{ textAlign: locale.includes("ar") ? "right" : "left" },
]}
value={value}
placeholderTextColor={colors.grey}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
editable={false}
placeholder={placeholder}
/>
<View style={styles.icon}>
<AntDesign
name="caretdown"
size={12}
color={isFocused ? colors.darkBlue : colors.grey}
/>
</View>
</View>
</TouchableOpacity>
<Modal visible={visible} transparent animationType="fade">
<TouchableOpacity
style={styles.overlay}
onPress={() => {
setVisible(false);
setIsFocused(false);
}}
>
<View
style={[
{
top:
Platform.OS === "android" ? dropdownTop : dropdownTop + 40,
left: dropdownLeft,
width: dropdownWidth,
},
Platform.OS === "ios" && styles.iosShadow,
]}
>
<FlatList
style={styles.list}
data={data}
renderItem={renderItem}
keyExtractor={(item) => item}
showsVerticalScrollIndicator={false}
/>
</View>
</TouchableOpacity>
</Modal>
</View>
);
}
);
export default CustomDropDown;
const styles = StyleSheet.create({
inputWrapper: {
width: "100%",
backgroundColor: colors.white,
borderWidth: 1,
borderColor: colors.grey,
borderRadius: 7,
marginTop: 5,
flexDirection: "row",
alignItems: "center",
},
input: {
color: colors.grey,
paddingHorizontal: 10,
marginVertical: 15,
},
inputFocused: {
borderColor: colors.darkBlue,
elevation: 1,
shadowColor: colors.lightGrey,
shadowOpacity: 10,
shadowOffset: { width: 1, height: 1 },
overflow: Platform.OS === "android" ? "hidden" : "visible",
},
label: {
marginBottom: 5,
fontSize: 16,
},
list: {
backgroundColor: "white",
elevation: 5,
borderRadius: 15,
marginTop: 10,
height: 200,
},
listItem: {
padding: 10,
},
listItemText: {
fontSize: 16,
},
icon: {
position: "absolute",
right: 10,
},
overlay: {
flex: 1,
},
iosShadow: {
shadowColor: "#393b3a",
shadowOpacity: 1,
shadowOffset: { width: 1, height: 1 },
overflow: Platform.OS === "android" ? "hidden" : "visible",
},
});