├── .gitignore ├── app.json ├── app ├── (tabs) │ ├── _layout.tsx │ ├── bookmarks.tsx │ ├── category.tsx │ ├── index.tsx │ ├── profile.tsx │ └── search.tsx ├── _layout.tsx └── listing │ └── [id].tsx ├── assets ├── fonts │ └── SpaceMono-Regular.ttf └── images │ ├── adaptive-icon.png │ ├── favicon.png │ ├── icon.png │ └── splash.png ├── babel.config.js ├── components ├── CategoryButtons.tsx ├── GroupListings.tsx └── Listings.tsx ├── constants └── Colors.ts ├── data ├── categories.ts ├── destinations.json └── groups.json ├── package-lock.json ├── package.json ├── tsconfig.json └── types ├── groupType.ts └── listingType.ts /.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 | 37 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 38 | # The following patterns were generated by expo-cli 39 | 40 | expo-env.d.ts 41 | # @end expo-cli -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "expo-travel-app", 4 | "slug": "expo-travel-app", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "splash": { 11 | "image": "./assets/images/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "assetBundlePatterns": [ 16 | "**/*" 17 | ], 18 | "ios": { 19 | "supportsTablet": true 20 | }, 21 | "android": { 22 | "adaptiveIcon": { 23 | "foregroundImage": "./assets/images/adaptive-icon.png", 24 | "backgroundColor": "#ffffff" 25 | } 26 | }, 27 | "web": { 28 | "bundler": "metro", 29 | "output": "static", 30 | "favicon": "./assets/images/favicon.png" 31 | }, 32 | "plugins": [ 33 | "expo-router" 34 | ], 35 | "experiments": { 36 | "typedRoutes": true 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/(tabs)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text } from "react-native"; 2 | import React from "react"; 3 | import { Tabs } from "expo-router"; 4 | import { FontAwesome, Ionicons, MaterialIcons } from "@expo/vector-icons"; 5 | import Colors from "@/constants/Colors"; 6 | 7 | export default function Layout() { 8 | return ( 9 | 21 | ( 25 | 26 | ), 27 | }} 28 | /> 29 | ( 33 | 34 | ), 35 | }} 36 | /> 37 | ( 41 | 50 | 51 | 52 | ), 53 | }} 54 | /> 55 | ( 59 | 60 | ), 61 | }} 62 | /> 63 | ( 67 | 68 | ), 69 | }} 70 | /> 71 | 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /app/(tabs)/bookmarks.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from 'react-native' 2 | import React from 'react' 3 | 4 | const Page = () => { 5 | return ( 6 | 7 | Bookmarks 8 | 9 | ) 10 | } 11 | 12 | export default Page 13 | 14 | const styles = StyleSheet.create({ 15 | container: { 16 | flex:1, 17 | justifyContent:'center', 18 | alignItems:'center', 19 | } 20 | }) -------------------------------------------------------------------------------- /app/(tabs)/category.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from 'react-native' 2 | import React from 'react' 3 | 4 | const Page = () => { 5 | return ( 6 | 7 | Category 8 | 9 | ) 10 | } 11 | 12 | export default Page 13 | 14 | const styles = StyleSheet.create({ 15 | container: { 16 | flex:1, 17 | justifyContent:'center', 18 | alignItems:'center', 19 | } 20 | }) -------------------------------------------------------------------------------- /app/(tabs)/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Image, 3 | ScrollView, 4 | StyleSheet, 5 | Text, 6 | TextInput, 7 | TouchableOpacity, 8 | View, 9 | } from "react-native"; 10 | import React, { useState } from "react"; 11 | import { Stack } from "expo-router"; 12 | import { Ionicons } from "@expo/vector-icons"; 13 | import Colors from "@/constants/Colors"; 14 | import { useHeaderHeight } from "@react-navigation/elements"; 15 | import CategoryButtons from "@/components/CategoryButtons"; 16 | import Listings from "@/components/Listings"; 17 | import listingData from "@/data/destinations.json"; 18 | import GroupListings from "@/components/GroupListings"; 19 | import groupData from "@/data/groups.json"; 20 | 21 | const Page = () => { 22 | const headerHeight = useHeaderHeight(); 23 | const [category, setCategory] = useState("All"); 24 | 25 | const onCatChanged = (category: string) => { 26 | console.log("Categpry: ", category); 27 | setCategory(category); 28 | }; 29 | 30 | return ( 31 | <> 32 | ( 37 | {}} style={{ marginLeft: 20 }}> 38 | 44 | 45 | ), 46 | headerRight: () => ( 47 | {}} 49 | style={{ 50 | marginRight: 20, 51 | backgroundColor: Colors.white, 52 | padding: 10, 53 | borderRadius: 10, 54 | shadowColor: "#171717", 55 | shadowOffset: { width: 2, height: 4 }, 56 | shadowOpacity: 0.2, 57 | shadowRadius: 3, 58 | }} 59 | > 60 | 61 | 62 | ), 63 | }} 64 | /> 65 | 66 | 67 | Explore The Beautiful World! 68 | 69 | 70 | 71 | 77 | 78 | 79 | {}} style={styles.filterBtn}> 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | ); 93 | }; 94 | 95 | export default Page; 96 | 97 | const styles = StyleSheet.create({ 98 | container: { 99 | flex: 1, 100 | paddingHorizontal: 20, 101 | backgroundColor: Colors.bgColor, 102 | }, 103 | headingTxt: { 104 | fontSize: 28, 105 | fontWeight: "800", 106 | color: Colors.black, 107 | marginTop: 10, 108 | }, 109 | searchSectionWrapper: { 110 | flexDirection: "row", 111 | marginVertical: 20, 112 | }, 113 | searchBar: { 114 | flex: 1, 115 | flexDirection: "row", 116 | backgroundColor: Colors.white, 117 | padding: 16, 118 | borderRadius: 10, 119 | }, 120 | filterBtn: { 121 | backgroundColor: Colors.primaryColor, 122 | padding: 12, 123 | borderRadius: 10, 124 | marginLeft: 20, 125 | }, 126 | }); 127 | -------------------------------------------------------------------------------- /app/(tabs)/profile.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from 'react-native' 2 | import React from 'react' 3 | 4 | const Page = () => { 5 | return ( 6 | 7 | Profile 8 | 9 | ) 10 | } 11 | 12 | export default Page 13 | 14 | const styles = StyleSheet.create({ 15 | container: { 16 | flex:1, 17 | justifyContent:'center', 18 | alignItems:'center', 19 | } 20 | }) -------------------------------------------------------------------------------- /app/(tabs)/search.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Text, View } from 'react-native' 2 | import React from 'react' 3 | 4 | const Page = () => { 5 | return ( 6 | 7 | Search 8 | 9 | ) 10 | } 11 | 12 | export default Page 13 | 14 | const styles = StyleSheet.create({ 15 | container: { 16 | flex:1, 17 | justifyContent:'center', 18 | alignItems:'center', 19 | } 20 | }) -------------------------------------------------------------------------------- /app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import FontAwesome from "@expo/vector-icons/FontAwesome"; 2 | import { useFonts } from "expo-font"; 3 | import { Stack } from "expo-router"; 4 | import * as SplashScreen from "expo-splash-screen"; 5 | import { useEffect } from "react"; 6 | 7 | export { 8 | // Catch any errors thrown by the Layout component. 9 | ErrorBoundary, 10 | } from "expo-router"; 11 | 12 | export const unstable_settings = { 13 | // Ensure that reloading on `/modal` keeps a back button present. 14 | initialRouteName: "(tabs)", 15 | }; 16 | 17 | // Prevent the splash screen from auto-hiding before asset loading is complete. 18 | SplashScreen.preventAutoHideAsync(); 19 | 20 | export default function RootLayout() { 21 | const [loaded, error] = useFonts({ 22 | SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"), 23 | ...FontAwesome.font, 24 | }); 25 | 26 | // Expo Router uses Error Boundaries to catch errors in the navigation tree. 27 | useEffect(() => { 28 | if (error) throw error; 29 | }, [error]); 30 | 31 | useEffect(() => { 32 | if (loaded) { 33 | SplashScreen.hideAsync(); 34 | } 35 | }, [loaded]); 36 | 37 | if (!loaded) { 38 | return null; 39 | } 40 | 41 | return ; 42 | } 43 | 44 | function RootLayoutNav() { 45 | return ( 46 | 47 | 48 | 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /app/listing/[id].tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Dimensions, 3 | Image, 4 | ScrollView, 5 | StyleSheet, 6 | Text, 7 | TouchableOpacity, 8 | View, 9 | } from "react-native"; 10 | import React from "react"; 11 | import { Stack, useLocalSearchParams, useRouter } from "expo-router"; 12 | import { ListingType } from "@/types/listingType"; 13 | import listingData from "@/data/destinations.json"; 14 | import { 15 | Feather, 16 | FontAwesome, 17 | FontAwesome5, 18 | Ionicons, 19 | } from "@expo/vector-icons"; 20 | import Colors from "@/constants/Colors"; 21 | import Animated, { 22 | SlideInDown, 23 | interpolate, 24 | useAnimatedRef, 25 | useAnimatedStyle, 26 | useScrollViewOffset, 27 | } from "react-native-reanimated"; 28 | 29 | const { width } = Dimensions.get("window"); 30 | const IMG_HEIGHT = 300; 31 | 32 | const ListingDetails = () => { 33 | const { id } = useLocalSearchParams(); 34 | const listing: ListingType = (listingData as ListingType[]).find( 35 | (item) => item.id === id 36 | ); 37 | 38 | const router = useRouter(); 39 | 40 | const scrollRef = useAnimatedRef(); 41 | const scrollOffset = useScrollViewOffset(scrollRef); 42 | const imageAnimatedStyle = useAnimatedStyle(() => { 43 | return { 44 | transform: [ 45 | { 46 | translateY: interpolate( 47 | scrollOffset.value, 48 | [-IMG_HEIGHT, 0, IMG_HEIGHT], 49 | [-IMG_HEIGHT / 2, 0, IMG_HEIGHT * 0.75] 50 | ), 51 | }, 52 | { 53 | scale: interpolate( 54 | scrollOffset.value, 55 | [-IMG_HEIGHT, 0, IMG_HEIGHT], 56 | [2, 1, 1] 57 | ), 58 | }, 59 | ], 60 | }; 61 | }); 62 | 63 | return ( 64 | <> 65 | ( 70 | router.back()} 72 | style={{ 73 | backgroundColor: "rgba(255, 255, 255, 0.5)", 74 | borderRadius: 10, 75 | padding: 4, 76 | }} 77 | > 78 | 85 | 86 | 87 | 88 | ), 89 | headerRight: () => ( 90 | {}} 92 | style={{ 93 | backgroundColor: "rgba(255, 255, 255, 0.5)", 94 | borderRadius: 10, 95 | padding: 4, 96 | }} 97 | > 98 | 105 | 106 | 107 | 108 | ), 109 | }} 110 | /> 111 | 112 | 116 | 120 | 121 | {listing.name} 122 | 123 | 128 | {listing.location} 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | Duration 138 | 139 | {listing.duration} Days 140 | 141 | 142 | 143 | 144 | 145 | 150 | 151 | 152 | Person 153 | {listing.duration} 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | Rating 162 | 163 | {listing.rating} 164 | 165 | 166 | 167 | 168 | 169 | {listing.description} 170 | 171 | 172 | 173 | 174 | 175 | {}} 177 | style={[styles.footerBtn, styles.footerBookBtn]} 178 | > 179 | Book Now 180 | 181 | {}} style={styles.footerBtn}> 182 | ${listing.price} 183 | 184 | 185 | 186 | ); 187 | }; 188 | 189 | export default ListingDetails; 190 | 191 | const styles = StyleSheet.create({ 192 | container: { 193 | flex: 1, 194 | backgroundColor: Colors.white, 195 | }, 196 | image: { 197 | width: width, 198 | height: IMG_HEIGHT, 199 | }, 200 | contentWrapper: { 201 | padding: 20, 202 | backgroundColor: Colors.white, 203 | }, 204 | listingName: { 205 | fontSize: 24, 206 | fontWeight: "500", 207 | color: Colors.black, 208 | letterSpacing: 0.5, 209 | }, 210 | listingLocationWrapper: { 211 | flexDirection: "row", 212 | marginTop: 5, 213 | marginBottom: 10, 214 | alignItems: "center", 215 | }, 216 | listingLocationTxt: { 217 | fontSize: 14, 218 | marginLeft: 5, 219 | color: Colors.black, 220 | }, 221 | highlightWrapper: { 222 | flexDirection: "row", 223 | marginVertical: 20, 224 | justifyContent: "space-between", 225 | }, 226 | highlightIcon: { 227 | backgroundColor: "#F4F4F4", 228 | paddingHorizontal: 8, 229 | paddingVertical: 5, 230 | borderRadius: 8, 231 | marginRight: 5, 232 | alignItems: "center", 233 | }, 234 | highlightTxt: { 235 | fontSize: 12, 236 | color: "#999", 237 | }, 238 | highlightTxtVal: { 239 | fontSize: 14, 240 | fontWeight: "600", 241 | }, 242 | listingDetails: { 243 | fontSize: 16, 244 | color: Colors.black, 245 | lineHeight: 25, 246 | letterSpacing: 0.5, 247 | }, 248 | footer: { 249 | flexDirection: "row", 250 | position: "absolute", 251 | bottom: 0, 252 | padding: 20, 253 | paddingBottom: 30, 254 | width: width, 255 | }, 256 | footerBtn: { 257 | flex: 1, 258 | backgroundColor: Colors.black, 259 | padding: 20, 260 | borderRadius: 10, 261 | alignItems: "center", 262 | }, 263 | footerBookBtn: { 264 | flex: 2, 265 | backgroundColor: Colors.primaryColor, 266 | marginRight: 20, 267 | }, 268 | footerBtnTxt: { 269 | color: Colors.white, 270 | fontSize: 16, 271 | fontWeight: "600", 272 | textTransform: "uppercase", 273 | }, 274 | }); 275 | -------------------------------------------------------------------------------- /assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itzpradip/travel-app-ui-expo-router/05b99bde8e583be8a14a089a9c6b205fcf365747/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itzpradip/travel-app-ui-expo-router/05b99bde8e583be8a14a089a9c6b205fcf365747/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itzpradip/travel-app-ui-expo-router/05b99bde8e583be8a14a089a9c6b205fcf365747/assets/images/favicon.png -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itzpradip/travel-app-ui-expo-router/05b99bde8e583be8a14a089a9c6b205fcf365747/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itzpradip/travel-app-ui-expo-router/05b99bde8e583be8a14a089a9c6b205fcf365747/assets/images/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ["babel-preset-expo"], 5 | plugins: ["react-native-reanimated/plugin"], 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /components/CategoryButtons.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ScrollView, 3 | StyleSheet, 4 | Text, 5 | TouchableOpacity, 6 | View, 7 | } from "react-native"; 8 | import React, { useRef, useState } from "react"; 9 | import Colors from "@/constants/Colors"; 10 | import destinationCategories from "@/data/categories"; 11 | import { MaterialCommunityIcons } from "@expo/vector-icons"; 12 | 13 | type Props = { 14 | onCagtegoryChanged: (category: string) => void; 15 | } 16 | 17 | const CategoryButtons = ({onCagtegoryChanged}: Props) => { 18 | const scrollRef = useRef(null); 19 | const itemRef = useRef([]); 20 | const [activeIndex, setActiveIndex] = useState(0); 21 | 22 | const handleSelectCategory = (index: number) => { 23 | const selected = itemRef.current[index]; 24 | setActiveIndex(index); 25 | 26 | selected?.measure((x) => { 27 | scrollRef.current?.scrollTo({ x: x, y: 0, animated: true }); 28 | }); 29 | 30 | onCagtegoryChanged(destinationCategories[index].title); 31 | }; 32 | 33 | return ( 34 | 35 | Categories 36 | 46 | {destinationCategories.map((item, index) => ( 47 | itemRef.current[index] = el} 50 | onPress={() => handleSelectCategory(index)} 51 | style={ 52 | activeIndex === index 53 | ? styles.categoryBtnActive 54 | : styles.categoryBtn 55 | } 56 | > 57 | 62 | 69 | {item.title} 70 | 71 | 72 | ))} 73 | 74 | 75 | ); 76 | }; 77 | 78 | export default CategoryButtons; 79 | 80 | const styles = StyleSheet.create({ 81 | title: { 82 | fontSize: 22, 83 | fontWeight: "700", 84 | color: Colors.black, 85 | }, 86 | categoryBtn: { 87 | flexDirection: "row", 88 | alignItems: "center", 89 | backgroundColor: Colors.white, 90 | paddingHorizontal: 16, 91 | paddingVertical: 10, 92 | borderRadius: 10, 93 | shadowColor: "#333333", 94 | shadowOffset: { width: 1, height: 2 }, 95 | shadowOpacity: 0.1, 96 | shadowRadius: 3, 97 | }, 98 | categoryBtnActive: { 99 | flexDirection: "row", 100 | alignItems: "center", 101 | backgroundColor: Colors.primaryColor, 102 | paddingHorizontal: 16, 103 | paddingVertical: 10, 104 | borderRadius: 10, 105 | shadowColor: "#333333", 106 | shadowOffset: { width: 1, height: 2 }, 107 | shadowOpacity: 0.1, 108 | shadowRadius: 3, 109 | }, 110 | categoryBtnTxt: { 111 | marginLeft: 5, 112 | color: Colors.black, 113 | }, 114 | categoryBtnTxtActive: { 115 | marginLeft: 5, 116 | color: Colors.white, 117 | }, 118 | }); 119 | -------------------------------------------------------------------------------- /components/GroupListings.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | FlatList, 3 | Image, 4 | ListRenderItem, 5 | StyleSheet, 6 | Text, 7 | View, 8 | } from "react-native"; 9 | import React from "react"; 10 | import { GroupType } from "@/types/groupType"; 11 | import Colors from "@/constants/Colors"; 12 | import { Ionicons } from "@expo/vector-icons"; 13 | 14 | const GroupListings = ({ listings }: { listings: GroupType[] }) => { 15 | const renderItem: ListRenderItem = ({ item }) => { 16 | return ( 17 | 18 | 19 | 20 | {item.name} 21 | 22 | 23 | {item.rating} 24 | ({item.reviews}) 25 | 26 | 27 | 28 | ); 29 | }; 30 | 31 | return ( 32 | 33 | Top Travel Groups 34 | 40 | 41 | ); 42 | }; 43 | 44 | export default GroupListings; 45 | 46 | const styles = StyleSheet.create({ 47 | title: { 48 | fontSize: 22, 49 | fontWeight: "600", 50 | color: Colors.black, 51 | marginBottom: 10, 52 | }, 53 | item: { 54 | backgroundColor: Colors.white, 55 | padding: 10, 56 | borderRadius: 10, 57 | marginRight: 20, 58 | flexDirection: "row", 59 | alignItems: "center", 60 | }, 61 | image: { 62 | width: 80, 63 | height: 100, 64 | borderRadius: 10, 65 | marginRight: 10, 66 | }, 67 | itemTxt: { 68 | fontSize: 14, 69 | fontWeight: "600", 70 | color: Colors.black, 71 | marginBottom: 8, 72 | }, 73 | itemRating: { 74 | fontSize: 14, 75 | fontWeight: '600', 76 | color: Colors.black, 77 | marginLeft: 5, 78 | }, 79 | itemReviews: { 80 | fontSize: 14, 81 | color: '#999' 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /components/Listings.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | FlatList, 3 | Image, 4 | ListRenderItem, 5 | StyleSheet, 6 | Text, 7 | TouchableOpacity, 8 | View, 9 | } from "react-native"; 10 | import React, { useEffect, useState } from "react"; 11 | import { ListingType } from "@/types/listingType"; 12 | import Colors from "@/constants/Colors"; 13 | import { FontAwesome5, Ionicons } from "@expo/vector-icons"; 14 | import { Link } from "expo-router"; 15 | 16 | type Props = { 17 | listings: any[]; 18 | category: string; 19 | }; 20 | 21 | const Listings = ({ listings, category }: Props) => { 22 | const [loading, setLoading] = useState(false); 23 | 24 | useEffect(() => { 25 | console.log('Update Listing'); 26 | setLoading(true); 27 | 28 | setTimeout(() => { 29 | setLoading(false) 30 | }, 200); 31 | }, [category]); 32 | 33 | const renderItems: ListRenderItem = ({ item }) => { 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | {item.name} 48 | 49 | 52 | 53 | 58 | {item.location} 59 | 60 | ${item.price} 61 | 62 | 63 | 64 | 65 | ); 66 | }; 67 | 68 | return ( 69 | 70 | 76 | 77 | ); 78 | }; 79 | 80 | export default Listings; 81 | 82 | const styles = StyleSheet.create({ 83 | item: { 84 | backgroundColor: Colors.white, 85 | padding: 10, 86 | borderRadius: 10, 87 | marginRight: 20, 88 | width: 220, 89 | }, 90 | image: { 91 | width: 200, 92 | height: 200, 93 | borderRadius: 10, 94 | marginBottom: 30, 95 | }, 96 | bookmark: { 97 | position: "absolute", 98 | top: 185, 99 | right: 30, 100 | backgroundColor: Colors.primaryColor, 101 | padding: 10, 102 | borderRadius: 30, 103 | borderWidth: 2, 104 | borderColor: Colors.white, 105 | }, 106 | itemTxt: { 107 | fontSize: 16, 108 | fontWeight: "600", 109 | color: Colors.black, 110 | marginBottom: 10, 111 | }, 112 | itemLocationTxt: { 113 | fontSize: 12, 114 | marginLeft: 5, 115 | }, 116 | itemPriceTxt: { 117 | fontSize: 12, 118 | fontWeight: "600", 119 | color: Colors.primaryColor, 120 | }, 121 | }); 122 | -------------------------------------------------------------------------------- /constants/Colors.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | primaryColor: '#ff7f36', 3 | bgColor: '#F4F4F4', 4 | black: '#27283a', 5 | white: '#FFFFFF', 6 | } -------------------------------------------------------------------------------- /data/categories.ts: -------------------------------------------------------------------------------- 1 | const destinationCategories = [ 2 | { 3 | title: "All", 4 | iconName: "hiking" 5 | }, 6 | { 7 | title: "Beaches", 8 | iconName: "beach" 9 | }, 10 | { 11 | title: "Mountains", 12 | iconName: "terrain" 13 | }, 14 | { 15 | title: "Cities", 16 | iconName: "city" 17 | }, 18 | { 19 | title: "Forests", 20 | iconName: "tree" 21 | }, 22 | { 23 | title: "Lakes", 24 | iconName: "swim" 25 | }, 26 | { 27 | title: "Historical Sites", 28 | iconName: "castle" 29 | }, 30 | { 31 | title: "National Parks", 32 | iconName: "pine-tree" 33 | }, 34 | { 35 | title: "Islands", 36 | iconName: "island" 37 | }, 38 | { 39 | title: "Deserts", 40 | iconName: "weather-sunny" 41 | } 42 | ]; 43 | 44 | export default destinationCategories; 45 | -------------------------------------------------------------------------------- /data/destinations.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name": "Tropical Paradise Resort", 5 | "image": "https://images.unsplash.com/photo-1540541338287-41700207dee6?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 6 | "description": "Escape to the ultimate tropical paradise retreat with pristine beaches, lush greenery, and luxurious accommodations.", 7 | "rating": 4.8, 8 | "price": "150", 9 | "duration": "3", 10 | "location": "Pacific Ocean", 11 | "category": "Beaches" 12 | }, 13 | { 14 | "id": "2", 15 | "name": "Alpine Chalet Retreat", 16 | "image": "https://images.unsplash.com/photo-1512273222628-4daea6e55abb?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 17 | "description": "Experience the breathtaking beauty of the Alps from your cozy chalet, surrounded by snow-capped peaks and charming villages.", 18 | "rating": 4.5, 19 | "price": "120", 20 | "duration": "4", 21 | "location": "Switzerland", 22 | "category": "Mountains" 23 | }, 24 | { 25 | "id": "3", 26 | "name": "Urban Explorer's Dream", 27 | "image": "https://images.unsplash.com/photo-1546436836-07a91091f160?q=80&w=3548&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 28 | "description": "Immerse yourself in the vibrant energy of the city with bustling streets, iconic landmarks, and endless cultural experiences.", 29 | "rating": 4.7, 30 | "price": "140", 31 | "duration": "2", 32 | "location": "USA", 33 | "category": "Cities" 34 | }, 35 | { 36 | "id": "4", 37 | "name": "Enchanted Forest Retreat", 38 | "image": "https://plus.unsplash.com/premium_photo-1668917805105-2d5d9e49af87?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 39 | "description": "Find serenity in the heart of nature with towering trees, hidden waterfalls, and enchanting wildlife in this magical forest retreat.", 40 | "rating": 4.6, 41 | "price": "180", 42 | "duration": "5", 43 | "location": "USA", 44 | "category": "Forests" 45 | }, 46 | { 47 | "id": "5", 48 | "name": "Tranquil Lakeside Haven", 49 | "image": "https://images.unsplash.com/photo-1535927583620-7940e95a5a05?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8bGFrZSx0cmFucXVpbHx8fHx8fDE3MTEwMzI1MjA&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 50 | "description": "Relax and rejuvenate by the tranquil lakeside, surrounded by stunning scenery and peaceful serenity.", 51 | "rating": 4.9, 52 | "price": "120", 53 | "duration": "3", 54 | "location": "England", 55 | "category": "Lakes" 56 | }, 57 | { 58 | "id": "6", 59 | "name": "Sandy Shores Beach Resort", 60 | "image": "https://images.unsplash.com/photo-1506929562872-bb421503ef21?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8YmVhY2gsc2FuZHl8fHx8fHwxNzExMDMyNTg5&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 61 | "description": "Indulge in the ultimate beachfront luxury with pristine sands, crystal-clear waters, and world-class amenities.", 62 | "rating": 4.8, 63 | "price": "140", 64 | "duration": "2", 65 | "location": "Maldives", 66 | "category": "Beaches" 67 | }, 68 | { 69 | "id": "7", 70 | "name": "Majestic Mountain Lodge", 71 | "image": "https://images.unsplash.com/photo-1543768179-656dbc9d9be9?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8bW91bnRhaW4sbWFqZXN0aWN8fHx8fHwxNzExMDMyNjUw&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 72 | "description": "Unwind in the lap of luxury amidst majestic mountain peaks, alpine meadows, and breathtaking vistas.", 73 | "rating": 4.7, 74 | "price": "100", 75 | "duration": "7", 76 | "location": "Canada", 77 | "category": "Mountains" 78 | }, 79 | { 80 | "id": "8", 81 | "name": "Metropolitan Marvels", 82 | "image": "https://images.unsplash.com/photo-1697543117287-53b5ab69742b?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8Y2l0eSxtZXRyb3BvbGl0YW58fHx8fHwxNzExMDMyNzM1&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 83 | "description": "Discover the cultural richness and architectural wonders of bustling metropolises around the globe.", 84 | "rating": 4.6, 85 | "price": "170", 86 | "duration": "3", 87 | "location": "Japan", 88 | "category": "Cities" 89 | }, 90 | { 91 | "id": "9", 92 | "name": "Mystical Forest Sanctuary", 93 | "image": "https://images.unsplash.com/photo-1496564692837-ec757c470e07?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8Zm9yZXN0LG15c3RpY2FsfHx8fHx8MTcxMTAzMjgyNA&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 94 | "description": "Embark on a journey through ancient forests, mystical ruins, and hidden wonders in this enchanted sanctuary.", 95 | "rating": 4.9, 96 | "price": "80", 97 | "duration": "2", 98 | "location": "Brazil", 99 | "category": "Forests" 100 | }, 101 | { 102 | "id": "10", 103 | "name": "Serene Lake Retreat", 104 | "image": "https://images.unsplash.com/photo-1541420937988-702d78cb9fa1?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8bGFrZSxzZXJlbmV8fHx8fHwxNzExMDMyODcx&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 105 | "description": "Escape the hustle and bustle of city life and immerse yourself in the serene beauty of lakeside living.", 106 | "rating": 4.5, 107 | "price": "120", 108 | "duration": "3", 109 | "location": "USA", 110 | "category": "Lakes" 111 | }, 112 | { 113 | "id": "11", 114 | "name": "Golden Sands Beach Resort", 115 | "image": "https://images.unsplash.com/photo-1535792679781-7a212687bc8f?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8YmVhY2gsZ29sZGVufHx8fHx8MTcxMTAzMjkzOQ&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 116 | "description": "Experience paradise on earth with golden sands, azure waters, and luxurious beachfront accommodations.", 117 | "rating": 4.8, 118 | "price": "150", 119 | "duration": "3", 120 | "location": "Fiji", 121 | "category": "Beaches" 122 | }, 123 | { 124 | "id": "12", 125 | "name": "Alpine Adventure Lodge", 126 | "image": "https://images.unsplash.com/photo-1521929135277-1891dc05ef4d?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8bW91bnRhaW4sYWxwaW5lfHx8fHx8MTcxMTAzMjk4OQ&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 127 | "description": "Embark on an unforgettable alpine adventure with thrilling outdoor activities and cozy lodge accommodations.", 128 | "rating": 4.6, 129 | "price": "120", 130 | "duration": "3", 131 | "location": "Italy", 132 | "category": "Mountains" 133 | }, 134 | { 135 | "id": "13", 136 | "name": "Cultural Capitals", 137 | "image": "https://images.unsplash.com/photo-1689723744935-60e01c337543?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8Y2l0eSxjdWx0dXJhbHx8fHx8fDE3MTEwMzMwODY&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 138 | "description": "Explore the vibrant tapestry of culture, history, and art in the world's most captivating cultural capitals.", 139 | "rating": 4.7, 140 | "price": "130", 141 | "duration": "5", 142 | "location": "France", 143 | "category": "Cities" 144 | }, 145 | { 146 | "id": "14", 147 | "name": "Enchanted Grove", 148 | "image": "https://images.unsplash.com/photo-1541897174996-d68b4a8055c9?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8Zm9yZXN0LGdyb3ZlfHx8fHx8MTcxMTAzMzEyMQ&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 149 | "description": "Step into a realm of enchantment and wonder in this mystical grove filled with ancient trees and magical creatures.", 150 | "rating": 4.9, 151 | "price": "150", 152 | "duration": "3", 153 | "location": "Germany", 154 | "category": "Forests" 155 | }, 156 | { 157 | "id": "15", 158 | "name": "Tranquil Mountain Hideaway", 159 | "image": "https://images.unsplash.com/photo-1584812261582-efe582e4c124?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=900&ixid=MnwxfDB8MXxyYW5kb218MHx8bW91bnRhaW4sdHJhbnF1aWx8fHx8fHwxNzExMDMzMTky&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1600", 160 | "description": "Find solace and tranquility amidst towering peaks and pristine wilderness in this secluded mountain hideaway.", 161 | "rating": 4.8, 162 | "price": "80", 163 | "duration": "5", 164 | "location": "Canada", 165 | "category": "Mountains" 166 | }, 167 | { 168 | "id": "16", 169 | "name": "Seaside Sanctuary", 170 | "image": "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df", 171 | "description": "Escape to a serene seaside sanctuary with breathtaking ocean views, pristine beaches, and luxurious accommodations.", 172 | "rating": 4.7, 173 | "price": "100", 174 | "duration": "3", 175 | "location": "Italy", 176 | "category": "Beaches" 177 | }, 178 | { 179 | "id": "17", 180 | "name": "Alpine Lakeside Lodge", 181 | "image": "https://images.unsplash.com/photo-1508899005055-fb1677e7b887?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 182 | "description": "Unwind by the tranquil shores of an alpine lake, surrounded by majestic peaks and pristine nature.", 183 | "rating": 4.6, 184 | "price": "100", 185 | "duration": "3", 186 | "location": "Canada", 187 | "category": "Lakes" 188 | }, 189 | { 190 | "id": "18", 191 | "name": "Urban Oasis", 192 | "image": "https://images.unsplash.com/photo-1525625293386-3f8f99389edd?q=80&w=3452&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 193 | "description": "Discover a peaceful oasis amidst the bustling cityscape, where lush gardens and tranquil ponds provide respite.", 194 | "rating": 4.8, 195 | "price": "90", 196 | "duration": "3", 197 | "location": "Singapore", 198 | "category": "Cities" 199 | }, 200 | { 201 | "id": "19", 202 | "name": "Whispering Woods Retreat", 203 | "image": "https://images.unsplash.com/photo-1574288339398-531d55fd84f3?q=80&w=3540&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 204 | "description": "Find solace in the tranquil embrace of whispering woods, where ancient trees and hidden streams beckon the soul.", 205 | "rating": 4.9, 206 | "price": "140", 207 | "duration": "3", 208 | "location": "USA", 209 | "category": "Forests" 210 | }, 211 | { 212 | "id": "20", 213 | "name": "Sapphire Shores Beach Resort", 214 | "image": "https://plus.unsplash.com/premium_photo-1681223447383-402912b83029?q=80&w=3474&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 215 | "description": "Experience the epitome of luxury beachfront living with sapphire waters, pristine sands, and world-class amenities.", 216 | "rating": 4.7, 217 | "price": "150", 218 | "duration": "3", 219 | "location": "Bahamas", 220 | "category": "Beaches" 221 | } 222 | ] 223 | -------------------------------------------------------------------------------- /data/groups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "name": "Holiday Travel Agency", 5 | "image": "https://images.unsplash.com/photo-1516496636080-14fb876e029d?q=80&w=3388&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 6 | "rating": 4.7, 7 | "reviews": 1450 8 | }, 9 | { 10 | "id": "2", 11 | "name": "Happy Tour Planners", 12 | "image": "https://images.unsplash.com/photo-1618591362251-1c9f70b59192?q=80&w=3177&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 13 | "rating": 4.8, 14 | "reviews": 650 15 | }, 16 | { 17 | "id": "3", 18 | "name": "Royal Tour & Travels", 19 | "image": "https://images.unsplash.com/photo-1600468636011-c75ae69b7fcb?q=80&w=2625&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 20 | "rating": 4.9, 21 | "reviews": 550 22 | } 23 | ] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expo-travel-app", 3 | "main": "expo-router/entry", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "expo start", 7 | "android": "expo start --android", 8 | "ios": "expo start --ios", 9 | "web": "expo start --web", 10 | "test": "jest --watchAll" 11 | }, 12 | "jest": { 13 | "preset": "jest-expo" 14 | }, 15 | "dependencies": { 16 | "@expo/vector-icons": "^14.0.0", 17 | "@react-navigation/native": "^6.0.2", 18 | "expo": "~50.0.14", 19 | "expo-font": "~11.10.3", 20 | "expo-linking": "~6.2.2", 21 | "expo-router": "~3.4.8", 22 | "expo-splash-screen": "~0.26.4", 23 | "expo-status-bar": "~1.11.1", 24 | "expo-system-ui": "~2.9.3", 25 | "expo-web-browser": "~12.8.2", 26 | "react": "18.2.0", 27 | "react-dom": "18.2.0", 28 | "react-native": "0.73.6", 29 | "react-native-safe-area-context": "4.8.2", 30 | "react-native-screens": "~3.29.0", 31 | "react-native-web": "~0.19.6", 32 | "react-native-reanimated": "~3.6.2" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "^7.20.0", 36 | "@types/react": "~18.2.45", 37 | "jest": "^29.2.1", 38 | "jest-expo": "~50.0.4", 39 | "react-test-renderer": "18.2.0", 40 | "typescript": "^5.1.3" 41 | }, 42 | "private": true 43 | } 44 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": [ 7 | "./*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "**/*.ts", 13 | "**/*.tsx", 14 | ".expo/types/**/*.ts", 15 | "expo-env.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /types/groupType.ts: -------------------------------------------------------------------------------- 1 | export interface GroupType { 2 | id: string; 3 | name: string; 4 | image: string; 5 | rating: number; 6 | reviews: number; 7 | } -------------------------------------------------------------------------------- /types/listingType.ts: -------------------------------------------------------------------------------- 1 | export interface ListingType { 2 | id: string; 3 | name: string; 4 | image: string; 5 | description: string; 6 | rating: number; 7 | price: string; 8 | duration: string; 9 | location: string; 10 | category: string; 11 | } --------------------------------------------------------------------------------