├── .expo-shared
└── assets.json
├── .gitignore
├── App.js
├── README.md
├── app.gif
├── app.json
├── assets
├── adaptive-icon.png
├── favicon.png
├── icon.png
└── splash.png
├── babel.config.js
├── components
├── Map.js
├── NavFavourites.js
├── NavOptions.js
├── NavigateCard.js
└── RideOptionsCard.js
├── package-lock.json
├── package.json
├── screens
├── HomeScreen.js
└── MapScreen.js
├── slices
└── navSlice.js
└── store.js
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .expo/
3 | dist/
4 | npm-debug.*
5 | *.jks
6 | *.p8
7 | *.p12
8 | *.key
9 | *.mobileprovision
10 | *.orig.*
11 | web-build/
12 |
13 | # macOS
14 | .DS_Store
15 |
16 | .env
17 |
18 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Text, View } from "react-native";
2 | import { Provider } from "react-redux";
3 | import store from "./store";
4 | import { SafeAreaProvider } from "react-native-safe-area-context";
5 | import HomeScreen from "./screens/HomeScreen";
6 | import { NavigationContainer } from "@react-navigation/native";
7 | import { createStackNavigator } from "@react-navigation/stack";
8 | import MapScreen from "./screens/MapScreen";
9 | import "react-native-gesture-handler";
10 |
11 | export default function App() {
12 | const Stack = createStackNavigator();
13 | return (
14 |
15 |
16 |
17 |
18 |
25 |
32 |
33 |
34 |
35 |
36 | );
37 | }
38 | const styles = StyleSheet.create({});
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Uber-Clone React-Native-App
2 |
3 | Built a React Native application using EXPO Framework which allows users to enter start location and destination and it displays the cost of cabs based on the type selected.
4 |
5 | ## Preview
6 |
7 | 
8 |
9 | ## Run on Expo App on IOS & Android
10 |
11 | - Go to [Uber-Clone](https://expo.dev/@tanishka_yadav/clone-uber?serviceType=classic&distribution=expo-go)
12 | - Scan the QR code and install the Expo app
13 | - Click on uber-clone
14 |
--------------------------------------------------------------------------------
/app.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tanishka-dev/Uber-Clone-React-Native/8410f80ff74eb85f70dba9dc41d8ca9026c7336a/app.gif
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "clone-uber",
4 | "slug": "clone-uber",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "splash": {
9 | "image": "./assets/splash.png",
10 | "resizeMode": "contain",
11 | "backgroundColor": "#ffffff"
12 | },
13 | "updates": {
14 | "fallbackToCacheTimeout": 0
15 | },
16 | "assetBundlePatterns": [
17 | "**/*"
18 | ],
19 | "ios": {
20 | "supportsTablet": true
21 | },
22 | "android": {
23 | "adaptiveIcon": {
24 | "foregroundImage": "./assets/adaptive-icon.png",
25 | "backgroundColor": "#FFFFFF"
26 | }
27 | },
28 | "web": {
29 | "favicon": "./assets/favicon.png"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tanishka-dev/Uber-Clone-React-Native/8410f80ff74eb85f70dba9dc41d8ca9026c7336a/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tanishka-dev/Uber-Clone-React-Native/8410f80ff74eb85f70dba9dc41d8ca9026c7336a/assets/favicon.png
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tanishka-dev/Uber-Clone-React-Native/8410f80ff74eb85f70dba9dc41d8ca9026c7336a/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tanishka-dev/Uber-Clone-React-Native/8410f80ff74eb85f70dba9dc41d8ca9026c7336a/assets/splash.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true);
3 | return {
4 | presets: ["babel-preset-expo"],
5 | "plugins": [
6 | ["module:react-native-dotenv", {
7 | "envName": "APP_ENV",
8 | "moduleName": "@env",
9 | "path": ".env",
10 | }],
11 | ],
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/components/Map.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Text, View } from "react-native";
2 | import React, { useEffect, useRef } from "react";
3 | import MapViewDirections from "react-native-maps-directions";
4 | import tw from "tailwind-react-native-classnames";
5 | import MapView, { Marker } from "react-native-maps";
6 | import { useDispatch, useSelector } from "react-redux";
7 | import { GOOGLE_MAPS_APIKEY } from "@env";
8 | import {
9 | selectDestination,
10 | selectOrigin,
11 | setTimeTravelInformation,
12 | } from "../slices/navSlice";
13 |
14 | const Map = () => {
15 | const origin = useSelector(selectOrigin);
16 | const destination = useSelector(selectDestination);
17 | const mapRef = useRef(null);
18 | const dispatch = useDispatch();
19 |
20 | useEffect(() => {
21 | if (!origin || !destination) return;
22 |
23 | mapRef.current.fitToSuppliedMarkers(["origin", "destination"], {
24 | edgePadding: { top: 60, right: 60, bottom: 60, left: 60 },
25 | });
26 |
27 | //Zoom
28 | }, [origin, destination]);
29 |
30 | useEffect(() => {
31 | if (!origin || !destination) return;
32 |
33 | const getTravelTime = async () => {
34 | fetch(`https://maps.googleapis.com/maps/api/distancematrix/json?
35 | units=imperial&origins=${origin.description}&destinations=${destination.description}&key=${GOOGLE_MAPS_APIKEY}`)
36 | .then((res) => res.json())
37 | .then((data) => {
38 | dispatch(setTimeTravelInformation(data.rows[0].elements[0]));
39 | });
40 | };
41 |
42 | getTravelTime();
43 | }, [origin, destination, GOOGLE_MAPS_APIKEY]);
44 | return (
45 |
56 | {origin && destination && (
57 |
64 | )}
65 |
66 | {origin?.location && (
67 |
76 | )}
77 | {destination?.location && (
78 |
87 | )}
88 |
89 | );
90 | };
91 |
92 | export default Map;
93 |
94 | const styles = StyleSheet.create({});
95 |
--------------------------------------------------------------------------------
/components/NavFavourites.js:
--------------------------------------------------------------------------------
1 | import {
2 | FlatList,
3 | StyleSheet,
4 | Text,
5 | TouchableOpacity,
6 | View,
7 | } from "react-native";
8 | import React from "react";
9 | import { Icon } from "react-native-elements";
10 | import tw from "tailwind-react-native-classnames";
11 | import { useDispatch } from "react-redux";
12 | import { setDestination, setOrigin } from "../slices/navSlice";
13 | import { useNavigation } from "@react-navigation/native";
14 | const data = [
15 | {
16 | id: "123",
17 | icon: "home",
18 | location: "Home",
19 | destination: "Noida, India",
20 | },
21 | {
22 | id: "456",
23 | icon: "briefcase",
24 | location: "Work",
25 | destination: "Gurgaon, India",
26 | },
27 | ];
28 |
29 | const NavFavourites = () => {
30 | const dispatch = useDispatch();
31 | const navigation = useNavigation();
32 | return (
33 | item.id}
36 | ItemSeparatorComponent={() => (
37 |
38 | )}
39 | renderItem={({ item: { location, destination, icon } }) => (
40 | {}}
42 | fetchDetails={true}
43 | style={tw`flex-row items-center p-5 `}
44 | >
45 |
52 |
53 | {location}
54 | {destination}
55 |
56 |
57 | )}
58 | />
59 | );
60 | };
61 |
62 | export default NavFavourites;
63 |
64 | const styles = StyleSheet.create({});
65 |
--------------------------------------------------------------------------------
/components/NavOptions.js:
--------------------------------------------------------------------------------
1 | import {
2 | FlatList,
3 | StyleSheet,
4 | Text,
5 | Image,
6 | TouchableOpacity,
7 | View,
8 | } from "react-native";
9 | import React from "react";
10 | import tw from "tailwind-react-native-classnames";
11 | import { Icon } from "react-native-elements";
12 | import { useNavigation } from "@react-navigation/native";
13 | import { useSelector } from "react-redux";
14 | import { selectOrigin } from "../slices/navSlice";
15 |
16 | const array = [
17 | {
18 | id: 1,
19 | title: "Get a ride",
20 | image: "https://links.papareact.com/3pn",
21 | screen: "MapScreen",
22 | },
23 |
24 | //Uber Eats can be implemented here
25 | ];
26 | const NavOptions = () => {
27 | const navigation = useNavigation();
28 | const origin = useSelector(selectOrigin);
29 | return (
30 | (
34 | navigation.navigate(item.screen)}
36 | style={tw`p-2 pb-8 pl-6 pt-4 bg-gray-200 m-3 w-60`}
37 | disabled={!origin}
38 | >
39 |
40 |
44 | {item.title}
45 |
51 |
52 |
53 | )}
54 | />
55 | );
56 | };
57 |
58 | export default NavOptions;
59 |
--------------------------------------------------------------------------------
/components/NavigateCard.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Text, View, TouchableOpacity } from "react-native";
2 | import React from "react";
3 | import { SafeAreaView } from "react-native-safe-area-context";
4 | import tw from "tailwind-react-native-classnames";
5 | import { GooglePlacesAutocomplete } from "react-native-google-places-autocomplete";
6 | import { GOOGLE_MAPS_APIKEY } from "@env";
7 | import { useDispatch } from "react-redux";
8 | import { setDestination } from "../slices/navSlice";
9 | import { useNavigation } from "@react-navigation/native";
10 | import NavFavourites from "./NavFavourites";
11 |
12 | import { Icon } from "react-native-elements";
13 |
14 | const NavigateCard = () => {
15 | const dispatch = useDispatch();
16 | const navigation = useNavigation();
17 |
18 | return (
19 |
20 |
21 |
22 | {
29 | dispatch(
30 | setDestination({
31 | location: details.geometry.location,
32 | description: data.description,
33 | })
34 | );
35 |
36 | navigation.navigate("RideOptionsCard");
37 | }}
38 | query={{
39 | key: GOOGLE_MAPS_APIKEY,
40 | language: "en",
41 | }}
42 | nearbyPlacesAPI="GooglePlacesSearch"
43 | debounce={400}
44 | />
45 |
46 |
47 |
48 |
49 | navigation.navigate("RideOptionsCard")}
51 | style={tw` flex flex-row bg-black w-24 px-4 py-3 rounded-full `}
52 | >
53 |
54 | Rides
55 |
56 |
57 |
58 | );
59 | };
60 |
61 | // add button for UberEats
62 | export default NavigateCard;
63 |
64 | const toInputBoxStyle = StyleSheet.create({
65 | container: {
66 | backgroundColor: "white",
67 | paddingTop: 20,
68 | flex: 0,
69 | },
70 | textInput: {
71 | backgroundColor: "#DDDDDF",
72 | borderRadius: 0,
73 | fontSize: 18,
74 | },
75 | textInputContainer: {
76 | paddingHorizontal: 20,
77 | paddingBottom: 0,
78 | },
79 | });
80 |
--------------------------------------------------------------------------------
/components/RideOptionsCard.js:
--------------------------------------------------------------------------------
1 | import { Text, TouchableOpacity, View, FlatList, Image } from "react-native";
2 | import React, { useState } from "react";
3 | import { SafeAreaView } from "react-native-safe-area-context";
4 | import tw from "tailwind-react-native-classnames";
5 | import { Icon } from "react-native-elements";
6 | import { useNavigation } from "@react-navigation/native";
7 | import { useSelector } from "react-redux";
8 | import { selectTimeTravelInformation } from "../slices/navSlice";
9 |
10 | const data = [
11 | {
12 | id: "Uber-X-123",
13 | title: "Uber X",
14 | multiplier: 1,
15 | image: "https://links.papareact.com/3pn",
16 | },
17 | {
18 | id: "Uber-XL-456",
19 | title: "Uber XL",
20 | multiplier: 1.2,
21 | image: "https://links.papareact.com/5w8",
22 | },
23 | {
24 | id: "Uber-LUX-789",
25 | title: "Uber LUX",
26 | multiplier: 1.75,
27 | image: "https://links.papareact.com/7pf",
28 | },
29 | ];
30 |
31 | const SURGE_CHARGE_RATE = 1.5;
32 | const RideOptionsCard = () => {
33 | const navigation = useNavigation();
34 |
35 | const [selected, setSelected] = useState(null);
36 | const travelTimeInformation = useSelector(selectTimeTravelInformation);
37 | return (
38 |
39 |
40 | navigation.navigate("NavigateCard")}
43 | >
44 |
45 |
46 |
47 | Choose A Ride for- {travelTimeInformation?.distance?.text}
48 |
49 |
50 | item.id}
53 | renderItem={({ item: { id, title, multiplier, image }, item }) => (
54 | setSelected(item)}
59 | >
60 |
64 |
65 | {title}
66 | Travel Time-{travelTimeInformation?.duration?.text}
67 |
68 |
69 | ₹
70 | {(
71 | (travelTimeInformation?.duration?.value *
72 | SURGE_CHARGE_RATE *
73 | multiplier) /
74 | 100
75 | ).toFixed()}
76 |
77 |
78 | )}
79 | />
80 |
81 |
85 |
86 | Choose {selected?.title}
87 |
88 |
89 |
90 |
91 | );
92 | };
93 |
94 | export default RideOptionsCard;
95 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "clone-uber",
3 | "version": "1.0.0",
4 | "main": "node_modules/expo/AppEntry.js",
5 | "scripts": {
6 | "postinstall": "npm run install",
7 | "start": "expo start",
8 | "android": "expo start --android",
9 | "ios": "expo start --ios",
10 | "web": "expo start --web",
11 | "eject": "expo eject"
12 | },
13 | "dependencies": {
14 | "@react-native-community/masked-view": "^0.1.11",
15 | "@react-navigation/native": "^6.0.8",
16 | "@react-navigation/native-stack": "^6.5.0",
17 | "@react-navigation/stack": "^6.1.1",
18 | "@reduxjs/toolkit": "^1.8.0",
19 | "expo": "~44.0.0",
20 | "expo-status-bar": "~1.2.0",
21 | "react": "17.0.1",
22 | "react-dom": "17.0.1",
23 | "react-native": "0.64.3",
24 | "react-native-dotenv": "^3.3.1",
25 | "react-native-elements": "^3.4.2",
26 | "react-native-gesture-handler": "~2.1.0",
27 | "react-native-google-places-autocomplete": "^2.4.1",
28 | "react-native-maps": "0.29.4",
29 | "react-native-maps-directions": "^1.8.0",
30 | "react-native-reanimated": "~2.3.1",
31 | "react-native-safe-area-context": "3.3.2",
32 | "react-native-screens": "~3.10.1",
33 | "react-native-vector-icons": "^9.1.0",
34 | "react-native-web": "0.17.1",
35 | "react-redux": "^7.2.6",
36 | "tailwind-react-native-classnames": "^1.5.1",
37 | "expo-updates": "~0.11.6"
38 | },
39 | "devDependencies": {
40 | "@babel/core": "^7.12.9"
41 | },
42 | "private": true
43 | }
44 |
--------------------------------------------------------------------------------
/screens/HomeScreen.js:
--------------------------------------------------------------------------------
1 | import { View, Text, SafeAreaView, Image } from "react-native";
2 | import React from "react";
3 | import tw from "tailwind-react-native-classnames";
4 | import NavOptions from "../components/NavOptions";
5 | import { GooglePlacesAutocomplete } from "react-native-google-places-autocomplete";
6 | import { GOOGLE_MAPS_APIKEY } from "@env";
7 | import { useDispatch } from "react-redux";
8 | import { setDestination, setOrigin } from "../slices/navSlice";
9 | import NavFavourites from "../components/NavFavourites";
10 | const HomeScreen = () => {
11 | const dispatch = useDispatch();
12 | return (
13 |
14 |
15 |
21 | {
32 | dispatch(
33 | setOrigin({
34 | location: details.geometry.location,
35 | description: data.description,
36 | })
37 | );
38 | dispatch(setDestination(null));
39 | }}
40 | fetchDetails={true}
41 | enablePoweredByContainer={false}
42 | returnKeyType={"search"}
43 | query={{
44 | key: GOOGLE_MAPS_APIKEY,
45 | language: "en",
46 | }}
47 | nearbyPlacesAPI="GooglePlacesSearch"
48 | debounce={400}
49 | />
50 |
51 |
52 | {/* */}
53 |
54 |
55 | );
56 | };
57 |
58 | export default HomeScreen;
59 |
--------------------------------------------------------------------------------
/screens/MapScreen.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
2 | import React from "react";
3 | import tw from "tailwind-react-native-classnames";
4 | import Map from "../components/Map";
5 | import { createStackNavigator } from "@react-navigation/stack";
6 | import NavigateCard from "../components/NavigateCard";
7 | import RideOptionsCard from "../components/RideOptionsCard";
8 | import { Icon } from "react-native-elements/dist/icons/Icon";
9 | import { useNavigation } from "@react-navigation/native";
10 | const MapScreen = () => {
11 | const Stack = createStackNavigator();
12 | const navigation = useNavigation();
13 | return (
14 |
15 | navigation.navigate("HomeScreen")}
17 | style={tw`absolute top-10 left-6 bg-gray-100 z-50 p-3 rounded-full shadow-lg`}
18 | >
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | export default MapScreen;
47 |
48 | const styles = StyleSheet.create({});
49 |
--------------------------------------------------------------------------------
/slices/navSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | const initialState = {
4 | origin: null,
5 | destination: null,
6 | travelTimeInformation: null,
7 | };
8 | export const navSlice = createSlice({
9 | name: "nav",
10 | initialState,
11 | reducers: {
12 | setOrigin: (state, action) => {
13 | state.origin = action.payload;
14 | },
15 | setDestination: (state, action) => {
16 | state.destination = action.payload;
17 | },
18 | setTimeTravelInformation: (state, action) => {
19 | state.travelTimeInformation = action.payload;
20 | },
21 | },
22 | });
23 |
24 | export const { setOrigin, setDestination, setTimeTravelInformation } =
25 | navSlice.actions;
26 |
27 | //Selectors
28 | export const selectOrigin = (state) => state.nav.origin;
29 | export const selectDestination = (state) => state.nav.destination;
30 | export const selectTimeTravelInformation = (state) =>
31 | state.nav.travelTimeInformation;
32 |
33 | export default navSlice.reducer;
34 |
--------------------------------------------------------------------------------
/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import navReducer from "./slices/navSlice";
3 |
4 | export default configureStore({
5 | reducer: {
6 | nav: navReducer,
7 | },
8 | });
9 |
--------------------------------------------------------------------------------