📚 Módulo 11: Mobile - Integração Nativa (Geolocalização, Preferences) e Design Responsivo
Com a base móvel estabelecida no Módulo 10, expandiremos nossa aplicação híbrida TecLoja 02 integrando recursos de hardware do dispositivo Android através de Plugins do Capacitor.
Neste módulo, implementaremos:
- Armazenamento persistente de tokens JWT e sessões usando o plugin Preferences (substituto seguro do
localStorageno ecossistema web móvel). - Detecção da localização física do usuário via sensor de GPS (Geolocation) para cálculo dinâmico e simulado de frete na tela do carrinho.
- Otimização de design reativo com foco em Mobile First, garantindo usabilidade ergonômica para interações de toque.
🗺️ 1. Fluxo de Acesso ao Hardware do Dispositivo
Abaixo, descrevemos como o fluxo de permissões e as chamadas JavaScript do Vue interagem com o hardware físico do smartphone:
sequenceDiagram
participant V as Vue Component (Composables)
participant C as Capacitor Native Bridge
participant A as Android OS (Permissions / Hardware)
V->>C: requestPermissions() [Geolocation]
C->>A: Solicitar acesso ao GPS do aparelho
A-->>C: Permissão Concedida pelo usuário
C-->>V: Retornar status "granted"
V->>C: getCurrentPosition()
C->>A: Consultar satélites/redes de localização
A-->>C: Objeto com Lat/Long
C-->>V: Retornar coordenadas geográficas
Note over V: Atualizar estado do frete na tela do Carrinho
💾 2. Armazenamento Seguro de Tokens com @capacitor/preferences
No ecossistema móvel, o clássico localStorage pode ser deletado pelo sistema operacional caso o dispositivo fique sem espaço em disco. Para resolver isso, utilizaremos o @capacitor/preferences, que encapsula chaves-valores persistentes no Android via SharedPreferences.
Instalação
Na pasta raiz do seu repositório frontend, rode:
npm install @capacitor/preferences
npx cap sync
Implementação: Composable de Autenticação Atualizado
Abra seu arquivo de autenticação reativa (src/composables/useAuth.ts ou equivalente) e atualize a persistência para utilizar Promises assíncronas do Preferences:
import { ref } from 'vue';
import { Preferences } from '@capacitor/preferences';
const token = ref<string | null>(null);
export function useAuth() {
// Salvar token de forma nativa e assíncrona
const saveToken = async (newToken: string) => {
token.value = newToken;
await Preferences.set({
key: 'auth_token',
value: newToken
});
};
// Carregar token no boot do aplicativo
const loadToken = async () => {
const { value } = await Preferences.get({ key: 'auth_token' });
token.value = value;
return value;
};
// Remover token na saída (logout)
const clearToken = async () => {
token.value = null;
await Preferences.remove({ key: 'auth_token' });
};
return {
token,
saveToken,
loadToken,
clearToken
};
}
📍 3. Coleta de Localização com @capacitor/geolocation
Para adicionar um calculador automático de frete na tela de checkout, consultaremos a geolocalização física do aluno.
Instalação
npm install @capacitor/geolocation
npx cap sync
Configurando Permissões do Android
Para ter acesso aos sensores do celular, precisamos declarar as permissões no arquivo manifest do Android. Abra o arquivo android/app/src/main/AndroidManifest.xml e certifique-se de adicionar as seguintes tags dentro do nó <manifest>:
<!-- Permissões de Localização Fina (GPS) e Coarse (Rede) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location.gps" />
Implementação na Tela do Carrinho (Carrinho.vue)
No script do seu componente Vue do carrinho, adicione a lógica de captura de coordenadas:
<script setup lang="ts">
import { ref } from 'vue';
import { Geolocation } from '@capacitor/geolocation';
const latitude = ref<number | null>(null);
const longitude = ref<number | null>(null);
const freteCalculado = ref<number | null>(null);
const carregandoLocalizacao = ref(false);
const obterLocalizacaoFrete = async () => {
try {
carregandoLocalizacao.value = true;
// Solicitar permissão nativa antes de ler o hardware
const statusPermissao = await Geolocation.requestPermissions();
if (statusPermissao.location === 'granted') {
const coordenadas = await Geolocation.getCurrentPosition();
latitude.value = coordenadas.coords.latitude;
longitude.value = coordenadas.coords.longitude;
// Simulação didática de frete baseado na latitude do usuário
freteCalculado.value = Math.abs(coordenadas.coords.latitude) * 1.5;
} else {
alert("Permissão de localização negada pelo usuário.");
}
} catch (error) {
console.error("Erro ao obter geolocalização:", error);
} finally {
carregandoLocalizacao.value = false;
}
};
</script>
<template>
<div class="carrinho-container">
<h2>Resumo da Compra</h2>
<div class="frete-secao">
<button @click="obterLocalizacaoFrete" :disabled="carregandoLocalizacao">
{{ carregandoLocalizacao ? 'Buscando GPS...' : '📍 Calcular Frete via GPS' }}
</button>
<div v-if="latitude && longitude" class="coordenadas-preview">
<p>Lat: {{ latitude.toFixed(4) }} | Lng: {{ longitude.toFixed(4) }}</p>
<p class="frete-valor">Valor do Frete: R$ {{ freteCalculado?.toFixed(2) }}</p>
</div>
</div>
</div>
</template>
🎨 4. Layout Otimizado para Mobile First (Ergonomia)
Para que o aplicativo pareça nativo, devemos ajustar nossos estilos no arquivo CSS global (styles.css ou index.css). Adicione regras específicas utilizando Media Queries e propriedades que eliminam comportamento de browser clássico:
/* Impedir zoom indesejado ao dar duplo clique no celular */
html, body {
touch-action: manipulation;
user-select: none;
-webkit-user-select: none;
background-color: #0f172a; /* Slate 900 */
}
/* Área de toque mínima recomendada pelo Guia de Acessibilidade (48px x 48px) */
button, a, input, select {
min-height: 48px;
min-width: 48px;
padding: 12px 16px;
}
/* Ajustes Responsivos para Telas de Smartphones */
@media (max-width: 640px) {
/* Grid do catálogo passa para 1 ou 2 colunas no celular */
.grid-produtos {
grid-template-columns: repeat(2, 1fr) !important;
gap: 8px !important;
padding: 8px;
}
/* Ajustes ergonômicos no menu inferior fixo estilo app de celular */
.navbar {
position: fixed;
bottom: 0;
top: auto;
left: 0;
width: 100%;
z-index: 1000;
box-shadow: 0 -4px 10px rgba(0,0,0,0.3);
border-radius: 16px 16px 0 0;
}
/* Margem inferior para o conteúdo não ficar escondido pela Navbar na parte inferior */
main {
margin-bottom: 80px;
}
}
✅ Pré-Requisitos deste Módulo
Antes de prosseguir, confirme se:
- A sincronização do Capacitor (
npx cap sync) está ocorrendo sem erros. - Você testou o carregamento da aplicação nativa com o console do desenvolvedor ativo para validar erros de carregamento assíncrono.
🤔 Por que fizemos assim?
- Por que usar
Preferencese nãolocalStorage? OlocalStoragedo navegador é uma implementação volátil. Em dispositivos móveis, quando o sistema fica com pouca memória ram ou de armazenamento, o sistema operacional do celular limpa agressivamente dados em cache do navegador para liberar espaço. Usar o pluginPreferencesforça o salvamento do token JWT no armazenamento nativo do celular, impedindo que o aluno perca o login toda vez que o app for reiniciado. - Por que solicitar permissões de forma programática? Ao contrário de sites web onde as permissões podem ser solicitadas na abertura da página, aplicativos de lojas exigem transparência (Privacidade por Design). Solicitar permissão de GPS somente no clique do botão “Calcular Frete” melhora a experiência do usuário (UX) e atende aos requisitos de privacidade da Google Play Store.
🔍 Checkpoint
- Persistência do Token: Faça login na aplicação nativa pelo emulador. Feche o aplicativo (force close) e abra-o novamente. A sessão deve ser mantida utilizando o token recuperado do
Preferences. - Validação de Coordenadas: Clique em “Calcular Frete via GPS” e verifique se o emulador exibe o pop-up nativo do Android solicitando permissão de localização. Aceite a permissão e verifique o frete calculado.
⚠️ Erros Comuns
| Erro | Causa | Solução |
|---|---|---|
Location permission not granted no console do emulador |
As permissões de localização foram omitidas no arquivo AndroidManifest.xml ou o usuário clicou em “Negar”. |
Verifique se as tags <uses-permission> foram inseridas no manifesto e reinicie o emulador limpando os dados de permissão do app nas configurações do Android. |
| O emulador não consegue obter as coordenadas de GPS | O emulador de Android nativo inicia com o sensor de geolocalização inativo ou sem pontos de coordenadas mockadas. | Vá no painel lateral do emulador (três pontos ...), clique na guia Location e informe uma coordenada de latitude e longitude de teste, clicando em Send. |
| A barra de navegação superior encobre a barra de status do celular | Ausência de tratamento para as chamadas “Safe Areas” dos celulares com notch. | Instale o plugin @capacitor/status-bar para gerenciar a cor de fundo e sobreposição da status bar, ou adicione padding-top: env(safe-area-inset-top) no seu arquivo de layout principal do app. |