├── README.md
├── assets
├── icon.png
├── splash.png
├── favicon.png
└── adaptive-icon.png
├── tsconfig.json
├── babel.config.js
├── .gitignore
├── package.json
├── app.json
├── utils
└── countries.ts
├── App.tsx
└── components
└── DropDown.tsx
/README.md:
--------------------------------------------------------------------------------
1 | # React-Native-Dropdown
2 |
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liptonzuma/React-Native-Dropdown/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liptonzuma/React-Native-Dropdown/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liptonzuma/React-Native-Dropdown/HEAD/assets/favicon.png
--------------------------------------------------------------------------------
/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liptonzuma/React-Native-Dropdown/HEAD/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "expo/tsconfig.base",
3 | "compilerOptions": {
4 | "strict": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2 |
3 | # dependencies
4 | node_modules/
5 |
6 | # Expo
7 | .expo/
8 | dist/
9 | web-build/
10 |
11 | # Native
12 | *.orig.*
13 | *.jks
14 | *.p8
15 | *.p12
16 | *.key
17 | *.mobileprovision
18 |
19 | # Metro
20 | .metro-health-check*
21 |
22 | # debug
23 | npm-debug.*
24 | yarn-debug.*
25 | yarn-error.*
26 |
27 | # macOS
28 | .DS_Store
29 | *.pem
30 |
31 | # local env files
32 | .env*.local
33 |
34 | # typescript
35 | *.tsbuildinfo
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dropdown",
3 | "version": "1.0.0",
4 | "main": "expo/AppEntry.js",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "web": "expo start --web"
10 | },
11 | "dependencies": {
12 | "expo": "~51.0.2",
13 | "expo-status-bar": "~1.12.1",
14 | "react": "18.2.0",
15 | "react-native": "0.74.1",
16 | "react-native-size-matters": "^0.4.2"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.20.0",
20 | "@types/react": "~18.2.45",
21 | "typescript": "^5.1.3"
22 | },
23 | "private": true
24 | }
25 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "dropdown",
4 | "slug": "dropdown",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "userInterfaceStyle": "light",
9 | "splash": {
10 | "image": "./assets/splash.png",
11 | "resizeMode": "contain",
12 | "backgroundColor": "#ffffff"
13 | },
14 | "ios": {
15 | "supportsTablet": true
16 | },
17 | "android": {
18 | "adaptiveIcon": {
19 | "foregroundImage": "./assets/adaptive-icon.png",
20 | "backgroundColor": "#ffffff"
21 | }
22 | },
23 | "web": {
24 | "favicon": "./assets/favicon.png"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/utils/countries.ts:
--------------------------------------------------------------------------------
1 | export const countries = [
2 | {
3 | label: "Ghana",
4 | value: "gh",
5 | flag: "🇬🇭",
6 | },
7 | {
8 | label: "Nigeria",
9 | value: "ng",
10 | flag: "🇳🇬",
11 | },
12 | {
13 | label: "Kenya",
14 | value: "ky",
15 | flag: "🇰🇪",
16 | },
17 | {
18 | label: "United States",
19 | value: "us",
20 | flag: "🇺🇸",
21 | },
22 | {
23 | label: "Pakistan",
24 | value: "pk",
25 | flag: "🇵🇰",
26 | },
27 | {
28 | label: "India",
29 | value: "in",
30 | flag: "🇮🇳",
31 | },
32 | {
33 | label: "Finland",
34 | value: "fn",
35 | flag: "🇫🇮",
36 | },
37 | {
38 | label: "France",
39 | value: "fr",
40 | flag: "🇫🇷",
41 | },
42 | ];
--------------------------------------------------------------------------------
/App.tsx:
--------------------------------------------------------------------------------
1 | import { StatusBar } from "expo-status-bar";
2 | import { StyleSheet, Text, View } from "react-native";
3 | import Dropdown from "./components/DropDown";
4 | import { countries } from "./utils/countries";
5 |
6 | const formattedCountries = countries.map((c) => ({
7 | value: c.label,
8 | label: `${c.flag} ${c.label}`,
9 | }));
10 |
11 | export default function App() {
12 | return (
13 |
14 |
15 |
20 |
21 |
30 |
31 | );
32 | }
33 |
34 | const styles = StyleSheet.create({
35 | container: {
36 | flex: 1,
37 | backgroundColor: "#ddd",
38 | alignItems: "center",
39 | justifyContent: "center",
40 | paddingHorizontal: 20,
41 | gap: 10,
42 | },
43 | });
44 |
--------------------------------------------------------------------------------
/components/DropDown.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | View,
3 | Text,
4 | TouchableOpacity,
5 | StyleSheet,
6 | FlatList,
7 | Modal,
8 | TouchableWithoutFeedback,
9 | Platform,
10 | } from "react-native";
11 | import React, { useCallback, useRef, useState } from "react";
12 | import { AntDesign } from "@expo/vector-icons";
13 |
14 | type OptionItem = {
15 | value: string;
16 | label: string;
17 | };
18 |
19 | interface DropDownProps {
20 | data: OptionItem[];
21 | onChange: (item: OptionItem) => void;
22 | placeholder: string;
23 | }
24 |
25 | export default function Dropdown({
26 | data,
27 | onChange,
28 | placeholder,
29 | }: DropDownProps) {
30 | const [expanded, setExpanded] = useState(false);
31 |
32 | const toggleExpanded = useCallback(() => setExpanded(!expanded), [expanded]);
33 |
34 | const [value, setValue] = useState("");
35 |
36 | const buttonRef = useRef(null);
37 |
38 | const [top, setTop] = useState(0);
39 |
40 | const onSelect = useCallback((item: OptionItem) => {
41 | onChange(item);
42 | setValue(item.label);
43 | setExpanded(false);
44 | }, []);
45 | return (
46 | {
49 | const layout = event.nativeEvent.layout;
50 | const topOffset = layout.y;
51 | const heightOfComponent = layout.height;
52 |
53 | const finalValue =
54 | topOffset + heightOfComponent + (Platform.OS === "android" ? -32 : 3);
55 |
56 | setTop(finalValue);
57 | }}
58 | >
59 |
64 | {value || placeholder}
65 |
66 |
67 | {expanded ? (
68 |
69 | setExpanded(false)}>
70 |
71 |
79 | item.value}
81 | data={data}
82 | renderItem={({ item }) => (
83 | onSelect(item)}
87 | >
88 | {item.label}
89 |
90 | )}
91 | ItemSeparatorComponent={() => (
92 |
93 | )}
94 | />
95 |
96 |
97 |
98 |
99 | ) : null}
100 |
101 | );
102 | }
103 |
104 | const styles = StyleSheet.create({
105 | backdrop: {
106 | padding: 20,
107 | justifyContent: "center",
108 | alignItems: "center",
109 | flex: 1,
110 | },
111 | optionItem: {
112 | height: 40,
113 | justifyContent: "center",
114 | },
115 | separator: {
116 | height: 4,
117 | },
118 | options: {
119 | position: "absolute",
120 | // top: 53,
121 | backgroundColor: "white",
122 | width: "100%",
123 | padding: 10,
124 | borderRadius: 6,
125 | maxHeight: 250,
126 | },
127 | text: {
128 | fontSize: 15,
129 | opacity: 0.8,
130 | },
131 | button: {
132 | height: 50,
133 | justifyContent: "space-between",
134 | backgroundColor: "#fff",
135 | flexDirection: "row",
136 | width: "100%",
137 | alignItems: "center",
138 | paddingHorizontal: 15,
139 | borderRadius: 8,
140 | },
141 | });
142 |
--------------------------------------------------------------------------------