├── .expo-shared
└── assets.json
├── .gitignore
├── App.js
├── app.json
├── assets
├── icon.png
└── splash.png
├── babel.config.js
├── components
├── Dashboard
│ ├── Dashboard.js
│ └── styles.js
├── Map
│ ├── MapView.js
│ └── styles.js
├── Menu
│ ├── Menu.js
│ └── styles.js
├── Navigation
│ └── Navigator.js
├── Place
│ ├── PlaceList.js
│ └── styles.js
├── Profile
│ ├── Profile.js
│ └── styles.js
├── Review
│ └── ReviewStars.js
└── SearchBar
│ ├── SearchBar.js
│ └── styles.js
├── package.json
├── yarn-error.log
└── yarn.lock
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "f9155ac790fd02fadcdeca367b02581c04a353aa6d5aa84409a59f6804c87acd": true,
3 | "89ed26367cdb9b771858e026f2eb95bfdb90e5ae943e716575327ec325f39c44": true
4 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | .env
4 | npm-debug.*
5 | *.jks
6 | *.p8
7 | *.p12
8 | *.key
9 | *.mobileprovision
10 | *.orig.*
11 | web-build/
12 | web-report/
13 |
14 | # macOS
15 | .DS_Store
16 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | //Components
3 | import Navigator from "./components/Navigation/Navigator";
4 | //Encapsulate every other component inside the navigation
5 | export default function App() {
6 | return (
7 | <>
8 |
9 | >
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "Real-world React Native app using Google Places and Maps",
4 | "slug": "rn-google-maps-places",
5 | "privacy": "public",
6 | "sdkVersion": "36.0.0",
7 | "platforms": ["ios", "android", "web"],
8 | "version": "1.0.0",
9 | "orientation": "portrait",
10 | "icon": "./assets/icon.png",
11 | "splash": {
12 | "image": "./assets/splash.png",
13 | "resizeMode": "contain",
14 | "backgroundColor": "#ffffff"
15 | },
16 | "updates": {
17 | "fallbackToCacheTimeout": 0
18 | },
19 | "assetBundlePatterns": ["**/*"],
20 | "ios": {
21 | "supportsTablet": true
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blarzHernandez/react-native-google-map/e91a31f116f457e0ed6abf31137228687a3fc025/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blarzHernandez/react-native-google-map/e91a31f116f457e0ed6abf31137228687a3fc025/assets/splash.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ["babel-preset-expo", "module:react-native-dotenv"]
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/components/Dashboard/Dashboard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Container, Content } from "native-base";
3 |
4 | //Components
5 | import SearchBar from "../SearchBar/SearchBar";
6 | import MenuItem from "../Menu/Menu";
7 |
8 | //Styles
9 | import styles from "./styles";
10 | class Dashboard extends Component {
11 | static navigationOptions = {
12 | headerTitle: "Find all Around Me!"
13 | };
14 | render() {
15 | const { navigation } = this.props;
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | }
25 | }
26 |
27 | export default Dashboard;
28 |
--------------------------------------------------------------------------------
/components/Dashboard/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from "react-native";
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | justifyContent: "center",
7 | alignItems: "center",
8 | flexWrap: "wrap",
9 | alignContent: "center",
10 | paddingHorizontal: 20,
11 | paddingVertical: 20
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/components/Map/MapView.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import MapView, { PROVIDER_GOOGLE } from "react-native-maps";
3 | import { View } from "react-native";
4 | import { GOOGLE_API_KEY } from "react-native-dotenv";
5 |
6 | //Components
7 | import PlaceList from "../Place/PlaceList";
8 | //Styles
9 | import styles from "./styles";
10 | class MapScreen extends Component {
11 | //Set the HeaderTitle screen
12 | static navigationOptions = props => {
13 | const placeName = props.navigation.getParam("placeName");
14 | return { headerTitle: placeName.toUpperCase() };
15 | };
16 | constructor(props) {
17 | super(props);
18 | //Initial State
19 | this.state = {
20 | lat: null,
21 | long: null,
22 | places: [],
23 | isLoading: false,
24 | placeType: "restaurant"
25 | };
26 | }
27 | componentDidMount() {
28 | console.log(this.props);
29 | const { navigation } = this.props;
30 | const placeType = navigation.getParam("placeType");
31 | this.setState({ placeType: placeType });
32 |
33 | this.getCurrentLocation();
34 | }
35 | /**
36 | * Get current user's position
37 | */
38 | getCurrentLocation() {
39 | navigator.geolocation.getCurrentPosition(position => {
40 | const lat = position.coords.latitude;
41 | const long = position.coords.longitude;
42 | this.setState({ lat: lat, long: long });
43 | this.getPlaces();
44 | });
45 | }
46 |
47 | /**
48 | * Get the Place URL
49 | */
50 | getPlacesUrl(lat, long, radius, type, apiKey) {
51 | const baseUrl = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?`;
52 | const location = `location=${lat},${long}&radius=${radius}`;
53 | const typeData = `&types=${type}`;
54 | const api = `&key=${apiKey}`;
55 | return `${baseUrl}${location}${typeData}${api}`;
56 | }
57 |
58 | getPlaces() {
59 | const { lat, long, placeType } = this.state;
60 | const markers = [];
61 | const url = this.getPlacesUrl(lat, long, 1500, placeType, GOOGLE_API_KEY);
62 | fetch(url)
63 | .then(res => res.json())
64 | .then(res => {
65 | res.results.map((element, index) => {
66 | const marketObj = {};
67 | marketObj.id = element.id;
68 | marketObj.name = element.name;
69 | marketObj.photos = element.photos;
70 | marketObj.rating = element.rating;
71 | marketObj.vicinity = element.vicinity;
72 | marketObj.marker = {
73 | latitude: element.geometry.location.lat,
74 | longitude: element.geometry.location.lng
75 | };
76 |
77 | markers.push(marketObj);
78 | });
79 | //update our places array
80 | this.setState({ places: markers });
81 | });
82 | }
83 |
84 | render() {
85 | const { lat, long, places } = this.state;
86 | return (
87 |
88 |
89 |
101 | {places.map((marker, i) => (
102 |
110 | ))}
111 |
112 |
113 |
114 |
115 |
116 |
117 | );
118 | }
119 | }
120 |
121 | export default MapScreen;
122 |
--------------------------------------------------------------------------------
/components/Map/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from "react-native";
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | justifyContent: "center"
7 | },
8 | mapView: {
9 | flex: 1,
10 | justifyContent: "center",
11 | height: "50%",
12 | width: "100%"
13 | },
14 | placeList: {
15 | flex: 1,
16 | justifyContent: "center"
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/components/Menu/Menu.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Text, View } from "native-base";
3 | import { TouchableOpacity } from "react-native";
4 | import { Icon } from "react-native-elements";
5 | import styles from "./styles";
6 |
7 | class Menu extends Component {
8 | /**
9 | * @param {String} name Icon name
10 | * @param {String} text place name
11 | * @param {Number} size Icon size
12 | * @param {String} color Icon color
13 | * @param {String} type Icon type
14 | * @param {String} placeType Place type to look up
15 |
16 | */
17 | getItem = (name, text, size, color, type, placeType) => (
18 |
20 | this.props.navigation.navigate("MapView", {
21 | placeType: placeType,
22 | placeName: text
23 | })
24 | }
25 | >
26 |
27 |
35 | {text}
36 |
37 |
38 | );
39 | render() {
40 | return (
41 |
42 |
43 | {this.getItem("beer", "Beers", 40, "#f50", "font-awesome", "bar")}
44 | {this.getItem("bank", "Bank", 40, "#031068", "font-awesome", "bank")}
45 | {this.getItem(
46 | "coffee",
47 | "Coffee",
48 | 40,
49 | "#300423",
50 | "font-awesome",
51 | "cafe"
52 | )}
53 | {this.getItem("md-fitness", "Gym", 40, "#0B6CFB", "ionicon", "gym")}
54 | {this.getItem(
55 | "bus",
56 | "Bus Station",
57 | 40,
58 | "#056C6B",
59 | "font-awesome",
60 | "bus_station"
61 | )}
62 | {this.getItem(
63 | "hotel",
64 | "Hotel",
65 | 40,
66 | "#0A23A6",
67 | "font-awesome",
68 | "book_store"
69 | )}
70 | {this.getItem(
71 | "local-pharmacy",
72 | "Pharmacy",
73 | 40,
74 | "#f50",
75 | "materialicons",
76 | "pharmacy"
77 | )}
78 | {this.getItem("movie", "Movie", 40, "#000000", "materialicons")}
79 | {this.getItem("favorite", "Favorities", 40, "#f66", "materialicons")}
80 |
81 |
82 | );
83 | }
84 | }
85 |
86 | export default Menu;
87 |
--------------------------------------------------------------------------------
/components/Menu/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from "react-native";
2 | const FONT_SIZE = 18;
3 | export default StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | justifyContent: "center",
7 | alignItems: "center"
8 | },
9 | iconContainer: {
10 | width: "100%",
11 | flexDirection: "row",
12 | flexWrap: "wrap"
13 | },
14 | iconStyle: {
15 | flexDirection: "column",
16 | alignItems: "center",
17 | padding: 5
18 | },
19 | textStyle: {
20 | fontSize: FONT_SIZE
21 | }
22 | });
23 |
--------------------------------------------------------------------------------
/components/Navigation/Navigator.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createAppContainer } from "react-navigation";
3 | import { createBottomTabNavigator } from "react-navigation-tabs";
4 | import { createStackNavigator } from "react-navigation-stack";
5 | import Ionicons from "react-native-vector-icons/Ionicons";
6 | import Icon from "react-native-vector-icons/FontAwesome";
7 | import DashboardScreen from "../../components/Dashboard/Dashboard";
8 | import ProfileScreen from "../../components/Profile/Profile";
9 | import MapScreen from "../../components/Map/MapView";
10 | //Screen in the Home tab
11 | const DashboardContainer = createStackNavigator(
12 | {
13 | Home: DashboardScreen,
14 | MapView: MapScreen
15 | },
16 | {
17 | initialRouteName: "Home"
18 | }
19 | );
20 | //The Main Tab =>Home - Profile add more tabs here..
21 | const bottomTab = createBottomTabNavigator(
22 | {
23 | Home: {
24 | screen: DashboardContainer,
25 | navigationOptions: {
26 | tabBarLabel: "Home",
27 | tabBarIcon: ({ focused }) => (
28 |
33 | ),
34 | style: {
35 | backgroundColor: "red"
36 | }
37 | }
38 | },
39 | Profile: {
40 | screen: ProfileScreen,
41 | navigationOptions: ({ navigation }) => ({
42 | title: "Profile",
43 | tabBarIcon: ({ focused }) => (
44 |
49 | )
50 | })
51 | }
52 | },
53 | {
54 | navigationOptions: {
55 | tabBarOptions: {
56 | activeTintColor: "#e90000",
57 | inactiveTintColor: "#575757",
58 | style: {
59 | backgroundColor: "#f2f2f2",
60 | height: 60
61 | }
62 | }
63 | }
64 | }
65 | );
66 | //Getting the tab header title
67 | bottomTab.navigationOptions = ({ navigation }) => {
68 | const { routeName } = navigation.state.routes[navigation.state.index];
69 | const headerTitle = routeName;
70 | return {
71 | headerTitle
72 | };
73 | };
74 |
75 | //Root navigator
76 | const AppNavigator = createStackNavigator(
77 | {
78 | Home: bottomTab
79 | },
80 | {
81 | initialRouteName: "Home",
82 | headerMode: "none"
83 | }
84 | );
85 | export default createAppContainer(AppNavigator);
86 |
--------------------------------------------------------------------------------
/components/Place/PlaceList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import {
3 | FlatList,
4 | TouchableOpacity,
5 | View,
6 | ActivityIndicator
7 | } from "react-native";
8 | import { ListItem, Text } from "react-native-elements";
9 | import { Container, Content } from "native-base";
10 | import { GOOGLE_API_KEY } from "react-native-dotenv";
11 |
12 | //Components
13 | import RenderStarReview from "../../components/Review/ReviewStars";
14 | import styles from "./styles";
15 |
16 | class PlaceList extends Component {
17 | render() {
18 | const { places } = this.props;
19 | const baseImage =
20 | "https://images.unsplash.com/photo-1552334405-4929565998d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1050&q=80";
21 | return (
22 |
23 |
24 | {places.length <= 0 && (
25 |
26 |
27 |
28 | )}
29 | {places.length > 0 && (
30 | (
33 |
34 |
38 | {item.name}
39 | 1.4km
40 |
41 | }
42 | subtitle={
43 | item.rating && (
44 |
45 |
46 |
47 | {item.rating.toFixed(1)}
48 |
49 |
50 | {item.vicinity}
51 |
52 |
53 | )
54 | }
55 | leftAvatar={{
56 | rounded: false,
57 | size: "large",
58 | source: item.photos && {
59 | uri:
60 | item.photos.length > 0
61 | ? `https://maps.googleapis.com/maps/api/place/photo?photoreference=${item.photos[0].photo_reference}&sensor=false&maxheight=${item.photos[0].height}&maxwidth=${item.photos[0].width}&key=${GOOGLE_API_KEY}`
62 | : baseImage
63 | }
64 | }}
65 | bottomDivider
66 | chevron={{ color: "#e90000", size: 30 }}
67 | />
68 |
69 | )}
70 | keyExtractor={item => item.id.toString()}
71 | />
72 | )}
73 |
74 |
75 | );
76 | }
77 | }
78 |
79 | export default PlaceList;
80 |
--------------------------------------------------------------------------------
/components/Place/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from "react-native";
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | justifyContent: "space-between"
7 | },
8 | container2: {
9 | flex: 1,
10 | justifyContent: "center"
11 | },
12 | menuTitle: {
13 | fontSize: 16,
14 | fontFamily: "Poppins-Medium",
15 | color: "#575757",
16 | marginLeft: 20,
17 | marginTop: 10
18 | },
19 | mapView: {
20 | flex: 1,
21 | justifyContent: "center"
22 | },
23 | restaurantList: {
24 | flex: 1,
25 | justifyContent: "center"
26 | },
27 | chevron: {
28 | color: "#e90000"
29 | },
30 | rowDirection: {
31 | flexDirection: "row",
32 | justifyContent: "space-between"
33 | },
34 | startReviewsContainer: {
35 | flexDirection: "row",
36 | justifyContent: "flex-start"
37 | },
38 | loaderContainer: {
39 | flex: 1,
40 | justifyContent: "center"
41 | }
42 | });
43 |
--------------------------------------------------------------------------------
/components/Profile/Profile.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Container, View } from "native-base";
3 | import { Image } from "react-native";
4 | import { Text } from "react-native-elements";
5 | import styles from "./styles";
6 |
7 | class ProfileScreen extends Component {
8 | static navigationOptions = {
9 | headerTitle: "Profile"
10 | };
11 |
12 | render() {
13 | const imagePlaceholder = "https://via.placeholder.com/150";
14 |
15 | return (
16 |
17 |
28 |
29 |
37 |
44 | Name:
45 | Name
46 |
47 |
54 | Email:
55 | Email
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
63 | export default ProfileScreen;
64 |
--------------------------------------------------------------------------------
/components/Profile/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from "react-native";
2 |
3 | export default StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | justifyContent: "center",
7 | alignItems: "center",
8 | flexWrap: "wrap",
9 | marginTop: 0,
10 | paddingHorizontal: 20
11 | }
12 | });
13 |
--------------------------------------------------------------------------------
/components/Review/ReviewStars.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { View } from "react-native";
3 | import { Icon } from "react-native-elements";
4 |
5 | class ReviewStars extends Component {
6 | FullStar = key => (
7 |
8 | );
9 |
10 | HalfStar = key => (
11 |
18 | );
19 |
20 | EmptyStar = key => (
21 |
28 | );
29 | render() {
30 | const { stars } = this.props;
31 | let starReviews = [];
32 | for (let i = 1; i <= 5; i++) {
33 | let star = this.FullStar(i);
34 | if (i > stars) {
35 | star = this.EmptyStar(i);
36 | }
37 | starReviews.push(star);
38 | }
39 | return {starReviews};
40 | }
41 | }
42 |
43 | export default ReviewStars;
44 |
--------------------------------------------------------------------------------
/components/SearchBar/SearchBar.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { SearchBar } from "react-native-elements";
3 | import styles from "./styles";
4 | export default class Search extends Component {
5 | render() {
6 | return (
7 |
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/components/SearchBar/styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from "react-native";
2 |
3 | export default StyleSheet.create({
4 | searchContainer: {
5 | borderBottomColor: "#ECECEC",
6 | borderBottomWidth: 2
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject"
9 | },
10 | "dependencies": {
11 | "expo": "~36.0.0",
12 | "native-base": "^2.13.8",
13 | "react": "~16.9.0",
14 | "react-dom": "~16.9.0",
15 | "react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz",
16 | "react-native-dotenv": "^0.2.0",
17 | "react-native-elements": "^1.2.7",
18 | "react-native-gesture-handler": "~1.5.0",
19 | "react-native-maps": "0.26.1",
20 | "react-native-reanimated": "~1.4.0",
21 | "react-native-screens": "2.0.0-alpha.12",
22 | "react-native-web": "~0.11.7",
23 | "react-navigation": "^4.0.10",
24 | "react-navigation-stack": "^1.10.3",
25 | "react-navigation-tabs": "^2.6.2"
26 | },
27 | "devDependencies": {
28 | "babel-preset-expo": "~8.0.0"
29 | },
30 | "private": true
31 | }
32 |
--------------------------------------------------------------------------------