1. Conceito e Arquitetura "Capacitor Bridge"
Como o código JavaScript da WebView consegue acionar a lente e o sensor físico do dispositivo Android/iOS.
A Sandbox da WebView
Por razões de segurança, o navegador web tradicional roda isolado (Sandbox). Ele não possui permissão direta para interagir com o hardware do telefone sem uma mediação do Sistema Operacional.
Capacitor Bridge
O plugin @capacitor/camera expõe métodos em JS que convertem comandos da web para chamadas nativas em Java/Kotlin (Android) ou Swift (iOS).
Hardware Nativo
O aplicativo padrão de câmera do Android toma o controle da interface, tira a foto em resolução máxima, aplica pós-processamento e devolve o arquivo seguro via URI para a WebView.
Fluxo de Execução Completo da Câmera Nativa
Clique do Usuário
Dispara evento de clique no botão React onClick.
Chamada Capacitor
Executa Camera.getPhoto({...}) com parâmetros configurados.
Hardware & OS
Android solicita permissão, abre app de câmera e grava foto em arquivo temporário.
Render na WebView
Retorna image.webPath que é injetado na tag <img>.
Matriz de Decisão: Uri vs. Base64 vs. DataUrl
| Modo (CameraResultType) | O que retorna? | Performance | Uso Recomendado |
|---|---|---|---|
| CameraResultType.Uri | URL local segura (capacitor://... ou http://...) |
Alta (Ultrarrápido) | Exibição direta na interface da WebView (melhor opção geral). |
| CameraResultType.Base64 | String de texto codificada gigante contendo os bytes da imagem. | Média/Baixa (Consome RAM) | Envio direto para APIs de banco de dados (JSON payloads) ou Supabase. |
| CameraResultType.DataUrl | Base64 pré-formatado com prefixo MIME (data:image/jpeg;base64,...) |
Média | Testes rápidos e renderização inline sem conversão prévia. |
2. Guia Passo a Passo (Passo a Passo em Sala)
Siga estes 4 passos simples com sua turma para configurar o projeto completo do zero.
Instalação e Sincronização de Dependências
No terminal integrado do VS Code (na raiz do projeto React + Capacitor), rode os seguintes comandos:
# 1. Instala o plugin oficial da câmera
npm install @capacitor/camera
# 2. Copia o plugin compilado para a pasta nativa do Android
npx cap sync android
Importante: Sempre execute 'npx cap sync' após instalar qualquer novo plugin Capacitor!
Configuração do AndroidManifest.xml (Permissões)
Abra o arquivo localizado no diretório nativo do Android: android/app/src/main/AndroidManifest.xml e adicione as permissões de acesso ao hardware:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissões para liberar uso da Câmera e Galeria -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<application ...>
...
</application>
</manifest>
Criação do Componente React e Função Async
No seu componente React (ex: CameraView.jsx), importe a biblioteca e utilize um estado para armazenar o link da imagem:
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
export default function CameraView() {
const [fotoUri, setFotoUri] = useState(null);
const capturarFoto = async () => {
try {
const image = await Camera.getPhoto({
quality: 90,
allowEditing: false,
resultType: CameraResultType.Uri,
source: CameraSource.Prompt // Abre modal para escolher Câmera ou Fotos
});
// Atualiza o estado do React com o caminho seguro webPath
setFotoUri(image.webPath);
} catch (error) {
console.warn("Captura cancelada pelo usuário ou falha:", error);
}
};
// ... JSX de renderização ...
}
Testando no Smartphone Físico
Conecte seu celular Android via cabo USB, ative o modo Depuração USB nas opções de desenvolvedor e execute o deploy nativo:
$ npx cap run android
O Android Studio ou a CLI do Capacitor compilará o APK e instalará diretamente no dispositivo conectado.
3. Código Exemplo Completo (React + Capacitor)
Copie o arquivo integral abaixo para utilizar na demonstração em tempo real com os alunos.
import React, { useState } from 'react';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
/**
* Componente Demonstrativo de Aula - Integração Câmera Nativa
* Disciplina: Programação para Dispositivos Móveis II
*/
export default function NativeCameraModule() {
const [fotoUri, setFotoUri] = useState(null);
const [carregando, setCarregando] = useState(false);
const [mensagemStatus, setMensagemStatus] = useState('');
// Função principal para invocar a câmera física ou galeria
const capturarFotoOuGaleria = async () => {
setCarregando(true);
setMensagemStatus('');
try {
// 1. Invocação da API Nativa do Capacitor
const image = await Camera.getPhoto({
quality: 90,
allowEditing: true, // Permite recortar foto nativamente
resultType: CameraResultType.Uri, // Retorna caminho de arquivo seguro
source: CameraSource.Prompt, // Prompt: Pergunta se quer Câmera ou Fotos
promptLabelHeader: 'Selecione a Origem',
promptLabelPhoto: 'Escolher da Galeria',
promptLabelPicture: 'Tirar Foto com Câmera'
});
// 2. image.webPath é a URI pronta para ser exibida no atributo src do React
if (image && image.webPath) {
setFotoUri(image.webPath);
setMensagemStatus('Foto capturada com sucesso!');
}
} catch (error) {
console.warn('Operação cancelada ou indisponível no navegador:', error);
setMensagemStatus('Captura cancelada ou não suportada neste ambiente.');
} finally {
setCarregando(false);
}
};
// Limpar a foto capturada
const descartarFoto = () => {
setFotoUri(null);
setMensagemStatus('Foto descartada.');
};
return (
<div className="max-w-md mx-auto my-8 p-6 bg-slate-50 border border-slate-200 rounded-2xl shadow-lg font-sans">
<div className="text-center mb-6">
<span className="text-xs font-bold uppercase tracking-wider text-red-600 bg-red-100 px-3 py-1 rounded-full">
Capacitor Hardware API
</span>
<h2 className="text-2xl font-black text-slate-900 mt-2">Captura Nativa</h2>
<p className="text-xs text-slate-500 mt-1">Integração de Câmera e Galeria com React</p>
</div>
{/* Área de Exibição da Imagem (Preview) */}
<div className="relative aspect-square w-full bg-slate-200 border-2 border-dashed border-slate-300 rounded-xl overflow-hidden flex items-center justify-center mb-6">
{fotoUri ? (
<img
src={fotoUri}
alt="Captura do Usuário"
className="w-full h-full object-cover rounded-xl transition duration-300"
/>
) : (
<div className="text-center p-6 text-slate-400">
<svg className="w-16 h-16 mx-auto mb-2 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<p className="text-xs font-semibold">Nenhuma imagem capturada ainda</p>
</div>
)}
</div>
{/* Status feedback */}
{mensagemStatus && (
<p className="text-xs text-center font-medium text-slate-600 mb-4 bg-white p-2 rounded-lg border border-slate-200">
{mensagemStatus}
</p>
)}
{/* Botões de Ação */}
<div className="space-y-3">
<button
onClick={capturarFotoOuGaleria}
disabled={carregando}
className="w-full py-3.5 px-4 bg-red-600 hover:bg-red-700 active:bg-red-800 text-white font-bold text-sm rounded-xl shadow-md transition flex items-center justify-center space-x-2 disabled:opacity-50"
>
<span>{carregando ? 'Abrindo Câmera...' : '📷 Abrir Câmera / Galeria'}</span>
</button>
{fotoUri && (
<button
onClick={descartarFoto}
className="w-full py-2.5 px-4 bg-slate-200 hover:bg-slate-300 text-slate-800 font-semibold text-xs rounded-xl transition"
>
Descartar Foto
</button>
)}
</div>
</div>
);
}
4. Playground / Simulador Interativo
Teste as configurações da API do Capacitor direto no navegador antes de enviar o código ao celular físico.
Parâmetros de Camera.getPhoto()
Quanto menor a qualidade, menor o tamanho do arquivo retornado.
Habilita a ferramenta de corte e edição nativa do Android.
{\n "status": "Aguardando ação..."\n}
5. Exercícios Individuais Práticos
Cada aluno possui seu tema exclusivo de aplicativo para implementar com código fonte próprio.