O que construímos até aqui
Nas últimas partes da série, saímos do zero e chegamos a um sistema funcional: configuramos a captura nativa no Android (Post 2) e no iOS (Post 3), integramos tudo ao Flutter com DeepLinkService, MethodChannel e EventChannel (Post 4), e publicamos os arquivos de verificação em deeplinkslab.dev para que App Links e Universal Links funcionem em produção (Post 5).
O link funciona. Mas só quando o app está instalado.
Cenário: João recebe um link de indicação do FitConnect. Clica. Mas ele ainda não tem o app instalado.
O que acontece com o referralCode enquanto ele passa pela loja, faz o download e abre o app pela primeira vez?
Com deep links normais: nada. O contexto se perde. O app abre na tela inicial, sem nenhuma pista de onde João veio.
É exatamente esse problema que os Deferred Deep Links resolvem.
Este é o sexto conteúdo de uma série completa sobre Deep Links no Flutter. Se você ainda não viu os posts anteriores: Post 1 — Guia para Iniciantes | Post 2 — Android com Kotlin | Post 3 — iOS com Swift | Post 4 — Integração Flutter | Post 5 — Produção.
Neste artigo você vai aprender:
- Por que deep links normais falham quando o app não está instalado.
- Solução simples com LocalStorage.
- Solução robusta com backend e fingerprint.
O desafio
O fluxo do problema é direto:
- Usuário clica no link — app não está instalado.
- Navegador abre a loja (Play Store / App Store).
- Usuário instala o app e abre pela primeira vez.
- ❓ Como o app sabe qual era o link original?
Aqui está o nó: quando o app é aberto diretamente da loja após a instalação, não há nenhum intent nem URL sendo entregue ao sistema. O deep link original ficou no navegador — e o app nunca teve acesso a ele.
A resposta para esse problema é Deferred Deep Links: uma estratégia para preservar o contexto do link entre o clique e o primeiro launch do app.
Fluxo do problema
Solução 1: LocalStorage
A abordagem mais simples usa o próprio device para guardar o estado. A ideia: quando a página web de redirect é aberta (veremos isso no Post 7), ela salva o referralCode no localStorage do navegador. Na primeira abertura do app, o Flutter verifica se existe um estado pendente salvo localmente e repassa o dado ao Flutter.
No Flutter, a lógica fica no DeepLinkService:
// lib/services/deep_link_service.dart
class DeepLinkService {
// Salvar código após primeira abertura
Future<void> processDeferredLink() async {
if (initialLink != null &&
initialLink!.contains('referralCode')) {
final code = _extractReferralCode(initialLink!);
await _storage.write('pending_referral', code);
}
}
// Recuperar código no signup
Future<String?> getPendingReferralCode() async {
final code = await _storage.read('pending_referral');
if (code != null) {
await _storage.remove('pending_referral'); // Cleanup após uso
return code;
}
return null;
}
}
A integração acontece em dois momentos distintos do ciclo de vida do app:
// SplashScreen — primeiro launch
await deepLinkService.processDeferredLink();
// SignupScreen — ao carregar a tela
final pendingCode = await deepLinkService.getPendingReferralCode();
if (pendingCode != null) {
referralCodeController.text = pendingCode;
}
O remove após a leitura é importante: garante que o código seja usado apenas uma vez. Sem isso, o código reapareceria em toda abertura do app até o signup ser concluído.
Vantagens:
- ✅ Simples de implementar.
- ✅ Funciona sem conexão.
- ✅ Sem dependência de backend.
Limitações:
- ⚠️ Essa abordagem funciona melhor quando o fluxo continua dentro do mesmo contexto web/app híbrido.
- ⚠️ Sem analytics — não há como saber quantos usuários chegaram via deferred link.
Solução 2: Backend com fingerprint
Quando você precisa de rastreamento cross-device ou analytics de conversão, a solução é mover o estado para o servidor.
O conceito central é o fingerprint: uma combinação de atributos do request HTTP (IP, User-Agent, Accept-Language) que, com alta probabilidade, identifica o mesmo dispositivo em dois momentos diferentes — o clique no link e o primeiro launch do app.
O fluxo no backend:
// Backend: Criar short link
app.post("/api/referral/create-link", async (req, res) => {
const { referralCode } = req.body;
const shortCode = generateShortCode();
await db.save(shortCode, referralCode);
res.json({
shortUrl: `https://deeplinkslab.dev/s/${shortCode}`,
});
});
// Backend: Processar clique
app.get("/s/:code", async (req, res) => {
const referralCode = await db.get(req.params.code);
const fingerprint = generateFingerprint(req); // IP + User-Agent + Accept-Language
// Salvar em cache por 48h
await redis.setex(`pending:${fingerprint}`, 172800, referralCode);
// Redirecionar para a loja da plataforma
const platform = detectPlatform(req);
res.redirect(getStoreUrl(platform));
});
// Backend: Recuperar código no primeiro launch
app.post("/api/referral/get-pending", async (req, res) => {
const fingerprint = generateFingerprint(req);
const code = await redis.get(`pending:${fingerprint}`);
if (code) {
await redis.del(`pending:${fingerprint}`); // Cleanup após uso
res.json({ referralCode: code });
} else {
res.status(404).json({ message: "Not found" });
}
});
No Flutter, o primeiro launch faz uma chamada ao backend para verificar se há um código pendente associado ao fingerprint do device:
// lib/services/deep_link_service.dart
Future<String?> checkPendingOnBackend() async {
try {
final response = await dio.post('/api/referral/get-pending');
return response.data['referralCode'];
} catch (e) {
return null;
}
}
Vantagens:
- ✅ Funciona cross-device.
- ✅ Analytics completo de cliques e conversões.
- ✅ Mais robusto no mesmo device.
Desvantagens:
- ⚠️ Requer backend e Redis (ou equivalente).
- ⚠️ Mais complexo de implementar e manter.
- ⚠️ Fingerprinting não é 100% preciso — redes com NAT ou VPN podem causar colisões.
Quando usar cada uma
Comece com LocalStorage. Se o seu caso de uso for simples — referral no mesmo device, sem necessidade de analytics — essa solução resolve com poucas linhas de código.
Migre para backend quando precisar de analytics de conversão, rastreamento cross-device, ou quando a precisão do fingerprinting for aceitável para o seu contexto de negócio.
O que construímos até aqui
Ao final desta etapa, você já tem:
- Clareza sobre por que deep links normais não preservam contexto após instalação.
- A solução com LocalStorage:
processDeferredLinkpara salvar egetPendingReferralCodepara recuperar. - A arquitetura da solução com backend: short link, fingerprint no Redis e consulta no primeiro launch.
- Os critérios para escolher entre as duas abordagens.
Este é o sexto de 9 posts da série. Nos próximos posts, o fluxo fica completo: no Post 7, criamos a página web que serve como ponte entre o navegador e a loja — e é ela que executa a parte do LocalStorage quando o app ainda não está instalado.
No próximo post: a página web de redirect inteligente que detecta a plataforma, exibe o código de indicação visualmente e redireciona para a loja certa.
Código completo disponível no repositório: FitConnect no GitHub
Se esse conteúdo te ajudou, deixa um clap 👏 e salva o post — isso me ajuda a continuar a série.
Você implementaria LocalStorage ou backend com fingerprint?
Ou usaria um serviço pronto como AppsFlyer / Branch / Firebase?
Tags: Flutter, Deep Links, Deferred Deep Links, Mobile Development



Top comments (0)