Deep Links em Flutter: O Guia Definitivo para Iniciantes (Sem Pacotes de Terceiros) (Parte 1)
Source: Dev.to
Imagine isso: seu usuário recebe um link de desconto, clica nele — e BOOM! Ele não só abre seu app, mas já está na tela de checkout com o cupom aplicado.
Isso é mágica? Não — são Deep Links!
Objetivo da série
- Implementar deep links em Flutter sem utilizar pacotes prontos.
- Aprender como tudo funciona por baixo dos panos.
O que você vai aprender neste artigo
- O que são deep links.
- Diferença entre App Links e Custom Schemes.
- Como estruturar a base no Flutter.
Conceitos básicos
Deep links são URLs que levam o usuário diretamente para uma tela específica do seu app, em vez de abrir no navegador.
Exemplo real
| Fluxo tradicional | Deep link |
|---|---|
| 1️⃣ Abre navegador → 2️⃣ Usuário instala o app → 3️⃣ Abre o app → 4️⃣ Navega até a tela desejada | 1️⃣ Abre o app diretamente na tela correta (mesmo que o app ainda não esteja instalado – Deferred Deep Linking). |
Deferred Deep Linking
O diagrama abaixo ilustra o roteamento conhecido como Deferred Deep Linking, onde, mesmo que o usuário ainda não tenha o app instalado, o contexto do link é preservado após ele passar pelas lojas de apps.
Fluxo completo:
1. Usuário clica no link →
2. Se o app não está instalado, redireciona para a loja →
3. Usuário instala o app →
4. Ao abrir, o app recebe o link original e navega para a tela correta.Exemplo de link
https://fitconnect.app/signup?referralCode=TRAINER12345| Parte | Valor |
|---|---|
| Scheme | https |
| Host | fitconnect.app |
| Path | /signup |
| Query | referralCode=TRAINER12345 |
Cada parte tem um papel específico na navegação: o scheme identifica o protocolo (ou, no caso de custom schemes, o aplicativo); o host e o path determinam a rota; os query parameters carregam dados extras (ex.: código de indicação).
Tipos de deep links
1️⃣ Custom Scheme
fitconnect://fitconnect.app/signup?referralCode=TRAINER12345- ✅ Rápido de implementar.
- ✅ Ótimo para testes locais.
- ⚠️ Menos seguro — qualquer aplicativo pode registrar o mesmo scheme.
- ⚠️ Se o app não estiver instalado, o sistema exibe um erro feio.
2️⃣ HTTPS (App Links / Universal Links)
https://fitconnect.app/signup?referralCode=TRAINER12345- ✅ Seguro — verificação bidirecional entre aplicativo e servidor.
- ✅ Se o app não estiver instalado, abre normalmente no navegador.
- ✅ Recomendado para produção.
- ⚠️ Requer domínio próprio.
- ⚠️ Setup mais complexo.
O que é a verificação bidirecional?
O sistema operacional só abre o app se ambas as pontas se reconhecerem:
App declara quais domínios ele trata.
Servidor confirma quais aplicativos têm permissão para isso, por meio de arquivos hospedados no próprio domínio:
- Android →
assetlinks.json - iOS →
apple-app-site-association
- Android →
Se algum desses arquivos estiver ausente ou inconsistente, o link abre no navegador como fallback, impedindo que um app mal‑icioso intercepte seus links.
Dica: use custom schemes durante o desenvolvimento e migre para HTTPS (App Links / Universal Links) em produção.
Caso de uso: FitConnect
Vamos construir o FitConnect — uma plataforma fictícia que conecta personal trainers com clientes por meio de um sistema de indicação.
Cenário
- Maria, personal trainer, compartilha seu link de indicação.
- Quando um aluno se cadastra usando o link, Maria ganha bônus e o aluno ganha desconto.
Deep link principal da série
https://fitconnect.app/signup?referralCode=TRAINER12345678901234Ao clicar, o app deve abrir diretamente na tela de cadastro com o código TRAINER12345678901234 já preenchido.
| Item | Valor |
|---|---|
| Domínio | fitconnect.app |
| Custom scheme | fitconnect:// |
| Package (Android) | com.fitconnect.app |
Preparando a estrutura
Antes de escrever código nativo, vamos definir constantes, enums e modelos que serão compartilhados entre todas as camadas do app. Centralizar strings (como nomes de channels) evita erros de digitação difíceis de rastrear.
Constantes
// lib/shared/const/deep_link_const.dart
class DeepLinkConst {
static const String methodChannel = 'com.fitconnect.app/deeplink';
static const String eventChannel = 'com.fitconnect.app/deeplink_stream';
static const String customScheme = 'fitconnect';
static const String httpsScheme = 'https';
static const String appHost = 'fitconnect.app';
static const String signupPath = '/signup';
static const String referralCodeParam = 'referralCode';
static const int referralCodeLength = 20;
}Enum de tipos de deep link
// lib/shared/enums/deep_link_type.dart
enum DeepLinkType {
customScheme,
appLink, // Android HTTPS
universalLink, // iOS HTTPS
unknown;
bool get isSecure => this == appLink || this == universalLink;
}O getter
isSecuretorna o código mais expressivo — em vez de comparar strings ou verificar o scheme manualmente, basta usardata.type.isSecure.
Modelo de dados (usando Freezed)
// lib/shared/models/deep_link_data.dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'deep_link_type.dart';
part 'deep_link_data.freezed.dart';
part 'deep_link_data.g.dart';
@freezed
class DeepLinkData with _$DeepLinkData {
const factory DeepLinkData({
required String url,
required DeepLinkType type,
required String scheme,
String? host,
String? path,
@Default({}) Map<String, dynamic> queryParameters,
String? referralCode,
}) = _DeepLinkData;
factory DeepLinkData.fromJson(Map<String, dynamic> json) =>
_$DeepLinkDataFromJson(json);
}Por que usar Freezed?
- Garante imutabilidade.
- Gera código para
copyWith,==,hashCodee (de)serialização JSON.- Facilita a manutenção de objetos que carregam parâmetros críticos (ex.: código de indicação).
Próximos passos
- Implementar a camada nativa (Android & iOS) para interceptar os links.
- Criar o fluxo de navegação no Flutter que consome
DeepLinkData. - Configurar App Links / Universal Links (arquivo
assetlinks.jsoneapple-app-site-association). - Testar tanto em modo debug (custom scheme) quanto em produção (HTTPS).
Nos próximos artigos vamos detalhar cada um desses itens, incluindo a configuração dos arquivos de verificação bidirecional (Post 5) e a integração completa no Flutter.
Até a próxima! 🚀
eferralCode,
required DateTime receivedAt,
}) = _DeepLinkData;
}Ao final desta etapa, você já tem:
- Clareza sobre como deep links funcionam.
- Entendimento das diferenças entre os tipos de links.
- Uma base sólida no Flutter para começar a implementação.
Essa base será utilizada nos próximos passos para integrar o código nativo e completar o fluxo ponta a ponta.
Próximos passos
Na próxima etapa, vamos partir para a implementação nativa no Android — incluindo AndroidManifest.xml e MainActivity.kt completos.
Código completo disponível no repositório: FitConnect no GitHub.
Sobre a série
Este é o primeiro post de uma série de 9 sobre deep links em Flutter. Nos próximos artigos, abordaremos:
- Código nativo para Android e iOS
- Deferred links
- Redirect pages
- Testes
Tudo sem depender de pacotes prontos.
Se você tem dúvidas, sugestões ou já passou por algum problema parecido com deep links, conte nos comentários! Adoro saber como essa experiência se aplica em projetos reais.
E se quiser acompanhar os próximos posts, é só seguir aqui no Medium.
Feedback
Se esse conteúdo te ajudou, deixe um ❤️ ou um 🔖 aqui no DEV.to — isso ajuda o post a alcançar mais devs.
Pergunta para a comunidade
E você, já implementou deep links no seu app?
Qual foi o maior desafio que encontrou? Quero usar esses casos reais nos próximos posts da série 👇
Tags
flutter android ios deeplinks mobile programming