expodropdownreact-native-flatlist

FlatList not scrolling on android - react native


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


Solution

  • 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",
      },
    });