Skip to content

Latest commit

 

History

History
439 lines (377 loc) · 11.4 KB

README.md

File metadata and controls

439 lines (377 loc) · 11.4 KB

Projeto - Aplicação Uper Mobile React Native


Generic badge Build Status Build Status made-for-VSCode npm version Open Source Love svg2


uper_logo


Aplicação Front-end Mobile desenvolvida em React Native para clone do app Uber com integração com a API Google Maps. App é voltado para transporte privado urbano, permite a interação do usuário entre seu local de origem e destino.



uper



🚀 Tecnologias

  • Expo
  • Components
  • react-native-maps
  • Google Maps Platform API
  • Platform
  • react-native-google-places-autocomplete
  • react-native-google-maps-directions
  • Marker
  • react-native-geocoding
  • States
  • PixelRatio
  • styled-components



▶️ Start

  • npm install
  • npm run start / npm start



👊 Como contribuir

  • Dê um fork nesse repositório
  • Crie a sua branch com a feature
    • git checkout -b my-feature
  • Commit a sua contribuição
    • git commit -m 'feat: My feature'
  • Push a sua branch
    • git push origin my-feature



📣 ⬇ Abaixo, as principais estruturas e interface principal:




src/components/Map/index.js

import React, { Component, Fragment } from 'react';
import { View, Image } from 'react-native';
//import { MapView } from 'expo';
import MapView, { Marker } from 'react-native-maps';
import Geocoder from 'react-native-geocoding';

import { getPixelSize } from '../../utils';
import Search from '../Search';
import Directions from '../Directions';
import Details from '../Details';
import markerImage from '../../assets/marker.png';
import backImage from '../../assets/back.png';

import
  {
    Back,
    LocationBox,
    LocationText,
    LocationTimeBox,
    LocationTimeText,
    LocationTimeTextSmall
  } from './styles';

Geocoder.init(" ");

export default class Map extends Component {
  state = {
    region: null,
    destination: null,
    duration: null,
    location: null
  };

  async componentDidMount() {
    // Posição atual do usuário
    navigator.geolocation.getCurrentPosition(
      // Coordenadas da localização do usuário
      async ({ coords: { latitude, longitude } }) => {
        const response = await Geocoder.from({ latitude, longitude });
        const address = response.results[0].formatted_address;
        // endereço mínimo (até a vírgula)
        const location = address.substring(0, address.indexOf(","));

        this.setState({
          location,
          region: {
            latitude,
            longitude,
            latitudeDelta: 0.0143,
            longitudeDelta: 0.0134
          }
        });
      }, //sucesso
      () => {}, //erro
      {
        // Tentar buscar conexão
        timeout: 2000,
        // Localização Gps
        enableHighAccuracy: true,
        // Intervalo busca
        maximumAge: 1000
      }
    );
  }

  handleLocationSelected = (data, { geometry }) => {
    const {
      location: {
        lat: latitude,
        lng: longitude }
    } = geometry;

    this.setState({
      destination: {
        latitude,
        longitude,
        title: data.structured_formatting.main_text
      }
    });
  };

  handleBack = () => {
    this.setState({ destination: null });
  };

  render() {
    const { region, destination, duration, location } = this.state;

    return (
      <View style={{ flex: 1 }}>
        <MapView
          style={{ flex: 1 }}
          region={ region }
          showsUserLocation
          loadingEnabled
          // instância mapView
          ref={el => (this.mapView = el)}
        >
          { destination && (
            <Fragment>
              <Directions
                origin={ region }
                destination={ destination }
                onReady={ result => {
                  this.setState({ duration: Math.floor(result.duration) });

                  //ref mapView
                  this.mapView.fitToCoordinates(result.coordinates, {
                    edgePadding: {
                      right: getPixelSize(50),
                      left: getPixelSize(50),
                      top: getPixelSize(50),
                      bottom: getPixelSize(350)
                    }
                  });
                }}
              />
              {/* Marcador destino */}
              <Marker
                /*
                 * Local do marcador
                 * state  -- destination
                 */
                coordinate={ destination }
                // ancorar marcador no centro(x: 0, y: 0) da imagem quadrada
                anchor={{ x: 0, y: 0 }}
                // ref img
                image={ markerImage }
              >
                <LocationBox>
                  {/*
                    * Label destino
                    * state  -- title
                    */}
                  <LocationText>{ destination.title }</LocationText>
                </LocationBox>
              </Marker>

              {/**
                * Marcador origem
                * state -- region, duration, location
                */}
              <Marker coordinate={ region } anchor={{ x: 0, y: 0 }}>
                <LocationBox>
                  <LocationTimeBox>
                    {/* label duração */}
                    <LocationTimeText>{ duration }</LocationTimeText>
                    <LocationTimeTextSmall>MIN</LocationTimeTextSmall>
                  </LocationTimeBox>
                  {/* label origem */}
                  <LocationText>{ location }</LocationText>
                </LocationBox>
              </Marker>
            </Fragment>
          )}
        </MapView>

        {/* se selecionar voltar */}
        { destination ? (
          <Fragment>
            <Back onPress={ this.handleBack }>
              <Image source={ backImage } />
            </Back>
            <Details />
          </Fragment>
        // senão..
        ) : (
          <Search onLocationSelected={ this.handleLocationSelected } />
        )}
      </View>
    );
  }
};



Interface inicial

0





src/components/Search/index.js

import React, { Component } from "react";
import { Platform } from "react-native";
import { GooglePlacesAutocomplete }
  from "react-native-google-places-autocomplete";

export default class Search extends Component {
  state = {
    searchFocused: false
  };

  render() {
    const { searchFocused } = this.state;
    const { onLocationSelected } = this.props;

    return (
      <GooglePlacesAutocomplete
        placeholder="Para onde?"
        placeholderTextColor="#333"
        onPress={onLocationSelected}
        query={{
          key: " ",
          language: "pt"
        }}
        textInputProps={{
          // Com foco na barra search
          onFocus: () => {
            this.setState({ searchFocused: true });
          },
          // Sem foco na barra search
          onBlur: () => {
            this.setState({ searchFocused: false });
          },
          autoCapitalize: "none",
          autoCorrect: false
        }}
        listViewDisplayed={searchFocused}
        // Trazer detalhes --> latit, longit, tempo percurso...
        fetchDetails
        enablePoweredByContainer={false}
        styles={{
          container: {
            position: "absolute",
            top: Platform.select({ ios: 60, android: 40 }),
            width: "100%"
          },
          textInputContainer: {
            flex: 1,
            backgroundColor: "transparent",
            height: 54,
            marginHorizontal: 20,
            borderTopWidth: 0,
            borderBottomWidth: 0
          },
          textInput: {
            height: 54,
            margin: 0,
            borderRadius: 0,
            paddingTop: 0,
            paddingBottom: 0,
            paddingLeft: 20,
            paddingRight: 20,
            marginTop: 0,
            marginLeft: 0,
            marginRight: 0,
            // sombra android
            elevation: 5,
            // sombra ios
            shadowColor: "#000",
            shadowOpacity: 0.1,
            shadowOffset: { x: 0, y: 0 },
            shadowRadius: 15,
            borderWidth: 1,
            borderColor: "#DDD",
            fontSize: 18
          },
          listView: {
            borderWidth: 1,
            borderColor: "#DDD",
            backgroundColor: "#FFF",
            marginHorizontal: 20,
            elevation: 5,
            shadowColor: "#000",
            shadowOpacity: 0.1,
            shadowOffset: { x: 0, y: 0 },
            shadowRadius: 15,
            marginTop: 10
          },
          description: {
            fontSize: 16
          },
          row: {
            padding: 20,
            height: 58
          }
        }}
      />
    );
  }
};



Interface após a idicação de endereço na barra de busca de destino

1





src/components/Directions/index.js

import React from 'react';
import MapViewDirections from 'react-native-maps-directions';

//props
const Directions = ({ destination, origin, onReady }) => (
  <MapViewDirections
    destination={destination}
    origin={origin}
    onReady={onReady}
    apikey=" "
    strokeWidth={3}
    strokeColor="#222"
  />
);

export default Directions;



src/components/Details/index.js

import React, { Component } from 'react';

import
  {
    Container,
    TypeTitle,
    TypeDescription,
    TypeImage,
    RequestButton,
    RequestButtonText
  } from './styles';

import uberx from '../../assets/uberx.png';

export default class Details extends Component {
  render() {
    return (
      <Container>
        <TypeTitle>Popular</TypeTitle>
        <TypeDescription>Viagens baratas para o dia a dia</TypeDescription>

        <TypeImage source={uberx} />
        <TypeTitle>UperX</TypeTitle>
        <TypeDescription>R$14,00</TypeDescription>

        <RequestButton onPress={() => {}}>
          <RequestButtonText>SOLICITAR UPERX</RequestButtonText>
        </RequestButton>
      </Container>
    );
  }
};



Interface após o redirecionamento entre origem e destino

3