O que vamos fazer com a Câmera no Ionic (Capacitor)
No Ionic com Capacitor, o acesso à câmera e à galeria é feito pelo plugin @capacitor/camera. Ele permite: (1) capturar uma foto com a câmera, (2) escolher uma imagem da galeria, e (3) receber o resultado em diferentes formatos (URI do arquivo, base64 ou Data URL). A escolha do formato impacta diretamente desempenho, consumo de memória e como você exibe/salva a imagem.
- URI (recomendado): retorna um caminho/URL local para o arquivo. Melhor para performance e para armazenar referência.
- Base64/Data URL: útil para enviar para API rapidamente, mas pode ficar pesado e causar travamentos em imagens grandes.
Instalação do plugin e sincronização
1) Instale o plugin
npm i @capacitor/camera2) Sincronize com as plataformas
npx cap syncSe você já tem o projeto com Android/iOS adicionados, o sync atualiza as dependências nativas. Em seguida, recompile o app no dispositivo/emulador.
Permissões: como funcionam e como tratar
Em geral, o plugin solicita permissões automaticamente quando necessário. Ainda assim, é importante: (1) checar permissões antes de tentar abrir câmera/galeria, (2) lidar com o usuário negando, e (3) oferecer um caminho de recuperação (abrir configurações do app).
Checando e solicitando permissões
import { Camera, CameraPermissionState } from '@capacitor/camera';
async function ensureCameraPermissions() {
const perms = await Camera.checkPermissions();
// perms.camera e perms.photos podem existir dependendo da plataforma
const needsRequest =
perms.camera !== 'granted' ||
(perms.photos && perms.photos !== 'granted');
if (needsRequest) {
const requested = await Camera.requestPermissions({ permissions: ['camera', 'photos'] });
return requested;
}
return perms;
}UX recomendada: antes de pedir permissão, explique em uma mensagem curta por que você precisa dela (ex.: “Precisamos da câmera para você tirar sua foto de perfil”). Se o usuário negar, mostre uma ação para abrir as configurações.
Abrindo configurações do app (quando negado)
import { App } from '@capacitor/app';
async function openAppSettings() {
await App.openSettings();
}Captura e seleção: opções comuns (qualidade, fonte, formato)
A função principal é Camera.getPhoto(). Você define a fonte (câmera ou galeria), a qualidade e o formato de retorno.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
Opções mais usadas
- source:
CameraSource.Camera(captura) ouCameraSource.Photos(galeria) ouCameraSource.Prompt(pergunta ao usuário). - quality: 0 a 100. Valores entre 60 e 85 costumam equilibrar bem.
- resultType:
CameraResultType.Uri(recomendado),Base64ouDataUrl. - allowEditing: permite edição/corte em alguns dispositivos (comportamento varia).
- width/height: redimensiona para reduzir peso (ótimo para avatares).
Exemplo: pedir ao usuário câmera ou galeria e retornar URI
import {
Camera,
CameraResultType,
CameraSource,
Photo
} from '@capacitor/camera';
async function pickOrTakePhoto(): Promise<Photo> {
return Camera.getPhoto({
source: CameraSource.Prompt,
quality: 80,
resultType: CameraResultType.Uri,
allowEditing: false
});
}Exibindo a imagem no app (URI, base64 e WebView)
Para exibir a imagem em um <img>, você precisa de uma string que o WebView consiga renderizar. Com resultType: Uri, o retorno traz propriedades como webPath (muito útil no Ionic) e path (caminho nativo).
Template (Angular) com preview
<ion-content class="ion-padding">
<ion-button expand="block" (click)="onSelectPhoto()">
Escolher/Tirar foto
</ion-button>
<ion-card *ngIf="photoWebPath">
<img [src]="photoWebPath" alt="Prévia" />
<ion-card-content>
<p>Prévia da imagem selecionada.</p>
</ion-card-content>
</ion-card>
</ion-content>Componente (TypeScript) usando webPath
import { Component } from '@angular/core';
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
@Component({
selector: 'app-photo',
templateUrl: './photo.page.html'
})
export class PhotoPage {
photoWebPath?: string;
photoPath?: string;
async onSelectPhoto() {
try {
const photo: Photo = await Camera.getPhoto({
source: CameraSource.Prompt,
quality: 80,
resultType: CameraResultType.Uri
});
// webPath é ideal para exibir no WebView
this.photoWebPath = photo.webPath ?? undefined;
// path pode ser útil para persistência nativa (quando disponível)
this.photoPath = photo.path ?? undefined;
} catch (err) {
// Tratamento detalhado mais abaixo
console.error('Falha ao obter foto', err);
}
}
}Dica prática: se você só precisa mostrar a imagem na tela, use webPath. Se você precisa guardar referência local para reabrir depois, avalie salvar o arquivo e guardar um caminho estável (ver seção de persistência).
Quando usar Base64/Data URL (e cuidados de performance)
Base64 ou Data URL pode ser conveniente para enviar a imagem em um JSON para uma API, mas aumenta o tamanho em ~33% e pode consumir muita memória. Se optar por base64, prefira reduzir quality e definir width/height.
Exemplo: obter base64 para upload
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
async function getPhotoBase64() {
const photo = await Camera.getPhoto({
source: CameraSource.Photos,
quality: 70,
width: 1024,
resultType: CameraResultType.Base64
});
// photo.base64String contém apenas o base64 (sem prefixo data:)
return photo.base64String;
}Exemplo: Data URL direto para preview
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
async function getPhotoDataUrlForPreview() {
const photo = await Camera.getPhoto({
source: CameraSource.Camera,
quality: 70,
resultType: CameraResultType.DataUrl
});
return photo.dataUrl; // já vem no formato data:image/...;base64,...
}Armazenando referência local (quando necessário)
Se você precisa que a foto continue disponível após fechar o app, o ideal é salvar uma cópia em um diretório do app e guardar apenas a referência (caminho/identificador). Em muitos casos, a URI retornada pela galeria pode mudar ou ficar inacessível dependendo de permissões e limpeza do sistema.
Uma abordagem comum é: (1) obter a foto como URI, (2) baixar os bytes via fetch(photo.webPath), (3) escrever em Filesystem e (4) guardar o caminho salvo.
Salvando no Filesystem do app
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { Filesystem, Directory } from '@capacitor/filesystem';
async function savePhotoToAppDirectory() {
const photo = await Camera.getPhoto({
source: CameraSource.Prompt,
quality: 80,
resultType: CameraResultType.Uri
});
if (!photo.webPath) throw new Error('webPath não disponível para salvar');
const response = await fetch(photo.webPath);
const blob = await response.blob();
const base64 = await blobToBase64(blob);
const fileName = `photo_${Date.now()}.jpeg`;
const saved = await Filesystem.writeFile({
path: fileName,
data: base64,
directory: Directory.Data
});
// saved.uri é uma URI do Filesystem
return { fileName, uri: saved.uri };
}
function blobToBase64(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onerror = reject;
reader.onload = () => {
const dataUrl = reader.result as string;
// remove o prefixo "data:*/*;base64,"
resolve(dataUrl.split(',')[1]);
};
reader.readAsDataURL(blob);
});
}Depois de salvar, você pode persistir fileName ou saved.uri no armazenamento local e reconstruir a exibição quando necessário.
Exibindo um arquivo salvo
Para exibir um arquivo salvo no Filesystem, você pode ler o arquivo e montar um Data URL (simples, mas pode ser pesado), ou converter a URI para algo exibível no WebView. Uma forma direta para casos pequenos (ex.: avatar) é ler e montar Data URL:
import { Filesystem, Directory } from '@capacitor/filesystem';
async function loadSavedPhotoAsDataUrl(fileName: string) {
const file = await Filesystem.readFile({
path: fileName,
directory: Directory.Data
});
// file.data é base64
return `data:image/jpeg;base64,${file.data}`;
}Tratamento de erro e cancelamento pelo usuário (UX adequada)
Ao chamar Camera.getPhoto(), o usuário pode cancelar o fluxo (fechar câmera, voltar, cancelar seletor). Isso não deve ser tratado como erro “grave”: o app deve simplesmente manter o estado anterior e, se necessário, mostrar uma mensagem discreta.
Padrão de tratamento: loading, cancelamento e falhas
import { Component } from '@angular/core';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { ToastController, LoadingController } from '@ionic/angular';
@Component({
selector: 'app-photo',
templateUrl: './photo.page.html'
})
export class PhotoPage {
photoWebPath?: string;
constructor(
private toastCtrl: ToastController,
private loadingCtrl: LoadingController
) {}
async onSelectPhoto() {
const loading = await this.loadingCtrl.create({ message: 'Abrindo câmera/galeria...' });
await loading.present();
try {
const photo = await Camera.getPhoto({
source: CameraSource.Prompt,
quality: 80,
resultType: CameraResultType.Uri
});
this.photoWebPath = photo.webPath ?? undefined;
} catch (err: any) {
// Cancelamento costuma cair aqui em algumas plataformas/versões
const msg = (err?.message || '').toLowerCase();
const isCancel = msg.includes('cancel') || msg.includes('canceled') || msg.includes('user cancelled');
if (!isCancel) {
const toast = await this.toastCtrl.create({
message: 'Não foi possível obter a foto. Verifique permissões e tente novamente.',
duration: 2500,
position: 'bottom'
});
await toast.present();
console.error('Erro ao obter foto:', err);
}
// Se cancelou, não faz nada: mantém UX limpa
} finally {
await loading.dismiss();
}
}
}Boas práticas de UX para câmera/galeria
- Evite loops de permissão: se o usuário negar permanentemente, mostre uma explicação e um botão “Abrir configurações”.
- Mostre feedback: use
ion-loadingao abrir o fluxo e ao processar/salvar a imagem. - Prévia e ação clara: após selecionar, mostre preview e botões como “Usar esta foto” e “Trocar”.
- Reduza tamanho: para avatar, use
width/qualitymenores para evitar travamentos.
Resumo rápido: qual formato escolher?
| Objetivo | Configuração sugerida | Por quê |
|---|---|---|
| Exibir preview na tela | resultType: Uri e usar photo.webPath | Leve e direto no WebView |
| Enviar para API (simples) | resultType: Base64 (com quality/width) | Fácil de embutir no payload |
| Persistir localmente | resultType: Uri + salvar cópia no Filesystem | Referência estável e controle do app |