Pular para o conteúdo principal

Introdução

O Nemu Flutter SDK (nemu_tracking_flutter) permite integrar o sistema de atribuição e Smart Links da Nemu diretamente no seu aplicativo Flutter. Com o SDK você pode:
  • Capturar a origem da instalação — saber de qual campanha, fonte ou mídia o usuário veio (UTMs)
  • Processar deep links diretos — quando o usuário clica em um Smart Link e o app já está instalado
  • Processar deep links diferidos — quando o usuário clica em um Smart Link, instala o app pela loja e abre pela primeira vez
  • Identificar usuários — associar um ID do seu sistema ao dispositivo para cruzar dados de atribuição
  • Consultar histórico de sessões — recuperar os UTMs da última interação do usuário
O SDK funciona de forma automática: ao ser inicializado, ele intercepta deep links, detecta se é a primeira abertura do app e realiza a atribuição com o backend da Nemu, sem necessidade de configuração manual de cada evento.

Pré-requisitos

RequisitoVersão mínima
Flutter>= 3.10.0
Dart SDK>= 3.0.0 < 4.0.0
iOS12.0+
AndroidAPI 21+ (Android 5.0)
Pacote privado: o nemu_tracking_flutter é um pacote de acesso restrito. Para instalá-lo, você precisa de acesso ao repositório Git fornecido pela equipe de suporte da Nemu. Consulte a seção de instalação abaixo.

Instalação

1. Configuração do token de acesso

O pacote nemu_tracking_flutter é privado e está hospedado em um repositório Git com acesso restrito. Antes de instalar, você precisa configurar a autenticação.

Obtenha seu acesso

Entre em contato com a equipe de suporte da Nemu para receber acesso ao repositório do SDK:

2. Instalação do pacote

Adicione o SDK ao seu arquivo pubspec.yaml:

Via repositório Git (recomendado)

dependencies:
  nemu_tracking_flutter:
    git:
      url: https://github.com/usenemu/sdk-app-tracking-flutter.git
      ref: main

Via caminho local (para desenvolvimento)

dependencies:
  nemu_tracking_flutter:
    path: ../sdk-app-tracking-flutter
Em seguida, execute:
flutter pub get
Todas as dependências necessárias (shared_preferences, flutter_secure_storage, app_links, etc.) são instaladas automaticamente como dependências transitivas do SDK. Não é necessário adicioná-las manualmente ao seu projeto.

Configuração nativa

iOS

Para que o iOS encaminhe os Smart Links diretamente para o app (sem abrir o navegador), você precisa configurar Associated Domains. No Xcode:
  1. Abra o projeto .xcworkspace (em ios/Runner.xcworkspace)
  2. Selecione o target Runner
  3. Vá em Signing & Capabilities
  4. Clique em + Capability e adicione Associated Domains
  5. Adicione o domínio no formato:
applinks:seu-dominio.nemu.com.br
O domínio exato será fornecido pela equipe da Nemu junto com as credenciais.

2. Configure o URI Scheme (obrigatório)

No arquivo ios/Runner/Info.plist, adicione o URI scheme do seu app:
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>seuapp</string>
    </array>
  </dict>
</array>
Substitua seuapp pelo scheme definido no painel da Nemu para o seu Smart Link.
O URI scheme configurado aqui deve corresponder ao valor passado no parâmetro uriScheme na inicialização do SDK.

Android

Para que os Smart Links abram o app diretamente, adicione intent filters na sua Activity principal. No arquivo android/app/src/main/AndroidManifest.xml, dentro da tag <activity> principal (a que contém android:name=".MainActivity"): App Links (Universal Links do Android):
<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="seu-dominio.nemu.com.br" />
</intent-filter>
URI Scheme:
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="seuapp" />
</intent-filter>
Substitua seu-dominio.nemu.com.br e seuapp pelos valores correspondentes ao seu projeto.

2. Permissão de Internet (geralmente já presente)

Verifique se o AndroidManifest.xml possui a permissão de internet:
<uses-permission android:name="android.permission.INTERNET" />
Na maioria dos projetos Flutter essa permissão já está incluída por padrão.

Inicialização

A inicialização do SDK deve ser feita uma única vez, o mais cedo possível no ciclo de vida do app — idealmente no initState do seu widget raiz ou na função main.

Parâmetros de configuração

ParâmetroTipoObrigatórioDescrição
apiKeyStringSimChave de API obtida no painel da Nemu
uriSchemeStringSimURI scheme do app (ex: "seuapp"). Deve corresponder ao valor configurado no Smart Link
trackingIdStringSimID de rastreamento associado ao app no painel da Nemu
isDebugModeboolNãoAtiva logs detalhados no console. Padrão: false
baseUrlString?NãoSobrescreve a URL base da API (somente quando isDebugMode é true)

Exemplo completo de inicialização

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();

    NemuTracking.instance.init(
      const NemuInitParams(
        apiKey: 'sua-api-key',
        uriScheme: 'seuapp',
        trackingId: 'seu-tracking-id',
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomeScreen(),
    );
  }
}

Uso com variáveis de ambiente

Para não expor credenciais diretamente no código, utilize o pacote flutter_dotenv ou defina as variáveis via --dart-define:

Usando --dart-define (recomendado)

NemuTracking.instance.init(
  const NemuInitParams(
    apiKey: String.fromEnvironment('NEMU_API_KEY'),
    uriScheme: String.fromEnvironment('NEMU_URI_SCHEME'),
    trackingId: String.fromEnvironment('NEMU_TRACKING_ID'),
  ),
);
Execute o app com:
flutter run \
  --dart-define=NEMU_API_KEY=sua-api-key \
  --dart-define=NEMU_URI_SCHEME=seuapp \
  --dart-define=NEMU_TRACKING_ID=seu-tracking-id

Usando flutter_dotenv

Crie um arquivo .env na raiz do projeto:
NEMU_API_KEY=sua-api-key
NEMU_URI_SCHEME=seuapp
NEMU_TRACKING_ID=seu-tracking-id
import 'package:flutter_dotenv/flutter_dotenv.dart';

await dotenv.load();

NemuTracking.instance.init(
  NemuInitParams(
    apiKey: dotenv.env['NEMU_API_KEY']!,
    uriScheme: dotenv.env['NEMU_URI_SCHEME']!,
    trackingId: dotenv.env['NEMU_TRACKING_ID']!,
  ),
);
Adicione o .env ao seu .gitignore para não versionar as credenciais no repositório.

Identificação de usuários

Após o login do usuário no seu app, associe o ID dele ao dispositivo. Isso permite que a Nemu cruze dados de atribuição com o seu sistema de usuários.

Registrar usuário

import 'package:nemu_tracking_flutter/nemu_tracking_flutter.dart';

// Após o login bem-sucedido
void onLoginSuccess(String userId) {
  NemuTracking.instance.setUserId(userId);
}
O SDK salva o ID localmente e envia a associação ao backend automaticamente.

Limpar usuário no logout

void onLogout() {
  NemuTracking.instance.clearUserId();
}
clearUserId() remove apenas a associação local. O histórico de atribuição no backend é preservado.

O SDK processa dois tipos de deep links automaticamente: Ocorrem quando o app já está instalado e o usuário clica em um Smart Link. O sistema operacional abre o app diretamente (via Universal Link / App Link ou URI scheme). O fluxo é transparente:
  1. O usuário clica no Smart Link
  2. O app abre com a URL
  3. O SDK processa a URL e registra a sessão
  4. Os listeners registrados via onDeepLink são notificados com os dados
Ocorrem quando o app não está instalado. O usuário clica no Smart Link, é redirecionado para a loja, instala o app e abre pela primeira vez. O SDK detecta automaticamente esse cenário na primeira abertura e entrega os dados de atribuição e deep link via onDeepLink com isDeferred: true. Registre um callback para receber os dados do deep link assim que estiverem disponíveis:
import 'package:flutter/material.dart';
import 'package:nemu_tracking_flutter/nemu_tracking_flutter.dart';

class _MyAppState extends State<MyApp> {
  VoidCallback? _unsubscribe;

  @override
  void initState() {
    super.initState();

    NemuTracking.instance.init(
      const NemuInitParams(
        apiKey: 'sua-api-key',
        uriScheme: 'seuapp',
        trackingId: 'seu-tracking-id',
      ),
    );

    _unsubscribe = NemuTracking.instance.onDeepLink((DeepLinkData data) {
      debugPrint('Deep link recebido: $data');

      if (data.deepLinkValue != null) {
        // Navegar para a tela correspondente
        // Ex: data.deepLinkValue = "product/456"
        navigateTo(data.deepLinkValue!);
      }

      if (data.utms != null) {
        debugPrint('Fonte: ${data.utms!.source}');
        debugPrint('Campanha: ${data.utms!.campaign}');
      }

      debugPrint('Foi diferido? ${data.isDeferred}');
    });
  }

  @override
  void dispose() {
    // Limpar listener ao desmontar
    _unsubscribe?.call();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomeScreen(),
    );
  }
}
Replay automático: se um deep link já foi processado antes do listener ser registrado, o callback é disparado imediatamente com os dados mais recentes. Isso evita que o app perca o deep link inicial.

Consulta imperativa (getDeepLinkData)

Se preferir consultar os dados de forma imperativa em vez de usar o listener:
Future<void> checkDeepLink() async {
  final data = await NemuTracking.instance.getDeepLinkData();

  if (data != null) {
    debugPrint('App aberto via Smart Link: ${data.deepLinkValue}');
  } else {
    debugPrint('App aberto organicamente');
  }
}

Estrutura do DeepLinkData

class DeepLinkData {
  /// Valor do deep link (ex: "product/456", "category/shoes")
  final String? deepLinkValue;

  /// true = deep link diferido (usuário instalou após clicar)
  final bool isDeferred;

  /// Parâmetros UTM do Smart Link
  final UtmData? utms;
}

class UtmData {
  final String? source;
  final String? medium;
  final String? campaign;
  final String? content;
  final String? term;
}

Histórico de sessão

Histórico da última sessão (last touch)

Consulte os UTMs da interação mais recente do usuário. Responde à pergunta: “quais foram os UTMs da última visita?”
Future<void> checkLastSession() async {
  final session = await NemuTracking.instance.getLastSessionHistory();

  if (session != null) {
    debugPrint('Fonte: ${session.utms.source}');
    debugPrint('Mídia: ${session.utms.medium}');
    debugPrint('Campanha: ${session.utms.campaign}');
    debugPrint('Origem: ${session.origin}');
    debugPrint('Criado em: ${session.createdAt}');
  } else {
    debugPrint('Nenhuma sessão encontrada');
  }
}

Exemplo: anexando UTMs a uma compra

Future<void> trackPurchase(String orderId, double amount) async {
  final lastSession = await NemuTracking.instance.getLastSessionHistory();

  final purchasePayload = {
    'orderId': orderId,
    'amount': amount,
    // Última interação (last touch)
    'lastSource': lastSession?.utms.source,
    'lastCampaign': lastSession?.utms.campaign,
    'lastMedium': lastSession?.utms.medium,
    'origin': lastSession?.origin,
  };

  // Enviar para o seu backend
  // await http.post(Uri.parse('https://sua-api.com/purchases'), body: jsonEncode(purchasePayload));
}

Uso avançado

Modo debug

Ative o modo debug para ver logs detalhados de todas as operações do SDK no console:
NemuTracking.instance.init(
  const NemuInitParams(
    apiKey: 'sua-api-key',
    uriScheme: 'seuapp',
    trackingId: 'seu-tracking-id',
    isDebugMode: true,
  ),
);
Os logs aparecerão prefixados com [NemuSDK] e incluem informações sobre:
  • Processamento de deep links
  • Fluxo de inicialização
  • Erros e falhas de rede
Desative o modo debug em produção. Os logs podem conter informações sensíveis.

Sobrescrita da URL base

Em ambiente de desenvolvimento ou staging, você pode apontar o SDK para uma API diferente:
NemuTracking.instance.init(
  const NemuInitParams(
    apiKey: 'sua-api-key',
    uriScheme: 'seuapp',
    trackingId: 'seu-tracking-id',
    isDebugMode: true,
    baseUrl: 'https://staging-trackings.nemu.com.br',
  ),
);
A baseUrl só é utilizada quando isDebugMode é true. Em modo de produção, o SDK sempre usa a URL padrão.

Tipos exportados

O SDK exporta os seguintes tipos Dart para uso na sua aplicação:
import 'package:nemu_tracking_flutter/nemu_tracking_flutter.dart';

// Classes e tipos disponíveis:
// - NemuTracking        (classe principal — singleton)
// - NemuInitParams      (parâmetros de inicialização)
// - DeepLinkData        (dados do deep link)
// - DeepLinkCallback    (typedef do callback)
// - UtmData             (parâmetros UTM)
// - SessionInfo         (informações da sessão)
// - SessionHistory      (histórico da sessão)

NemuInitParams

class NemuInitParams {
  final String apiKey;
  final String uriScheme;
  final String trackingId;
  final bool isDebugMode;    // Padrão: false
  final String? baseUrl;
}

DeepLinkData

class DeepLinkData {
  final String? deepLinkValue;
  final bool isDeferred;
  final UtmData? utms;
}

UtmData

class UtmData {
  final String? source;
  final String? medium;
  final String? campaign;
  final String? content;
  final String? term;
}

SessionHistory

class SessionHistory {
  final String trackingSessionId;
  final String utmTerm;
  final UtmData utms;
  final String? origin;
  final String createdAt;
}

SessionInfo

class SessionInfo {
  final String trackingSessionId;
  final String utmTerm;
  final String trackingId;
}

DeepLinkCallback

typedef DeepLinkCallback = void Function(DeepLinkData data);

Solução de problemas

Erro “flutter pub get” falha ao resolver o pacote

Erro:
Could not resolve URL "https://github.com/usenemu/sdk-app-tracking-flutter.git"
Solução:
  • Verifique se você tem acesso ao repositório Git do SDK
  • Confirme que suas credenciais Git (SSH ou HTTPS) estão configuradas corretamente
  • Entre em contato com o suporte da Nemu para validar seu acesso

Erro ao inicializar: “NemuTracking must be initialized before use”

Causa: um método do SDK foi chamado antes da inicialização. Solução: certifique-se de que NemuTracking.instance.init() é chamado no initState do widget raiz antes de qualquer outro método do SDK.
Verificações:
  • O Associated Domain está configurado corretamente no Xcode? (formato: applinks:seu-dominio)
  • O arquivo apple-app-site-association está publicado e acessível no domínio? (configurado pela equipe da Nemu)
  • O URI scheme está declarado no Info.plist?
  • O WidgetsFlutterBinding.ensureInitialized() está sendo chamado antes do runApp?

Verificações:
  • Os intent filters estão corretos no AndroidManifest.xml?
  • O domínio está com verificação automática ativa (android:autoVerify="true")?
  • O arquivo assetlinks.json está publicado no domínio? (configurado pela equipe da Nemu)
  • A MainActivity está configurada com launchMode="singleTop" ou singleTask?

Nenhum dado de atribuição retornado

Verificações:
  • O apiKey e o trackingId estão corretos?
  • O Smart Link está configurado e ativo no painel da Nemu?
  • Teste com isDebugMode: true e verifique os logs [NemuSDK] no console

flutter_secure_storage falha no Android

Erro:
PlatformException(Exception encountered, KeyStoreException, ...)
Solução:
  • Verifique se o minSdkVersion no android/app/build.gradle é pelo menos 21
  • Em dispositivos com Android < 6.0, pode ser necessário configurar o flutter_secure_storage com opções específicas. Consulte a documentação do pacote

Precisa de ajuda? Entre em contato com a equipe de suporte da Nemu: