Estilizando as Annotations do Mapbox no React Native

React Native 14 de Ago de 2018

Esse post é a sétima parte da série de posts “Clone AirBnB com AdonisJS, React Native e ReactJS” onde iremos construir do zero uma aplicação web com ReactJS e também uma aplicação mobile com React Native com dados servidos através de uma API REST feita com NodeJS utilizando o framework AdonisJS.

Nesse ponto já temos a aplicação criada e configurada para se comunicar com a API desenvolvida no começo dessa série, e com isso já conseguimos implementar o processo de autenticação e cadastro de usuário, nessa parte iremos criar um Modal para exibição em detalhes das informações previamente cadastradas sobre os imóveis, estilizando as Annotation do Mapbox para que não tenham o comportamento padrão da lib.

React Native na prática - Evento online | Rocketseat
Crie dois projetos mobile para Android e iOS com uma única base de código em duas aulas 100% práticas.

Iniciando a Aplicação

Caso você tenha entrado direto nessa parte, você pode acessar esse link e baixar o projeto com todas as configurações do post anterior, mas é ideal que você leia as partes anteriores para entender o fluxo da aplicação tal como as configurações e instalações feitas de componentes como a Câmera e o Mapbox.

E para que você consiga consultar e também para não ter que adicionar parte por parte manualmente durante esse post, segue o state final dessa etapa como ficou:

state = {
  locations: [],
  newRealty: false,
  cameraModalOpened: false,
  dataModalOpened: false,
  detailsModalOpened: false,
  realtyDetailed: null,
  realtyData: {
    location: {
      latitude: null,
      longitude: null,
    },
    name: '',
    price: '',
    address: '',
    images: [],
  },
};

E antes de começar a implementação do Modal vamos fazer um pequeno ajuste para que pare de aparecer o Warning na tela de listagem dos imóveis no mapa, para isso basta adicionar a seguinte prop no <MapboxGL.PointAnnotation> dentro da função renderLocations:

key={location.id.toString()}
React Native na prática - Evento online | Rocketseat
Crie dois projetos mobile para Android e iOS com uma única base de código em duas aulas 100% práticas.

Criando o Modal de Detalhe do Imóvel

Antes de criarmos o Modal, vamos criar os estilos que serão usados nele, como a lista ficou um pouco extensa deixei ela disponível nesse link, basta você copiar o conteúdo do link para o final do arquivo src/pages/main/styles.js.

Com os estilos criados vamos à criação do componente Modal, e para isso vamos criar outra função antes do método render() chamada renderDetailsModal, seu código vai ficar assim:

renderDetailsModal = () => (
  <Modal
    visible={this.state.detailsModalOpened}
    transparent={false}
    animationType="slide"
    onRequestClose={() => this.handleDetailsModalClose(null)}
  >
    <ModalContainer>
      <DetailsModalFirstDivision>
        <DetailsModalBackButton onPress={() => this.handleDetailsModalClose(null)}>
          Voltar
        </DetailsModalBackButton>
      </DetailsModalFirstDivision>
      <DetailsModalSecondDivision>
        <DetailsModalRealtyTitle>
          {this.state.detailsModalOpened
            ? this.state.locations[this.state.realtyDetailed].title
            : ''
          }
        </DetailsModalRealtyTitle>
        <DetailsModalRealtySubTitle>Casa mobiliada com 3 quartos + quintal</DetailsModalRealtySubTitle>
        <DetailsModalRealtyAddress>
          {this.state.detailsModalOpened
            ? this.state.locations[this.state.realtyDetailed].address
            : ''
          }
        </DetailsModalRealtyAddress>
        { this.renderDetailsImagesList() }
      </DetailsModalSecondDivision>
      <DetailsModalThirdDivision>
        <DetailsModalPrice>R$ {this.state.detailsModalOpened
          ? this.state.locations[this.state.realtyDetailed].price
          : 0
        }</DetailsModalPrice>
      </DetailsModalThirdDivision>
    </ModalContainer>
  </Modal>
)

A estrutura desse Modal é bem parecida com o que vimos nos post anteriores, primeiramente temos uma variável do state para controlar a visibilidade do Modal, depois temos uma função para abri-lo e fechá-lo, e dentro dele temos um Container com 3 divisões, cada um contendo uma parte do Layout da tela de detalhes.

Na primeira parte temos apenas um botão Voltar chamando a função handleDetailsModalClose que controla a exibição do Modal.

Na última parte temos apenas um componente de Texto que exibe o preço do Imóvel selecionado, mas percebam que há uma verificação se a variável que controla a visibilidade do Modal é true, pois caso seja é exibido o preço, caso não, é exibido 0, pois mesmo sem o Modal estar visível ele existe na página, e sem essa tratativa acontece um erro de variável undefined para o preço.

E por fim, na segunda parte temos uma estrutura bem parecida com a terceira parte, onde exibimos o título do imóvel, uma descrição do mesmo e o endereço, exceto a descrição que está estática, os demais textos tem a verificação do valor da variável que controla a visibilidade do Modal, pois todo texto que for dinâmico irá precisar dessa verificação para que não aconteçam erros.

Você deve ter percebido que no Modal há uma lista das imagens do imóvel !? Essa função iremos criar logo na sequência e já te explico o que ela faz 😉

Implementando função para listagem das imagens

Para que sejam listadas corretamente as imagens no Modal de Detalhes do imóvel, crie uma função chamada renderDetailsImagesList logo abaixo da função renderDetailsModal com o código:

renderDetailsImagesList = () => (
  this.state.detailsModalOpened && (
    <ModalImagesList horizontal>
      { this.state.locations[this.state.realtyDetailed].images.map(image => (
        <ModalImageItem
          key={image.id}
          source={{ uri: `http://10.0.3.2:3333/images/${image.path}` }}
          resizeMode="stretch"
        />
      ))}
    </ModalImagesList>
  )
)

Ela basicamente vai verificar se o Modal está aberto, caso esteja ela faz um map no índice do imóvel selecionado dentro do array locations do statecriando para cada imagem um componente Image com a URL obtida.

E já temos a listagem das imagens funcional, agora falta apenas a chamada do Modal quando o usuário clicar na imagem no Mapa, bora lá!?

Obs.: Como eu estava realizando os testes com o Genymotion deixei a URL da imagem como http://10.0.3.2:3333, mas caso esteja testando nos emuladores da máquina pode modificar para uri: image.url.

Configurando as Annotations

Primeiro vamos criar a função que irá controlar a visibilidade do Modal, para isso crie a função chamada handleDetailsModalClose logo abaixo das funções acima, e seu código vai ficar assim:

handleDetailsModalClose = index => this.setState({
  detailsModalOpened: !this.state.detailsModalOpened,
  realtyDetailed: index,
})

Além dela modificar o valor da variável detailsModalOpened ela também vai atribuir um valor para a variável realtyDetailed, que irá guardar o índice do imóvel selecionado.

Você deve ter percebido que no código da função de renderização do Modal em várias partes havia this.state.locations[this.state.realtyDetailed].*, isso porque diferente do processo de cadastro do Imóvel, agora as informações estão dentro de um índice do array locations no state, por isso precisamos saber qual índice devemos buscar essas informações.

Outro ponto é que diferente das outras funções de controle de visibilidade do Modal, essa passamos um parâmetro, esse parâmetro é o índice do imóvel selecionado, por isso na função renderDetailsModal temos a chamada à ela passando null como parâmetro, pois esse valor vai mudar apenas quando clicarmos sobre o imóvel para abrir o Modal, e não quando queremos fechá-lo.

Com essa função criada vamos agora configurar a Annotation para exibir o Modal quando clicada, e não abrir o Callout (pop-up) com o title como abaixo:

E para retirar esse Callout você deve retirar a linha abaixo de dentro do <MapboxGL.PointAnnotation> na função renderLocations:

<MapboxGL.Callout title={location.title} />

Está quase pronto, faltou um detalhe que é a chamada da função handleDetailsModalClose quando o usuário clicar em uma Annotation, e para isso vamos antes fazer um ajuste no map ainda dentro da função renderLocations, que antes estava assim:

this.state.locations.map(location => ( ... )

E vamos modificar para:

this.state.locations.map((location, index) => ( ... )

Assim conseguimos recuperar o índice correto do imóvel selecionado, e por último temos que passar esse índice para a função handleDetailsModalClose, e para chamá-la vamos adicionar a prop onPress no componente <AnnotationText>, ficando assim:

<AnnotationText onPress={() => this.handleDetailsModalClose(index)}>
  {location.price}
</AnnotationText>

Pronto, dessa maneira já atribuímos a abertura do Modal ao clique na Annotation no Mapa e já configuramos o Modal para aparecer estilizando sempre com as informações do imóvel selecionado.

Ufa, acabou!

E é assim que finalizamos a última parte da série do React Native, nesse momento temos uma aplicação completa se comunicando com uma API REST, e implementando Mapas, Câmera, usando diversos recursos como cadastro e listagem de imóveis, customização de elementos presentes no Mapa, Autenticação, JWT e muito mais…

Você pode conferir o código completo da aplicação nesse link.

Espero que tenham gostado 😃 Deixe seu comentário logo abaixo que é muito importante para nós 😉

Marcadores