SDK do Flutter - Rastreamento de desinstalação

Documento

Rastreamento de desinstalação

Acompanhe as desinstalações de aplicativos para medir a retenção de usuários e otimizar campanhas de reengajamento integrando serviços de notificação por push com o SDK Singular.

Importante: o Google descontinuou as APIs do GCM em abril de 2018. Use o Firebase Cloud Messaging (FCM) para todas as implementações de rastreamento de desinstalação do Android.


Rastreamento de desinstalação do Android

Pré-requisitos

Antes de implementar o rastreamento de desinstalação em seu aplicativo Flutter, configure seu aplicativo na plataforma Singular seguindo o guia Configurando o rastreamento de desinstalação do Android.


Requisitos do sistema

O rastreamento de desinstalação requer o Firebase Cloud Messaging e configurações específicas do dispositivo.

Requisitos do FCM(fonte):

  • Versão do Android: Os dispositivos devem executar o Android 4.1 (API 16) ou superior
  • Serviços do Google Play: Os dispositivos devem ter o aplicativo Google Play Store instalado
  • Suporte de emulador: São suportados emuladores Android 4.1+ com APIs do Google
  • Distribuição: As aplicações podem ser distribuídas fora da Google Play Store, continuando a suportar o controlo de desinstalações

Nota: Os utilizadores de versões do Android não suportadas ou de dispositivos sem o Google Play Services não serão rastreados para desinstalações.


Etapas de implementação

Etapa 1: Instalar os pacotes do Firebase

Adicione as dependências do Firebase ao seu arquivo pubspec.yaml para obter a funcionalidade principal e o suporte a mensagens.

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^2.24.2
  firebase_messaging: ^14.7.10
  singular_flutter_sdk: ^1.8.0

Depois de adicionar as dependências, execute flutter pub get para instalar os pacotes.


Etapa 2: configurar o Firebase

Adicione arquivos de configuração do Firebase ao seu projeto Flutter para Android.

  1. Registre seu aplicativo Android no seu projeto do Firebase Console
  2. Baixe google-services.json e coloque-o em android/app/
  3. Verifique se as dependências de mensagens do Firebase foram adicionadas ao seu projeto

Para obter instruções detalhadas de configuração, consulte Adicionar o Firebase ao seu aplicativo Flutter.


Etapa 3: inicializar o Firebase

Inicialize o Firebase antes de executar seu aplicativo Flutter para habilitar os serviços de mensagens.

Dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Firebase
  await Firebase.initializeApp();

  runApp(MyApp());
}

Etapa 4: solicitar permissão de notificação

Solicite permissões de notificação do usuário (necessário para Android 13+) antes de recuperar o token FCM.

Dart
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

Future<bool> requestNotificationPermission() async {
  if (Platform.isAndroid) {
    // Android 13+ requires explicit permission request
    // Note: firebase_messaging handles this automatically via requestPermission
    final FirebaseMessaging messaging = FirebaseMessaging.instance;
    final NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    return settings.authorizationStatus == AuthorizationStatus.authorized ||
           settings.authorizationStatus == AuthorizationStatus.provisional;
  }

  // iOS permission handled separately
  return true;
}

Passo 5: Recuperar e registar o token FCM

Obtenha o token de dispositivo FCM e registe-o no Singular utilizando registerDeviceTokenForUninstall() após solicitar as permissões.

Dart
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

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

    if (Platform.isAndroid) {
      initializeAndroidUninstallTracking();
    }
  }

  Future<void> initializeAndroidUninstallTracking() async {
    try {
      // Request notification permission
      final hasPermission = await requestNotificationPermission();

      if (!hasPermission) {
        print('Notification permission denied - uninstall tracking unavailable');
        return;
      }

      // Get FCM token
      final token = await FirebaseMessaging.instance.getToken();

      if (token != null) {
        // Register token with Singular for uninstall tracking
        Singular.registerDeviceTokenForUninstall(token);
        print('FCM token registered with Singular: $token');
      } else {
        print('No FCM token available');
      }
    } catch (error) {
      print('Error setting up uninstall tracking: $error');
    }
  }

  Future<bool> requestNotificationPermission() async {
    final messaging = FirebaseMessaging.instance;
    final settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    return settings.authorizationStatus == AuthorizationStatus.authorized ||
           settings.authorizationStatus == AuthorizationStatus.provisional;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

Assinatura do método:

static void registerDeviceTokenForUninstall(String token)

Para obter a documentação completa do método, consulte a referência registerDeviceTokenForUninstall.


Etapa 6: lidar com a atualização do token

Atualize o token FCM com o Singular sempre que ele for atualizado para manter o rastreamento preciso da desinstalação.

Dart
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

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

    if (Platform.isAndroid) {
      setupTokenRefreshListener();
    }
  }

  void setupTokenRefreshListener() {
    // Listen for token refresh events
    FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
      print('FCM token refreshed: $token');

      // Update Singular with new token
      Singular.registerDeviceTokenForUninstall(token);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

Melhores práticas: Os tokens FCM podem ser atualizados a qualquer momento (atualizações de aplicativos, restauração de dispositivos, etc.). Sempre assine o fluxo onTokenRefresh para manter o Singular atualizado com o token mais recente.


Rastreamento de desinstalação do iOS

Pré-requisitos

Configure seu aplicativo iOS na plataforma Singular seguindo o guia Configurando o rastreamento de desinstalação do iOS.

O rastreamento de desinstalação no iOS é baseado na tecnologia do serviço de notificação por push da Apple (APNs). Se o seu aplicativo não for compatível com notificações por push, consulte o guia da Apple para Registrar seu aplicativo com APNs.


Passos de implementação

Etapa 1: Configurar o projeto iOS

Adicione a configuração do Firebase e ative os recursos de notificação por push no seu projeto iOS.

  1. Registre seu aplicativo iOS no seu projeto do Console do Firebase
  2. Baixe GoogleService-Info.plist e adicione-o à pasta Xcode Runner
  3. Nas configurações do projeto Xcode, ative o recurso Notificações por push
  4. Habilite Modos em segundo plano e marque Notificações remotas

Etapa 2: solicitar autorização de notificação do iOS

Solicite permissões de notificação ao utilizador e recupere o token do dispositivo APNS.

Dart
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

Future<bool> requestIOSNotificationPermission() async {
  if (!Platform.isIOS) {
    return false;
  }

  try {
    final FirebaseMessaging messaging = FirebaseMessaging.instance;
    final NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
      provisional: false,
    );

    final authorized =
        settings.authorizationStatus == AuthorizationStatus.authorized ||
        settings.authorizationStatus == AuthorizationStatus.provisional;

    if (authorized) {
      print('iOS notification authorization status: ${settings.authorizationStatus}');
      return true;
    }

    return false;
  } catch (error) {
    print('Error requesting iOS notification permission: $error');
    return false;
  }
}

Passo 3: Recuperar e registar o Token APNS

Obtenha o token do dispositivo APNS e registe-o na Singular utilizando registerDeviceTokenForUninstall() após a autorização ser concedida.

Dart
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

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

    if (Platform.isIOS) {
      initializeIOSUninstallTracking();
    }
  }

  Future<void> initializeIOSUninstallTracking() async {
    try {
      // Request notification authorization
      final hasPermission = await requestIOSNotificationPermission();

      if (!hasPermission) {
        print('Notification permission denied - uninstall tracking unavailable');
        return;
      }

      // Get APNS token
      final apnsToken = await FirebaseMessaging.instance.getAPNSToken();

      if (apnsToken != null) {
        // Register token with Singular for uninstall tracking
        Singular.registerDeviceTokenForUninstall(apnsToken);
        print('APNS token registered with Singular: $apnsToken');
      } else {
        print('No APNS token available');
      }
    } catch (error) {
      print('Error setting up iOS uninstall tracking: $error');
    }
  }

  Future<bool> requestIOSNotificationPermission() async {
    final messaging = FirebaseMessaging.instance;
    final settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    return settings.authorizationStatus == AuthorizationStatus.authorized ||
           settings.authorizationStatus == AuthorizationStatus.provisional;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

Formato do token: O token APNS recuperado de getAPNSToken() já está formatado como uma cadeia hexadecimal, que é o formato correto para o Singular.


Etapa 4: lidar com a atualização do token (iOS)

Atualize o token APNS com o Singular se ele for alterado durante o ciclo de vida do aplicativo.

Dart
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

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

    if (Platform.isIOS) {
      setupIOSTokenRefreshListener();
    }
  }

  void setupIOSTokenRefreshListener() {
    // Listen for token refresh events
    FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
      print('APNS token refreshed: $token');

      // Update Singular with new token
      Singular.registerDeviceTokenForUninstall(token);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

Implementação completa entre plataformas

Configuração unificada do rastreamento de desinstalação

Implemente o rastreamento de desinstalação para as plataformas Android e iOS com tratamento adequado de erros e lógica de atualização de token.

Dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
import 'dart:io';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Firebase
  await Firebase.initializeApp();

  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

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

    // Initialize Singular SDK
    initializeSingularSDK();

    // Setup uninstall tracking
    initializeUninstallTracking();

    // Setup token refresh listener
    setupTokenRefreshListener();
  }

  void initializeSingularSDK() {
    final config = SingularConfig(
      'YOUR_SDK_KEY',
      'YOUR_SDK_SECRET'
    );

    Singular.start(config);
  }

  Future<void> initializeUninstallTracking() async {
    try {
      if (Platform.isAndroid) {
        await setupAndroidUninstallTracking();
      } else if (Platform.isIOS) {
        await setupIOSUninstallTracking();
      }
    } catch (error) {
      print('Error initializing uninstall tracking: $error');
    }
  }

  Future<void> setupAndroidUninstallTracking() async {
    print('Setting up Android uninstall tracking');

    // Request notification permission
    final messaging = FirebaseMessaging.instance;
    final settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    if (settings.authorizationStatus != AuthorizationStatus.authorized &&
        settings.authorizationStatus != AuthorizationStatus.provisional) {
      print('Android notification permission denied');
      return;
    }

    // Get and register FCM token
    final token = await messaging.getToken();

    if (token != null) {
      Singular.registerDeviceTokenForUninstall(token);
      print('Android FCM token registered: $token');
    } else {
      print('Failed to retrieve Android FCM token');
    }
  }

  Future<void> setupIOSUninstallTracking() async {
    print('Setting up iOS uninstall tracking');

    // Request iOS notification authorization
    final messaging = FirebaseMessaging.instance;
    final settings = await messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );

    final authorized =
        settings.authorizationStatus == AuthorizationStatus.authorized ||
        settings.authorizationStatus == AuthorizationStatus.provisional;

    if (!authorized) {
      print('iOS notification permission denied');
      return;
    }

    // Get and register APNS token
    final apnsToken = await messaging.getAPNSToken();

    if (apnsToken != null) {
      Singular.registerDeviceTokenForUninstall(apnsToken);
      print('iOS APNS token registered: $apnsToken');
    } else {
      print('Failed to retrieve iOS APNS token');
    }
  }

  void setupTokenRefreshListener() {
    // Listen for token refresh events (works for both platforms)
    FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
      print('${Platform.operatingSystem.toUpperCase()} token refreshed: $token');
      Singular.registerDeviceTokenForUninstall(token);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Uninstall Tracking Demo'),
        ),
        body: Center(
          child: Text('Uninstall tracking initialized'),
        ),
      ),
    );
  }
}

Notas específicas da plataforma

  • iOS: Certifique-se de que seu aplicativo tenha os direitos de notificação por push necessários e que os APNs estejam configurados corretamente na sua conta de desenvolvedor da Apple
  • Android: Verifique se o FCM está configurado no seu console do Firebase e se o arquivo google-services.json está incluído no seu projeto em android/app/

Verificação e solução de problemas

Verificar a implementação

Confirme se o rastreamento de desinstalação está funcionando corretamente antes de implantar na produção.

  1. Verifique os logs: Verifique se o registro do token aparece nos logs do console com o formato correto
  2. Teste a geração de tokens: Certifique-se de que os tokens sejam gerados na primeira inicialização do aplicativo após a concessão de permissões
  3. Monitorar o painel: Verifique o painel do Singular para obter dados de rastreamento de desinstalação após 24-48 horas
  4. Testar atualização de token: Limpar os dados do aplicativo e verificar se o token é atualizado corretamente quando o aplicativo é reiniciado

Problemas comuns

  • Token não gerado: Verifique se as dependências do Firebase estão instaladas corretamente e se o Firebase está configurado no seu projeto Flutter. Execute flutter pub get depois de adicionar dependências
  • Permissão negada: Verifique se os usuários concederam permissões de notificação. Para Android 13+, é necessária uma solicitação de permissão explícita. Para iOS, os usuários devem autorizar as notificações
  • O token não está sendo atualizado: Certifique-se de que subscreveu o fluxo onTokenRefresh para ambas as plataformas. O ouvinte deve ser configurado na inicialização do seu aplicativo
  • Dados ausentes: Confirme se os dispositivos cumprem os requisitos da plataforma (Android 4.1+ com Google Play Services, iOS com suporte para APNs). Os dispositivos sem estes serviços não podem ser monitorizados
  • Erro de configuração: Verifique se o rastreamento de desinstalação está ativado nas configurações da plataforma Singular para seu aplicativo. Siga os guias de configuração específicos da plataforma vinculados em Pré-requisitos
  • Configuração do Firebase: Para Android, verifique se google-services.json está em android/app/. Para iOS, verifique se GoogleService-Info.plist foi adicionado à pasta Runner do projeto Xcode
  • Inicialização do SDK: Confirme se o Singular SDK foi inicializado antes de chamar registerDeviceTokenForUninstall(). O registro do token deve ocorrer após a inicialização do SDK
  • Deteção de plataforma: Use Platform.isAndroid e Platform.isIOS de dart:io para garantir que o código específico da plataforma seja executado na plataforma correta

Práticas recomendadas

Gerenciamento de token

  • Registro antecipado: Solicite permissões e registre tokens o mais cedo possível no ciclo de vida do aplicativo, de preferência durante a primeira inicialização do aplicativo
  • Tratamento de erros: Implemente um tratamento de erros robusto em torno da recuperação e do registo de tokens para lidar com falhas de forma graciosa
  • Atualização de token: Sempre implemente ouvintes de atualização de token para manter o Singular atualizado quando os tokens forem alterados
  • Experiência do utilizador: Solicitar permissões de notificação no contexto, explicando por que o aplicativo precisa delas para melhorar as taxas de concessão de permissão

Estratégia de teste

  • Teste de desenvolvimento: Teste em dispositivos Android e iOS físicos, pois os emuladores podem ter suporte limitado a notificações por push
  • Fluxos de permissão: Testar cenários em que os utilizadores negam permissões e verificar se a aplicação lida com isso de forma graciosa
  • Persistência de tokens: Verificar se os tokens persistem durante as reinicializações da aplicação e se são actualizados corretamente na atualização
  • Registo: Ativar o registo detalhado durante o desenvolvimento para acompanhar a geração e o registo de tokens