Flutter Deep Linking

Tempo de leitura: 16 minutes

Os deep links estão transformando o cenário dos aplicativos móveis, e o Flutter está na vanguarda. Imagine o seguinte: um amigo lhe envia um link de produto. Você toca nele e pronto! Você não está apenas no aplicativo, mas está vendo exatamente aquele produto. É mágica? Não, isso é deep linking!

No Flutter, os deep links servem como caminhos diretos para conteúdo ou recursos específicos do aplicativo, essenciais para aplicativos com conteúdo compartilhável. Eles permitem experiências in-app perfeitas quando os usuários compartilham links, dando suporte a tudo, desde campanhas de marketing até atalhos de conteúdo.

Este artigo desmistificará o deep linking no Flutter. Ele abrange tudo, desde a configuração nativa do Android e do iOS até o manuseio da navegação com o GoRouter.

Os links diretos não apenas facilitam a navegação, mas também fazem com que os usuários voltem, aumentam o envolvimento e simplificam o compartilhamento de conteúdo. Se você estiver criando seu primeiro deep link ou ajustando o que já tem, este guia é o roteiro definitivo para o sucesso do deep linking.

 

Anatomia de um Deep Link

Um deep link é um link que envia os usuários para um destino no seu aplicativo, em vez de uma página da Web. Para entender o que é um deep link, considere este exemplo:

  • https://capsistema.com.br/artigos/parse-json-dart/

Os deep links contêm uma sequência de caracteres conhecida como URI, que contém:

  • Scheme – Esta é a primeira parte do link, geralmente identificada como http ou https. O esquema nos informa qual protocolo usar ao buscar o recurso on-line.
  • Host – Esse é o nome de domínio do link. No nosso caso, capsistema.com.br é o host, mostrando-nos onde o recurso está localizado.
  • Path – identifica o recurso específico no host que o usuário deseja acessar. No nosso exemplo, /artigos/parse-json-dart/ é o caminho.

Às vezes, você também verá isso:

  • Query Parameters (Parâmetros de consulta) – São opções extras adicionadas após uma marca ? Geralmente, elas estão lá para ajudar a classificar ou filtrar dados.
  • Port – Para acessar os serviços de rede do servidor. É comum vê-la em configurações de desenvolvimento. Quando não são mencionadas, HTTP e HTTPS se manterão em suas portas padrão (80 e 443).
  • Fragment – Também chamado de âncora, esse bit opcional segue um # e aumenta o zoom em uma parte específica de uma página da Web.

Aqui está um exemplo anotado que mostra todos os itens acima:

Compreender a estrutura de um link o ajudará a lidar com ele de forma eficaz em seu aplicativo Flutter. Se você estiver nos estágios iniciais do projeto, poderá ter uma palavra a dizer sobre como a URL é criada para mantê-la organizada e fácil de manusear.

Lembre-se de que alguns URLs são configurados melhor do que outros. Se os seus URLs não forem bem projetados, pode ser complicado manipulá-los em seu aplicativo. Fique atento à parte da implementação do Flutter deste guia para ver como isso é feito.

Tipos de deep links

Os aplicativos móveis podem lidar com dois tipos de deep links, com base no esquema que usam.

Imagem da Web
Imagem da Web

 

Esquemas personalizados

Considere este URI de esquema personalizado como um exemplo:

  • yourScheme://your-domain.com

No Android, isso é chamado de deep link, enquanto no universo iOS, é chamado de esquema de URL personalizado. Esse método é útil se você não possui um domínio, mas deseja aproveitar o poder dos links diretos.

Você pode escolher qualquer esquema personalizado que desejar, desde que o defina em seu aplicativo. A configuração é muito rápida, pois você não precisa se preocupar com a exclusividade.

Mas a desvantagem é que é menos seguro, pois qualquer aplicativo pode sequestrar seu esquema personalizado e tentar abrir seus links. Além disso, se alguém não tiver seu aplicativo e clicar em um link de esquema personalizado, chegará a um beco sem saída com uma mensagem de erro. Embora essa não seja a melhor prática, sua simplicidade a torna útil para testes rápidos.

Esquema HTTP/HTTPS

Esses são os URLs comuns da Web que encontramos todos os dias:

  • https://your-domain.com

Conhecido como App Links no Android e Universal Links no iOS, esse método oferece a maneira mais segura de adicionar suporte a deep links ao seu aplicativo móvel.

Ele exige que você possua um domínio e realize a verificação em ambas as extremidades. Você deve registrar seu domínio no código do aplicativo (arquivo de manifesto no Android e domínios associados no iOS) e verificar seu aplicativo móvel no lado do servidor.

Ao fazer essa dança, seu aplicativo reconhece o domínio, e o domínio verifica seu aplicativo. Essa verificação bidirecional garante a integridade e a autenticidade dos links diretos, proporcionando uma experiência segura de links diretos. 👍

Chega de teoria; vamos começar a configurar a plataforma!

 

Configuração de Deep Links no Android

Vamos começar ajustando o arquivo AndroidManifest.xml:

  • Abra o arquivo android/app/src/main/AndroidManifest.xml
  • Adicione um intent filter à sua tag activity
  • Especifique seu scheme e host
  • Opcional: defina os paths suportados

Veja como deve ser o seu intent filter

<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />

  <data android:scheme="https" />
  <data android:host="yourDomain.com" />
</intent-filter>

Se você quiser usar um esquema personalizado e abrir links como yourScheme://yourDomain.com, use:

<data android:scheme="yourScheme" />
<data android:host="yourDomain.com" />

O exemplo acima não chama nenhum caminho específico. Com essa configuração, seu aplicativo pode abrir todos os URLs do seu domínio. Isso geralmente não é desejável e, se você quiser ter mais controle, precisará definir os caminhos aceitos adicionando tags de caminho relevantes. As mais comuns são path, pathPattern e pathPrefix.

<!--o valor exato do caminho deve ser "/login/" -->
<data android:path="/login/" />  

<!-- O caminho deve começar com "/product/" e, em seguida, pode conter qualquer número de caracteres; nesse caso, provavelmente seria um ID de produto. -->
<data android:pathPattern="/product/.*" />

<!-- O caminho deve começar com /account/, portanto, pode ser /account/confirm, /account/verify etc. -->
<data android:pathPrefix="/account/" />

Você pode encontrar um guia detalhado sobre como usar várias tags de dados na documentação oficial do Android.

Verificação do servidor Android

Agora que o aplicativo sabe que tipo de URLs deve manipular, você deve garantir que o seu domínio reconheça o seu aplicativo como um manipulador de URL confiável.

Lembre-se de que essa etapa só é necessária para links HTTP/HTTPS e não para esquemas personalizados.

Etapa 1: crie um novo arquivo chamado assetlinks.json. Esse arquivo conterá os links de ativos digitais que associam seu site ao seu aplicativo. Aqui está o que deve estar dentro dele:

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example",
    "sha256_cert_fingerprints":
    ["00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"]
  }
}]

Troque "com.example" pelo nome do pacote do seu aplicativo e substitua o valor sha256_cert_fingerprints pela impressão digital SHA256 exclusiva do seu aplicativo.

Etapa 2: carregue o arquivo assetlinks.json em seu site. Ele deve ser acessível via https://yourdomain/.well-known/assetlinks.json. Se não souber como fazer isso, entre em contato com o administrador do seu domínio.

Etapa 3: para verificar se o assetlinks.json está configurado corretamente, use a ferramenta de teste de lista de declarações fornecida pelo Google em https://developers.google.com/digital-asset-links/tools/generator.

Você pode gerar a impressão digital SHA256 do seu aplicativo com este comando:

keytool -list -v -keystore <keystore path> -alias <key alias> -storepass <store password> -keypass <key password>

Ou, se você assinar seu aplicativo de produção via Google Play Store, poderá encontrá-lo no Console do desenvolvedor em Setup > App Integrity > App Signing.

 

Como testar deep links no Android

Pronto para testar seus deep links no Android? Basta iniciar um emulador de Android e abrir o terminal para executar estes comandos.

Para links HTTP/HTTPS:

adb shell am start -a android.intent.action.VIEW \
  -c android.intent.category.BROWSABLE \
  -d https://yourDomain.com

E para esquemas personalizados:

adb shell am start -a android.intent.action.VIEW \
  -c android.intent.category.BROWSABLE \
  -d yourScheme://yourDomain.com

Atenção! O ADB normalmente fica no diretório android/sdk/platform-tools. Certifique-se de que ele esteja em seu caminho do sistema antes de executar esses comandos.

Está se deparando com o problema “adb: more than one device/emulator”? Se você tiver vários dispositivos Android ou emuladores em movimento, consulte este tópico para obter uma correção.

Outra maneira de testar é enviar o link https://yourDomain.com para você mesmo usando qualquer aplicativo (como o WhatsApp) e, em seguida, tocar nele. No momento, o objetivo é apenas fazer com que o aplicativo ganhe vida. Mais tarde, aprenderemos a navegar para a tela certa com o código Flutter.

🚨 Possível problema 🚨

Se tocar no URL abrir seu aplicativo “dentro” do aplicativo de origem (como Gmail ou WhatsApp), em vez de sua própria janela, adicione android:launchMode="singleTask" dentro da tag <activity>.

Aqui está um exemplo da aparência do aplicativo após ser aberto com um deep link do WhatsApp:

 

Exemplo mostrando que o aplicativo de destino é aberto dentro de outro aplicativo (Whatsapp)
Exemplo mostrando que o aplicativo de destino é aberto dentro de outro aplicativo (Whatsapp)

 

Entendendo a caixa de diálogo de desambiguação

A caixa de diálogo de desambiguação é um prompt que o Android mostra quando um usuário clica em um link que pode ser aberto por vários aplicativos. É uma maneira de o sistema perguntar: “Qual aplicativo deve cuidar disso?”

Exemplo da caixa de diálogo de desambiguação no Android
Exemplo da caixa de diálogo de desambiguação no Android

Se você estiver vendo essa caixa de diálogo depois de clicar em um link, sua configuração de deep link está quase perfeita, mas há um pequeno problema em algum lugar. Um descuido comum é a falta de android:autoVerify="true" em seu arquivo de manifesto, que é fundamental para contornar essa caixa de diálogo.

Ou pode haver um problema com seu arquivo assetlinks.json. Verifique novamente as impressões digitais e procure por erros de digitação no nome do pacote.

A resolução desses pequenos problemas facilitará a jornada do usuário, permitindo que seus links sejam abertos diretamente no aplicativo e ignorando completamente a caixa de diálogo de desambiguação.

Observe que, durante a depuração, essa caixa de diálogo é totalmente normal. Isso ocorre porque o aplicativo ainda não foi assinado com a chave de produção e, portanto, não corresponderá à impressão digital em assetlinks.json.

Configuração de deep links no iOS

Mudando para o iOS, o processo de configuração de links diretos é bem diferente em comparação com o Android.

Antes de mais nada, vamos configurar seus domínios associados. Veja como:

  • Abra o ios/Runner .xcworkspace no Xcode.
  • Vá para Runner > Signing & Capabilities > + Capability > Associated Domains (Assinatura e recursos > + recurso > Domínios associados).
  • Prefixe seu domínio com applinks: ao adicioná-lo.

Se quiser usar um esquema personalizado, você também precisará defini-lo no Xcode. O procedimento é o seguinte:

  • Abra o arquivo Info.plist.
  • Pressione o botão + ao lado de Information Property List para adicionar tipos de URL.
  • Abaixo disso, vá para URL Schemes e troque o yourScheme pelo esquema personalizado que você está procurando.
  • Configure um identificador (embora não seja importante saber qual é).

Quando terminar, seu arquivo Info.plist deverá ter a seguinte aparência:

Como alternativa, você pode abrir o arquivo Info.plist como código-fonte e colar esse snippet:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>yourScheme</string>
    </array>
    <key>CFBundleURLName</key>
    <string>yourIdentifier</string>
  </dict>
</array>

Lembre-se de que você só precisa se preocupar com os tipos de URL para um esquema personalizado. Se o que você quer são links diretos HTTP/HTTPS, pode pular esta parte.

Verificação do servidor iOS para Deep Links

Ao contrário do Android, no iOS, o arquivo hospedado no servidor contém não apenas as informações do aplicativo, mas também os caminhos suportados. Veja a seguir como fazer isso:

Etapa 1: Crie um novo arquivo chamado apple-app-site-association sem extensão de arquivo. Esse arquivo conterá os links de ativos digitais que unem o site e o aplicativo. Preencha-o com algo parecido com isto:

{
  "applinks":{
    "apps":[
       
    ],
    "details":[
      {
        "appIDs":[
          "TeamID.BundleID"
        ],
        "components":[
          {
            "/":"/login",
            "comment":"Matches URL with a path /login"
          },
          {
            "/":"/product/*",
            "comment":"Matches any URL with a path that starts with /product/."
          },
          {
            "/":"/secret",
            "exclude":true,
            "comment":"Matches URL with a path /secret and instructs the system not to open it as a universal link."
          }
        ]
      }
    ]
  }
}

Altere "TeamID.BundleID" para o ID da equipe e o ID do pacote do seu aplicativo e atualize os componentes para que correspondam aos caminhos com os quais seu aplicativo lidará.

O iOS permite que você exclua caminhos, portanto, é bastante comum ver uma lista de caminhos excluídos, seguida de uma definição “aceitar todos os caminhos” no final (já que as verificações são feitas de cima para baixo):

{
   "/":"*"
}

Etapa 2: hospede o arquivo apple-app-site-association em seu site. Ele deve ser acessível via https://yourDomain.com/.well-known/apple-app-site-association.

O arquivo apple-app-site-association deve ser servido com o tipo de conteúdo application/json (mas não deve ter a extensão de arquivo .json!) e não deve ser assinado ou criptografado.

Atenção: as alterações no arquivo apple-app-site-association podem não ser instantâneas no lado da Apple. Se não funcionar imediatamente, aguarde um pouco e tente novamente. Você pode verificar se a Apple tem a versão mais recente do seu arquivo acessando https://app-site-association.cdn-apple.com/a/v1/yourDomain.com. Isso retornará o arquivo apple-app-site-assocation conforme visto pelo serviço de verificação da Apple.

Como testar deep links no iOS

Para testar deep links no iOS, você pode simplesmente tocar no link ou usar o terminal para testar com estes comandos:

Para links HTTP/HTTPS:

xcrun simctl openurl booted https://yourDomain.com/path

E para links de esquemas personalizados:

xcrun simctl openurl booted yourScheme://yourDomain.com/path

Se o seu site for acessível somente via VPN (ou apenas em seu ambiente de preparação/teste), siga estas instruções sobre como configurar o modo alternativo.

Observação: o iOS é muito menos permissivo do que o Android. Sem o arquivo apple-app-site-association em seu site, os links HTTP/HTTPS não funcionarão, quer você esteja clicando diretamente ou usando o terminal. Portanto, você tem duas opções: apressar-se e publicar esse arquivo ou implementar um esquema personalizado para fins de teste.

E com isso, a configuração nativa está concluída! Seu aplicativo agora deve ser iniciado por meio de um deep link. A próxima etapa é conectar os pontos e garantir que o aplicativo não apenas abra, mas também leve os usuários diretamente ao conteúdo que eles procuram. E é aí que o Flutter entra em ação!

Como lidar com links profundos com o GoRouter no Flutter

O GoRouter é a escolha ideal para muitos desenvolvedores do Flutter quando se trata de navegação. Ele usa a API Router para oferecer uma abordagem baseada em URL para navegar entre telas, o que se alinha perfeitamente com os links diretos.

Para demonstrar como implementar links diretos usando o GoRouter e guiá-lo por todas as etapas, criei um aplicativo simples (o código-fonte completo está disponível aqui).

Primeiro, configure seu aplicativo com MaterialApp.router da seguinte forma:

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: goRouter,
    );
  }
}

Em seguida, crie uma instância do GoRouter para delinear as rotas do seu aplicativo. Vamos começar com duas telas simples:

  • Uma tela principal que serve como ponto de entrada do aplicativo, correspondente a https://yourdomain.com.
  • Uma tela de detalhes com um URL como https://yourdomain.com/details/itemId.
final goRouter = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const MainScreen(),
      routes: [
        GoRoute(
          path: 'details/:itemId',
          builder: (context, state) =>
              DetailsScreen(id: state.pathParameters['itemId']!),
        )
      ],
    )
  ],
);

Observe que a tela de detalhes está aninhada sob a tela principal. Portanto, quando você navegar para a página de detalhes, a tela principal estará bem ali, na pilha de navegação abaixo dela.

A parte :itemId no caminho indica que esse URL espera um parâmetro após /details/, e isso pode ser acessado com state.pathParameters['itemId'].

Você também pode obter parâmetros de consulta com state.uri.queryParameters['paramName'] – embora observe que o GoRouter 10.0.0 alterou a sintaxe dos parâmetros de consulta. Se estiver usando uma versão mais antiga, verifique este guia de migração.

Esta é a aparência do aplicativo:

Navegando até o destino do deep link

Agora que as rotas estão configuradas, precisamos lidar com o deep link e orientar nosso aplicativo na direção certa.

Na Web, isso funciona imediatamente. Você pode alterar o URL no navegador e ele deverá navegar para o local correto em seu aplicativo.

Uma pequena dica para os aplicativos da Web do Flutter: você notará que os caminhos geralmente contêm um fragmento de hash (como https://yourDomain.com/#/so mePath). Se preferir uma aparência mais limpa sem o #, você pode chamar usePathUrlStrategy() em seu método principal antes de executar o aplicativo.

Para o Android, você precisa inserir um pouco de metadados no AndroidManifest.xml dentro da tag activity:

<meta-data
    android:name="flutter_deeplinking_enabled"
    android:value="true" />

E no iOS, você precisará adicionar este trecho ao Info.plist:

<key>FlutterDeepLinkingEnabled</key>
<true/>

Com esses ajustes, seu aplicativo estará pronto para responder a deep links, esteja ele apenas começando ou já em execução.

Hora de fazer um test drive? Você pode usar o adb para Android ou o xcrun para iOS, como discutimos anteriormente:

# Test deep link on Android
adb shell am start -a android.intent.action.VIEW \
  -c android.intent.category.BROWSABLE \
  -d https://yourDomain.com/details/3
# Test deep link on iOS
xcrun simctl openurl booted https://yourDomain.com/details/3

Depois de executar esses comandos, seu aplicativo deverá navegar para a tela associada ao URL do deep link.

Implementação de redirecionamentos e proteções com o GoRouter

O GoRouter oferece uma maneira simples de configurar redirecionamentos de usuários ou “guardas”, como são conhecidos em outros pacotes de roteamento. Essa funcionalidade pode ser implementada no nível da rota, adicionando um redirecionamento dentro de um objeto GoRoute:

GoRoute(
  path: '/home',
  // always redirect to '/' if '/home' is requested
  redirect: (context, state) => '/',
)

Esse redirecionamento é acionado quando um evento de navegação está prestes a exibir a rota.

Além disso, você também pode definir um redirecionamento de nível superior, o que é especialmente útil para controlar o acesso a determinadas áreas do seu aplicativo, como garantir que um usuário esteja conectado antes de poder prosseguir:

Para colocar isso em ação, você precisará de uma maneira de verificar se um usuário está conectado usando qualquer solução de gerenciamento de estado de sua escolha.

Para simplificar, vamos usar o Riverpod com um ChangeNotifier simples:

class UserInfo extends ChangeNotifier {
  bool isLoggedIn = false;

  void logIn() {
    isLoggedIn = true;
    notifyListeners();
  }

  void logOut() {
    isLoggedIn = false;
    notifyListeners();
  }
}

Esse estado pode ser armazenado em um ChangeNotifierProvider:

final userInfoProvider = ChangeNotifierProvider((ref) => UserInfo());

Agora, vamos definir o objeto GoRouter em um provedor e introduzir uma nova rota /login:

final goRouterProvider = Provider<GoRouter>((ref) {
  return GoRouter(
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => const MainScreen(),
        routes: [
          GoRoute(
            path: 'details/:itemId',
            builder: (context, state) =>
              DetailsScreen(id: state.pathParameters['itemId']!),
          )
        ],
      ),
      GoRoute(
        path: '/login',
        builder: (context, state) => const LoginScreen(),
      ),
    ],
    redirect: (context, state) {
      // Here, you'll add your redirect logic
    },
  );
}

Transforme seu widget de nível superior em um ConsumerWidget e observe o goRouterProvider dentro do método build method:

class App extends ConsumerWidget {
  App({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final goRouter = ref.watch(goRouterProvider);
    return MaterialApp.router(
      routerConfig: goRouter,
      ...,
    );
  }
}

Em seguida, inclua um redirecionamento de nível superior em sua configuração do GoRouter. Essa função deve retornar null se nenhum redirecionamento for necessário, ou a nova rota se for necessário um redirecionamento:

final goRouterProvider = Provider<GoRouter>((ref) {
  return GoRouter(
    routes: [...],
    redirect: (context, state) {
      final isLoggedIn = ref.read(userInfoProvider).isLoggedIn;
      if (isLoggedIn) {
        return null;
      } else {
        return '/login';
      }
    },
  );
});

Inicialmente, os usuários se encontrarão desconectados. Eles podem fazer login usando um botão em uma tela de login simples:

class LoginScreen extends ConsumerWidget {
  const LoginScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Welcome!'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            ref.read(userInfoProvider).logIn();
            // explicitly navigate to the home page
            context.go('/');
          },
          child: const Text('Login'),
        ),
      ),
    );
  }
}

Aqui, context.go('/') é usado para navegar o usuário do login para a tela principal. No entanto, essa abordagem não é flexível e pode ser propensa a erros, especialmente em aplicativos grandes com muitas rotas.

Atualizando o GoRouter com refreshListenable

E se pudéssemos fazer com que o GoRouter fosse atualizado automaticamente quando o usuário fizesse login, sem navegar manualmente?

Bem, o GoRouter pensou nisso.

Ele permite que você conecte um Listenable (a classe UserInfo, que chama notifyListeners()) usando o argumento refreshListenable, de modo que qualquer alteração de estado possa solicitar uma atualização. E quando a atualização ocorre, a função de redirecionamento atualizada pode lidar com a situação em que o usuário está na página de login, mas já está autenticado:

final goRouterProvider = Provider<GoRouter>((ref) {
  return GoRouter(
    routes: [...],
    refreshListenable: ref.read(userInfoProvider),
    redirect: (context, state) {
      final isLoggedIn = ref.read(userInfoProvider).isLoggedIn;
      final isLoggingIn = state.matchedLocation == '/login';

      if (!isLoggedIn) return isLoggingIn ? null : '/login';
      if (isLoggingIn) return '/';

      return null;
    },
  );
});

Atenção: no trecho acima, estamos usando ref. read em vez de ref.watch porque não queremos que o GoRouter inteiro seja reconstruído quando o status da autenticação for alterado. Em vez disso, o GoRouter verificará internamente quando o estado (escutável) for alterado e chamará a chamada de retorno de redirect conforme necessário.

Com isso, não há mais necessidade de navegar manualmente após o login; o GoRouter faz o trabalho pesado para você.

Se estiver usando uma técnica de gerenciamento de estado diferente e depender de fluxos (como no flutter_bloc), você poderá adaptar isso usando uma classe GoRouterRefreshStream personalizada como esta: refreshListenable: GoRouterRefreshStream(streamController.stream). Consulte este guia de migração para obter mais detalhes.

Redirecionamento baseado em URL com parâmetros de consulta

Ao configurar proteções, considere a possibilidade de orientar os usuários de volta à página que eles buscavam inicialmente após o login. Isso envolve manter o controle do local inicial que o usuário queria alcançar. Você poderia passar esse local como um parâmetro para a tela de login ou armazená-lo em uma estrutura como UserInfo ou em uma estrutura profunda relacionada a links.

No entanto, uma abordagem mais simples e elegante aproveita a navegação baseada em URL adicionando parâmetros de consulta aos seus redirecionamentos. Veja como isso é feito:

redirect: (context, state) {
  final isLoggedIn = ref.read(userInfoProvider).isLoggedIn;
  final isLoggingIn = state.matchedLocation == '/login';

  final savedLocation = state.matchedLocation == '/' ? '' : '?from=${state.matchedLocation}';

  if (!isLoggedIn) return isLoggingIn ? null : '/login$savedLocation';

  if (isLoggingIn) return state.uri.queryParameters['from'] ?? '/';

  return null;
},

Esse código verifica se o usuário está conectado e para onde ele está navegando. Se ele precisar fazer login, ele o redireciona para a página de login, anexando o local inicial como um parâmetro de consulta. Uma vez conectado, ele redireciona o usuário para sua página inicial ou de volta para a página inicial se a página inicial não for especificada.

Se você abrir o aplicativo com o URL ${yourScheme}://${yourDomain}/details/4, você chegará primeiro à tela de login e, em seguida, aos detalhes do item nº 4.

Mas e se alguém tentar acessar rotas inexistentes como https://yourdomain.com/register ou https://yourdomain.com/details/15001900? Essa é uma ótima pergunta!

Como lidar com caminhos incorretos com o tratamento de erros

Quando você se aprofunda no tratamento de erros de deep link, há alguns tipos de erros que você precisa ter em mente.

No nível da definição de caminho

Você pode definir os caminhos aceitos no arquivo AndroidManifest.xml (para Android) e no arquivo apple-app-site-association (para iOS). Se um usuário tentar um caminho que não se encaixa nesses padrões, o navegador abrirá o caminho, não o seu aplicativo. Nesse cenário, seu código Flutter não precisa fazer nada.

No nível de correspondência de caminho

O fato de um deep link abrir seu aplicativo não significa necessariamente que o caminho seja válido. Talvez você tenha uma configuração “abrir todos os caminhos”, mas ainda precisa verificar cada caminho dentro da configuração do GoRouter.

Se um deep link apontar para uma rota que não existe no seu aplicativo, você deve apresentar uma mensagem de erro clara ou uma tela de fallback geral.

Aqui está o que você obtém com o GoRouter por padrão quando uma rota está faltando (isso é o que apareceu quando tentei https://yourdomain.com/register):

Não é exatamente a página mais amigável, certo? Você pode personalizá-la com os argumentos errorPageBuilder ou errorWidgetBuilder do GoRouter. Ou pode usar o manipulador onException, no qual pode optar por redirecionar o usuário, deixar para lá e assim por diante.

No nível do conteúdo

Muito bem! Seu caminho coincidiu e você se sentiu como uma pequena vitória. Mas espere um pouco! Um caminho correto não significa que o item que o usuário deseja (como um ID de produto específico ou um título de livro) realmente existe em seu banco de dados.

Se o conteúdo estiver faltando, você precisará lidar com isso de forma elegante. Coloque uma mensagem ou uma página “Conteúdo não encontrado” para manter os usuários informados. O objetivo é envolvê-los e proporcionar uma experiência perfeita, mesmo quando ocorrerem problemas.

Conclusão

Os links diretos podem melhorar significativamente a experiência do usuário, fornecendo passagem direta para áreas específicas em um aplicativo. Este guia ofereceu uma visão geral abrangente, desde a configuração do código nativo do Android e do iOS até o gerenciamento de rotas com o GoRouter.

Seu feedback é inestimável. Se houver alguma dúvida ou área de seu interesse especial, não hesite em compartilhar. À medida que a jornada pelas profundezas do deep linking no Flutter continua, sua curiosidade ajudará a moldar a exploração de tópicos futuros.

Mas espere, há mais na história dos links diretos. Alguns tópicos e ferramentas adicionais merecem ser mais explorados. 👇

Pacotes de links profundos externos

Os pacotes app_links e uni_links fornecem funcionalidade adicional para lidar com links diretos no Flutter. Eles podem ser especialmente úteis se você precisar trabalhar com padrões de URL complexos – lidar com eles dentro da configuração do GoRouter pode rapidamente se tornar incômodo.

Esses pacotes lhe dão controle total: você simplesmente recebe um URL e pode mapeá-lo para qualquer tela ou ação que desejar. Mas, como na vida, com maior poder vem maior responsabilidade. Você terá que lidar com toda a correspondência de caminhos e extração de parâmetros por conta própria.

Deep Links diferidos

Não vamos nos esquecer dos links diretos diferidos. Eles são uma vantagem para aplicativos promovidos por meio de anúncios, permitindo que novos usuários cheguem à página relevante logo após instalar o aplicativo a partir de um link. Os provedores desse recurso geralmente incluem ferramentas avançadas de marketing e análise para ajudá-lo a rastrear conversões, atribuições e origens de usuários. As soluções mais populares incluem branch.io, Adjust, AppsFlyer e Kochava.