O que é geolocalização no Ionic com Capacitor
Geolocalização é a capacidade do app obter a posição aproximada do dispositivo (latitude/longitude) usando fontes como GPS, Wi‑Fi e rede celular. No Ionic com Capacitor, o acesso é feito via plugin @capacitor/geolocation, que abstrai as diferenças entre Android, iOS e Web.
Em apps móveis, geolocalização envolve três pontos essenciais: permissões (o usuário precisa autorizar), estratégia de obtenção (uma leitura única vs. monitoramento contínuo) e tratamento de falhas (GPS desligado, permissão negada, timeout, indisponibilidade).
Localização única vs. monitoramento (watch)
- Localização única (
getCurrentPosition): pega uma coordenada “agora” e encerra. É a melhor opção para a maioria dos casos (ex.: preencher endereço, marcar ponto no mapa, calcular distância pontual). - Monitoramento (
watchPosition): fica recebendo atualizações conforme o usuário se move. Útil para navegação, tracking, corrida/ciclismo, entregas em tempo real.
Impacto em bateria e precisão
Quanto maior a precisão, maior o consumo de bateria. Em geral:
- Alta precisão (
enableHighAccuracy: true) tende a acionar GPS com mais frequência: melhor para navegação, pior para bateria. - Baixa/média precisão pode usar rede/Wi‑Fi: suficiente para “aproximado”, menor consumo.
- Monitoramento contínuo é o que mais consome bateria, especialmente com alta precisão e intervalos curtos.
Instalação e permissões
1) Instalar o plugin
npm i @capacitor/geolocationEm seguida, sincronize com as plataformas (quando estiver usando Android/iOS):
npx cap sync2) Configurar permissões (visão prática)
O Capacitor cuida de boa parte, mas você precisa garantir que o app declare permissões no projeto nativo.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
- Android: normalmente envolve permissões como
ACCESS_COARSE_LOCATIONe/ouACCESS_FINE_LOCATIONnoAndroidManifest.xml. Para monitoramento em segundo plano, há exigências adicionais (não recomendado para iniciantes sem necessidade real). - iOS: é necessário declarar mensagens de uso no
Info.plist(ex.: “Precisamos da sua localização para mostrar sua posição no mapa”). Sem isso, o app pode falhar ao solicitar permissão.
Mesmo com declarações corretas, o usuário pode negar. Por isso, o app deve sempre ter fallback e mensagens claras.
Passo a passo: obter coordenadas com fallback e exibir na tela
O objetivo aqui é: (1) solicitar permissão, (2) tentar obter a posição, (3) lidar com erros comuns, (4) exibir latitude/longitude e precisão.
1) Serviço de geolocalização (recomendado para organizar)
Crie um serviço para centralizar permissões, leitura única e monitoramento. Exemplo:
import { Injectable } from '@angular/core';
import { Geolocation, Position, PermissionStatus } from '@capacitor/geolocation';
export type LocationResult =
| { ok: true; position: Position }
| { ok: false; reason: 'permission-denied' | 'location-unavailable' | 'timeout' | 'gps-disabled' | 'unknown'; message: string };
@Injectable({ providedIn: 'root' })
export class LocationService {
async ensurePermission(): Promise<PermissionStatus> {
// Checa status atual
const status = await Geolocation.checkPermissions();
// Se ainda não concedido, solicita
if (status.location !== 'granted') {
return Geolocation.requestPermissions();
}
return status;
}
async getOnce(options?: { highAccuracy?: boolean; timeoutMs?: number }): Promise<LocationResult> {
try {
const perm = await this.ensurePermission();
if (perm.location !== 'granted') {
return {
ok: false,
reason: 'permission-denied',
message: 'Permissão de localização negada. Ative nas configurações do sistema.'
};
}
const position = await Geolocation.getCurrentPosition({
enableHighAccuracy: options?.highAccuracy ?? true,
timeout: options?.timeoutMs ?? 10000,
maximumAge: 0
});
return { ok: true, position };
} catch (err: any) {
// Mapeamento simples de erros comuns
const msg = String(err?.message ?? err);
// Em alguns ambientes, códigos podem existir (ex.: 1,2,3 na Web)
const code = err?.code;
if (code === 1) {
return { ok: false, reason: 'permission-denied', message: 'Permissão negada pelo usuário.' };
}
if (code === 2) {
return { ok: false, reason: 'location-unavailable', message: 'Localização indisponível. Verifique GPS/rede.' };
}
if (code === 3) {
return { ok: false, reason: 'timeout', message: 'Tempo esgotado ao obter localização. Tente novamente.' };
}
// Heurística para GPS desativado (mensagens variam por plataforma)
if (msg.toLowerCase().includes('gps') || msg.toLowerCase().includes('location services')) {
return { ok: false, reason: 'gps-disabled', message: 'Serviços de localização parecem estar desativados.' };
}
return { ok: false, reason: 'unknown', message: 'Falha ao obter localização. Tente novamente.' };
}
}
async startWatch(onUpdate: (pos: Position) => void, onError?: (e: any) => void) {
const perm = await this.ensurePermission();
if (perm.location !== 'granted') {
throw new Error('Permissão de localização não concedida.');
}
const watchId = await Geolocation.watchPosition(
{ enableHighAccuracy: true, maximumAge: 0, timeout: 10000 },
(position, err) => {
if (err) {
onError?.(err);
return;
}
if (position) onUpdate(position);
}
);
return watchId;
}
async stopWatch(watchId: string) {
await Geolocation.clearWatch({ id: watchId });
}
}2) Página: mostrar coordenadas e botões de ação
Agora, uma página simples com botões para “Obter localização” e “Iniciar/Parar monitoramento”. A UI pode ser minimalista e focada no comportamento.
import { Component, OnDestroy } from '@angular/core';
import { Position } from '@capacitor/geolocation';
import { LocationService } from '../services/location.service';
@Component({
selector: 'app-location',
templateUrl: './location.page.html'
})
export class LocationPage implements OnDestroy {
loading = false;
errorMsg = '';
position?: Position;
watching = false;
watchId?: string;
constructor(private locationService: LocationService) {}
async getLocationOnce() {
this.loading = true;
this.errorMsg = '';
const result = await this.locationService.getOnce({ highAccuracy: true, timeoutMs: 10000 });
this.loading = false;
if (!result.ok) {
this.position = undefined;
this.errorMsg = result.message;
return;
}
this.position = result.position;
}
async toggleWatch() {
this.errorMsg = '';
if (this.watching && this.watchId) {
await this.locationService.stopWatch(this.watchId);
this.watching = false;
this.watchId = undefined;
return;
}
try {
this.watching = true;
this.watchId = await this.locationService.startWatch(
(pos) => (this.position = pos),
(err) => (this.errorMsg = 'Erro no monitoramento: ' + (err?.message ?? err))
);
} catch (e: any) {
this.watching = false;
this.watchId = undefined;
this.errorMsg = e?.message ?? 'Não foi possível iniciar o monitoramento.';
}
}
ngOnDestroy() {
if (this.watchId) {
this.locationService.stopWatch(this.watchId);
}
}
}Template (exemplo) exibindo coordenadas, precisão e timestamp:
<ion-header>
<ion-toolbar>
<ion-title>Localização</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button expand="block" (click)="getLocationOnce()" [disabled]="loading">
Obter localização (uma vez)
</ion-button>
<ion-button expand="block" color="secondary" (click)="toggleWatch()">
{{ watching ? 'Parar monitoramento' : 'Iniciar monitoramento' }}
</ion-button>
<ion-spinner *ngIf="loading"></ion-spinner>
<ion-card *ngIf="position">
<ion-card-header>
<ion-card-title>Coordenadas</ion-card-title>
</ion-card-header>
<ion-card-content>
<p><strong>Latitude:</strong> {{ position.coords.latitude }}</p>
<p><strong>Longitude:</strong> {{ position.coords.longitude }}</p>
<p><strong>Precisão (m):</strong> {{ position.coords.accuracy }}</p>
<p><strong>Timestamp:</strong> {{ position.timestamp }}</p>
</ion-card-content>
</ion-card>
<ion-item *ngIf="errorMsg" lines="none">
<ion-label color="danger">{{ errorMsg }}</ion-label>
</ion-item>
</ion-content>3) Estratégias de fallback (quando falha)
Fallback é o plano B para manter o app útil quando a localização não vem. Algumas estratégias práticas:
- Permissão negada: exibir mensagem com ação “Abrir configurações” (em muitos casos você orienta o usuário; abrir configurações pode depender de plugin adicional). Também ofereça alternativa manual (ex.: campo para digitar cidade/CEP).
- Timeout: permitir “Tentar novamente” e, opcionalmente, repetir com
enableHighAccuracy: falsepara obter uma posição aproximada mais rápido. - GPS desativado / localização indisponível: orientar o usuário a ativar “Serviços de localização” e testar em área aberta. Em ambiente interno, a precisão pode piorar.
Exemplo de retry com menor precisão:
const firstTry = await this.locationService.getOnce({ highAccuracy: true, timeoutMs: 8000 });
if (!firstTry.ok) {
const fallbackTry = await this.locationService.getOnce({ highAccuracy: false, timeoutMs: 8000 });
// Use fallbackTry se ok, senão mostre erro
}Integração com mapa: opções e exemplo simples
Existem duas abordagens comuns para mostrar a posição em um mapa:
- Mapa via WebView (iframe/URL): simples para protótipos e visualização rápida. Depende de conectividade e de políticas do provedor do mapa.
- Biblioteca de mapas (ex.: Leaflet): mais controle (marcadores, camadas, eventos), mas exige configuração de assets e cuidado com tamanho/resize no mobile.
Opção A (conceitual): abrir mapa externo com coordenadas
Quando você só precisa “ver no mapa”, pode abrir um link com as coordenadas (ex.: Google Maps). Isso evita embutir mapa no app, mas tira o usuário do fluxo.
const lat = this.position?.coords.latitude;
const lng = this.position?.coords.longitude;
const url = `https://www.google.com/maps?q=${lat},${lng}`;
window.open(url, '_blank');Opção B (componente simples): iframe com OpenStreetMap
Para uma visualização embutida e simples, você pode usar um iframe com uma URL do OpenStreetMap. É uma solução prática para demonstrar o conceito, mas não é a mais flexível para apps complexos.
Exemplo de template (renderiza quando houver posição):
<div *ngIf="position" style="height: 320px; border-radius: 12px; overflow: hidden;">
<iframe
width="100%"
height="320"
[src]="mapUrl"
style="border:0;"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade">
</iframe>
</div>Para gerar mapUrl com segurança no Angular, use DomSanitizer:
import { Component } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { Position } from '@capacitor/geolocation';
export class LocationPage {
position?: Position;
mapUrl?: SafeResourceUrl;
constructor(private sanitizer: DomSanitizer) {}
updateMapUrl() {
if (!this.position) return;
const lat = this.position.coords.latitude;
const lng = this.position.coords.longitude;
// bbox simples ao redor do ponto
const delta = 0.01;
const left = lng - delta;
const right = lng + delta;
const top = lat + delta;
const bottom = lat - delta;
const url = `https://www.openstreetmap.org/export/embed.html?bbox=${left}%2C${bottom}%2C${right}%2C${top}&layer=mapnik&marker=${lat}%2C${lng}`;
this.mapUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}Chame updateMapUrl() sempre que position for atualizada (na leitura única e no monitoramento).
Tratamento de erros: cenários comuns e respostas do app
| Cenário | Sintoma | Resposta recomendada |
|---|---|---|
| Permissão negada | Erro imediato ao solicitar/obter posição | Explicar por que precisa, oferecer alternativa manual, orientar a habilitar nas configurações |
| GPS/serviços de localização desligados | Localização indisponível ou erro do sistema | Pedir para ativar serviços de localização e tentar novamente; sugerir ir para área aberta |
| Timeout | Demora e falha | Botão “Tentar novamente”; fallback com baixa precisão; aumentar timeout com parcimônia |
| Sem internet (para mapa embutido) | Mapa não carrega | Mostrar coordenadas mesmo assim; exibir placeholder e opção de abrir mapa quando online |
| Monitoramento esquecido | Bateria drenando | Parar watch ao sair da tela; oferecer toggle claro; evitar watch sem necessidade |
Boas práticas essenciais
- Peça permissão no momento certo: solicite quando o usuário acionar uma função que depende de localização, não ao abrir o app sem contexto.
- Prefira leitura única quando possível; use
watchPositionapenas quando houver valor real em atualizações contínuas. - Desligue o monitoramento ao sair da tela (
ngOnDestroy) e também quando o usuário não precisar mais. - Mostre estado e feedback: loading, erro e última posição conhecida (se fizer sentido) melhoram a UX.
- Evite alta precisão por padrão se o caso de uso tolerar aproximação; isso reduz consumo e melhora tempo de resposta.
- Trate mapa como dependência opcional: o app deve continuar útil exibindo coordenadas mesmo sem mapa.