Começar

Flutter

O pacote unolink é uma camada fina sobre os SDKs nativos de iOS e Android, que vêm dentro dele. Ele não precisa de nenhuma permissão além de internet e de nenhuma entrada no Info.plist. Publicado no pub.dev por unolink.dev. Plano Pro ou superior.

Requisitos

  • Flutter 3.22+ (Dart 3.4+).
  • Android 6.0+ (minSdk 23) e iOS 13+.
  • Uma chave do SDK: no painel, abra seu app → Configurações → Chaves do SDK → Nova chave. Ela aparece uma única vez, então copie-a nesse momento.

Instalação

flutter pub add unolink

Isso adiciona unolink: ^0.3.3 ao pubspec.yaml e baixa o pacote. Recompile o app depois; um hot reload não basta depois de adicionar código nativo.

Se o CocoaPods falhar no build de iOS com um erro de codificação (Ruby 4, ou um locale que não é UTF-8), rode você mesmo em UTF-8 e gere o build de novo:

cd ios && LANG=en_US.UTF-8 pod install

Configuração

Chame configure uma vez em main(), depois de WidgetsFlutterBinding.ensureInitialized() e antes de runApp:

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:unolink/unolink.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Unolink.configure('ulk_…', debug: kDebugMode);
  runApp(const MyApp());
}

Opções

OpçãoPadrãoO que faz
debugfalseMarca esta instalação como dispositivo de teste. Passe kDebugMode. Os painéis escondem dispositivos de teste, a menos que Incluir dispositivos de teste esteja ativado.
logLevelUnolinkLogLevel.warnQuanto vai para o console do Xcode ou para o Logcat (tag Unolink): none, error, warn, info, debug.
baseUrlhttps://api.unolink.devA API do SDK. Deixe como está.

A chave é pública: ela vai dentro do seu app e só pode reportar dados desse app. Veja Chaves do SDK.

Eventos do funil

Adicione uma linha em cada momento importante. Elas viram as etapas em Funil e Jornadas.

Unolink.onboardingCompleted();                     // the last onboarding screen is done
Unolink.paywallViewed(placement: 'onboarding');    // a paywall appeared; placement says which one
Unolink.paywallSkipped(placement: 'onboarding');   // closed without buying
Unolink.signUp(method: 'apple');                   // an account was created
Unolink.signUpSkipped();                           // went on as a guest (0.3.2+)

Sem RevenueCat, reporte você mesmo os testes grátis e as compras. O valor é uma string, então nunca é arredondado:

Unolink.trialStarted(productId: 'pro_yearly');
Unolink.purchase('49.99', 'USD', productId: 'pro_yearly');
Unolink.purchaseCancelled(productId: 'pro_yearly');              // closed the purchase sheet
Unolink.purchaseFailed(productId: 'pro_yearly', reason: 'payment_failed');

Seus próprios eventos recebem um nome e até 20 propriedades:

Unolink.track('level_up', {'level': 3});

A lista completa, com os nomes que chegam ao painel, está em Eventos.

ID de usuário e RevenueCat

Se você usa RevenueCat, conecte-o no seu app → Configurações → RevenueCat. Depois disso, não chame purchase nem trialStarted: o próprio RevenueCat envia testes grátis, compras, renovações, cancelamentos e reembolsos, e o Unolink ignora as chamadas de compra do app para que nada conte duas vezes.

Para ligar essas compras à instalação, informe o próprio app user ID do RevenueCat. Com o SDK 0.3.2 ou posterior, uma linha o mantém atualizado. Chame-a uma vez na abertura, antes ou depois de configure:

Unolink.connectRevenueCat(
  () => Purchases.appUserID,
  addListener: Purchases.addCustomerInfoUpdateListener,
  removeListener: Purchases.removeCustomerInfoUpdateListener,
);

O Unolink lê o ID assim que o RevenueCat está configurado, a cada atualização das informações do cliente (compras, logIn, logOut) e sempre que o app é retomado, e só o envia quando ele mudou. Se o RevenueCat ainda não estiver configurado, a leitura falha em silêncio e é tentada de novo depois. Ele retorna uma função que desconecta. Por isso, Unolink.revenueCatUserChanged() normalmente não é necessário no Flutter.

Sem ela (ou antes da 0.3.2), chame setUserId com o ID do RevenueCat depois de Purchases.configure, depois de cada Purchases.logIn e Purchases.logOut, e em toda abertura. Use um ou outro, não os dois:

// after Purchases.configure, logIn and logOut, and on every launch
Unolink.setUserId(await Purchases.appUserID);

Para um convidado, esse é o ID anônimo do RevenueCat, então uma compra feita antes do cadastro se junta à instalação na hora. Depois do logIn, é o ID da conta. Todo ID que uma instalação reportou conta, então a compra do convidado continua ligada. Veja Convidados e contas.

O Unolink nunca guarda o ID em si, apenas um hash unidirecional que é diferente para cada um dos seus apps.

No Android, quando alguém instala a partir de um link do Unolink que tem deep link, o listener roda uma vez, na primeira abertura. No iPhone ele não é chamado: a Apple não tem referrer de instalação, e o Unolink nunca faz fingerprinting.

Unolink.setDeferredDeepLinkListener((link) {
  // link.deepLink: '/promo/3for2', link.params: {'offer': '3for2'}
  // link.linkSlug: 'tiktok' or null, link.clickId: 'clk_01JA…'
  if (link.deepLink != null) router.go(link.deepLink!);
  Unolink.consumeDeferredDeepLink();
});

O listener é chamado uma vez por instalação. Um link que chegou antes de você defini-lo é entregue assim que você o define.

Abrir depois do onboarding

A primeira abertura geralmente começa no onboarding. Se a tela do link fica depois do onboarding ou do login, não a abra pelo listener. Quando o onboarding (ou o login) terminar, leia o link pendente, abra-o e depois marque-o como consumido:

// when onboarding (or sign-in) is done, and on every launch after that until it's consumed
final link = await Unolink.getPendingDeferredDeepLink();
if (link?.deepLink != null) {
  router.go(link!.deepLink!);
  await Unolink.consumeDeferredDeepLink();
}

O link espera até você consumi-lo, mesmo que o app seja fechado nesse meio-tempo (até 30 dias depois da instalação; 0.3.3+). Um usuário novo que sai no meio do onboarding ainda o recebe na próxima abertura, então você não precisa guardá-lo. Chame consumeDeferredDeepLink() depois de abri-lo: até lá, getPendingDeferredDeepLink() o retorna a cada abertura. Ele é null antes de configure e até o Google Play responder, e sempre null no iPhone.

Defina o Deep link e os Parâmetros do link no painel (Links → ⋯ → Editar). Como isso funciona está em Deep links adiados no Android.

Outras chamadas

ChamadaO que faz
await Unolink.getInstallId()O ID aleatório desta instalação, ou null antes de configure. Ele não identifica nenhuma pessoa nem dispositivo.
Unolink.flush()Envia agora os eventos na fila. Normalmente não é necessário.
Unolink.setEnabled(false)Desativa o rastreamento e apaga os eventos na fila, para a sua opção de desativação (opt-out). true ativa de novo.

Dispositivos de teste

Com debug: kDebugMode, as builds de depuração são dispositivos de teste. Os dados delas são reais, mas ficam ocultos até você ativar Incluir dispositivos de teste em Funil e Jornadas. Mais em Testes.

Privacidade

  • Sem IDFA, sem Google Advertising ID, sem o aviso do App Tracking Transparency, sem fingerprinting.
  • Um ID de instalação aleatório, versão do app, versão do sistema, versão do SDK, celular ou tablet, idioma. O país vem da borda da rede. Endereços IP nunca são guardados.
  • No iOS, o manifesto de privacidade do SDK nativo vem com o pacote: análises, não vinculados à identidade.
  • Nunca coloque nomes, e-mails ou outros dados pessoais nas propriedades dos eventos.

Solução de problemas

  • MissingPluginException? Pare o app e rode-o de novo. Hot reload e hot restart não carregam código nativo novo.
  • Nada no painel? Ative Incluir dispositivos de teste: builds com kDebugMode ficam ocultas por padrão.
  • Ainda nada? Configure com logLevel: UnolinkLogLevel.debug e procure por Unolink no console do Xcode ou no Logcat. Uma chave errada ou revogada aparece no log como erro.

Mais em Solução de problemas.