Cos'è il deep linking

Il deep linking permette di aprire una schermata specifica della tua app a partire da un link esterno: un'email, un messaggio, un sito web o una notifica. Invece di portare l'utente sulla home, lo si conduce direttamente al contenuto desiderato, ad esempio una pagina prodotto (https://miosito.it/prodotti/42).

Esistono diverse tipologie di link che Flutter può intercettare:

  • Custom URL scheme: link del tipo miapp://prodotti/42. Semplici da configurare ma poco sicuri e con UX limitata.
  • Universal Links (iOS) e App Links (Android): usano URL https reali e verificati. Sono lo standard consigliato perché aprono l'app se installata, altrimenti il browser.

In questa guida vediamo come gestire entrambi con il pacchetto app_links integrato con go_router.

Installazione

Aggiungi le dipendenze al pubspec.yaml:

dependencies:
  app_links: ^6.3.0
  go_router: ^14.0.0

Configurazione Android (App Links)

Nel file android/app/src/main/AndroidManifest.xml, all'interno dell'<activity> principale, aggiungi un 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"
        android:host="miosito.it" />
</intent-filter>

L'attributo android:autoVerify="true" richiede di pubblicare un file assetlinks.json su https://miosito.it/.well-known/assetlinks.json:

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "it.miosito.miapp",
    "sha256_cert_fingerprints": ["FA:C6:17:..."]
  }
}]

Il fingerprint SHA-256 si ottiene dalla chiave di firma con keytool o dalla Google Play Console.

Configurazione iOS (Universal Links)

In Xcode, sotto Signing & Capabilities, aggiungi la capability Associated Domains e inserisci:

applinks:miosito.it

Sul server pubblica https://miosito.it/.well-known/apple-app-site-association (senza estensione, con Content-Type: application/json):

{
  "applinks": {
    "apps": [],
    "details": [{
      "appID": "TEAMID.it.miosito.miapp",
      "paths": ["/prodotti/*"]
    }]
  }
}

Intercettare i link nel codice

Il pacchetto app_links gestisce due scenari: l'app aperta da chiusa (cold start) e l'app già in esecuzione in background.

import 'package:app_links/app_links.dart';
import 'package:flutter/material.dart';

class DeepLinkHandler {
  final AppLinks _appLinks = AppLinks();
  final GoRouter router;

  DeepLinkHandler(this.router);

  Future<void> init() async {
    // Link che ha avviato l'app da chiusa
    final initialUri = await _appLinks.getInitialLink();
    if (initialUri != null) {
      _handleUri(initialUri);
    }

    // Link ricevuti mentre l'app è attiva
    _appLinks.uriLinkStream.listen((uri) {
      _handleUri(uri);
    });
  }

  void _handleUri(Uri uri) {
    // Es: https://miosito.it/prodotti/42 -> /prodotti/42
    router.go(uri.path);
  }
}

Integrazione con go_router

Definisci le rotte che corrispondono ai path dei tuoi link:

final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
    GoRoute(
      path: '/prodotti/:id',
      builder: (context, state) {
        final id = state.pathParameters['id']!;
        return ProdottoScreen(id: id);
      },
    ),
  ],
);

Inizializza l'handler dopo aver creato il router:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final handler = DeepLinkHandler(router);
  await handler.init();
  runApp(MyApp(router: router));
}

Nota: go_router include un supporto nativo al deep linking tramite il sistema di routing della piattaforma. Tuttavia, usare app_links esplicitamente dà maggiore controllo, soprattutto quando serve gestire la logica prima della navigazione (es. autenticazione o tracciamento).

Testare i deep link

Durante lo sviluppo puoi simulare un link senza configurare il server.

Su Android:

adb shell am start -a android.intent.action.VIEW \
  -d "https://miosito.it/prodotti/42" it.miosito.miapp

Su iOS (simulatore):

xcrun simctl openurl booted "https://miosito.it/prodotti/42"

Best practice

  • Valida sempre i parametri ricevuti dai link: un utente potrebbe manipolare l'URL.
  • Gestisci i fallback: se il contenuto non esiste, mostra una schermata di errore invece di crashare.
  • Proteggi le rotte sensibili con controlli di autenticazione tramite il redirect di go_router.
  • Verifica i file di associazione: usa lo strumento Statement List Tester di Google per Android e i tool Apple per iOS.

Conclusione

Il deep linking trasforma l'esperienza utente, collegando il mondo web alla tua app in modo fluido. Combinando app_links per intercettare gli URL e go_router per la navigazione dichiarativa, ottieni una soluzione robusta e mantenibile. La parte più delicata resta la corretta configurazione dei file di verifica lato server: una volta superato quello scoglio, i tuoi Universal Links e App Links funzioneranno in modo trasparente su entrambe le piattaforme.