Soporte de notificaciones push
Realice un seguimiento de las interacciones de los usuarios con las notificaciones push para medir las campañas de reenganche y atribuir las conversiones con precisión mediante la integración de Firebase Cloud Messaging (FCM) con Singular SDK.
Siga las directrices de implementación que se indican a continuación para garantizar que los datos de las notificaciones se transmiten correctamente al SDK de Singular para una atribución adecuada.
Por qué realizar un seguimiento de las notificaciones push: Las notificaciones push impulsan el reenganche, pero el seguimiento requiere una integración correcta. Singular garantiza la correcta atribución de los usuarios que interactúan con las notificaciones, optimizando las campañas de marketing y las estrategias de compromiso.
Guía de implementación
Configurar Firebase Cloud Messaging
Instala los paquetes de Firebase y configura los ajustes específicos de la plataforma para la compatibilidad con notificaciones push en tu aplicación Flutter.
Instalar paquetes Firebase
Añada las dependencias de Firebase a su archivo pubspec.yaml para la funcionalidad básica y la compatibilidad con mensajería.
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.24.2
firebase_messaging: ^14.7.10
singular_flutter_sdk: ^1.8.0
Después de añadir las dependencias, ejecuta flutter pub get para instalar los paquetes.
Configuración de iOS
Registra tu aplicación iOS en Firebase y configura las capacidades de notificaciones push en Xcode.
- Registrar aplicación iOS: Crea una app iOS en tu proyecto Firebase Console.
-
Add Configuration File: Descarga
GoogleService-Info.plisty añádelo a la carpeta Xcode Runner - Habilitar capacidades: En la configuración del proyecto Xcode, habilita la capacidad de notificaciones push
- Habilitar modos de fondo: Habilita Background Modes y marca Remote notifications
Configuración Android
Registre su aplicación Android en Firebase y añada el archivo de configuración a su proyecto.
- Registrar aplicación Android: Crea una app Android en tu proyecto de Consola Firebase
-
Añadir archivo de configuración: Descarga
google-services.jsony colócalo enandroid/app/ - Verificar Dependencias: Asegúrate de que las dependencias de mensajería Firebase están añadidas y los permisos están concedidos en AndroidManifest.xml
Inicializar Firebase en Flutter
Configura Firebase para que se inicialice antes de ejecutar tu app Flutter y configura el gestor de mensajes en segundo plano para las notificaciones recibidas cuando la app no está en primer plano.
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
// Background message handler (must be top-level function)
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Background message: ${message.messageId}');
print('Data: ${message.data}');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
// Set background message handler
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
Configurar rutas de enlaces push
Defina las rutas JSON en las que se encuentran los enlaces de seguimiento de Singular dentro de la estructura de carga útil de la notificación push.
Configure las rutas de enlaces push pasando matrices de cadenas que especifiquen la ruta clave al enlace Singular en su estructura de datos de notificación. Cada ruta es una matriz que representa la estructura anidada de claves.
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
SingularConfig config = SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
);
// Configure paths where Singular links are located in push payload
config.pushNotificationsLinkPaths = [
['sng_link'], // Top-level key
['path', 'to', 'url'], // Nested path
['rootObj', 'nestedObj', 'singularLink'] // Deep nested path
];
Singular.start(config);
Ejemplos de configuración de rutas:
-
Claves simples: Utilice
['sng_link']para las claves de nivel superior de la carga útil. -
Claves anidadas: Utilice
['rootObj', 'nestedObj', 'key']para recorrer estructuras JSON anidadas. - Varias rutas: Defina varias matrices de rutas para comprobar las distintas ubicaciones posibles de los enlaces singulares.
Propiedad de configuración:
List<List<String>>? pushNotificationsLinkPaths
Para obtener documentación completa sobre la configuración, consulte la referencia pushNotificationsLinkPaths.
Gestión específica de la plataforma
Gestión de notificaciones push en Flutter
Implemente escuchadores de mensajes Firebase para capturar datos de notificación en estados de primer y segundo plano y, a continuación, pase los datos a Singular para el seguimiento de la atribución.
import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:singular_flutter_sdk/singular.dart';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
@override
void initState() {
super.initState();
setupPushNotifications();
}
void setupPushNotifications() async {
// Request permission for iOS
NotificationSettings settings = await _firebaseMessaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
// Handle foreground notifications
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Foreground message received: ${message.messageId}');
handleForegroundNotification(message);
});
// Handle notifications that opened the app from background
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('Notification opened app from background: ${message.messageId}');
handleBackgroundNotification(message);
});
// Check for notification that launched the app (terminated state)
RemoteMessage? initialMessage = await _firebaseMessaging.getInitialMessage();
if (initialMessage != null) {
print('App launched from notification: ${initialMessage.messageId}');
handleTerminatedNotification(initialMessage);
}
}
void handleForegroundNotification(RemoteMessage message) {
String title = message.notification?.title ?? '';
String body = message.notification?.body ?? '';
Map<String, dynamic> data = message.data;
print('Title: $title');
print('Body: $body');
print('Data: $data');
// Pass notification data to Singular
if (data.isNotEmpty) {
Singular.handlePushNotification(data);
}
// Display local notification or custom UI
displayLocalNotification(title, body, data);
}
void handleBackgroundNotification(RemoteMessage message) {
print('Processing background notification: ${message.messageId}');
// Pass notification data to Singular
if (message.data.isNotEmpty) {
Singular.handlePushNotification(message.data);
}
// Navigate to specific screen based on notification data
navigateFromNotification(message.data);
}
void handleTerminatedNotification(RemoteMessage message) {
print('Processing terminated state notification: ${message.messageId}');
// Pass notification data to Singular
if (message.data.isNotEmpty) {
Singular.handlePushNotification(message.data);
}
// Navigate to specific screen
navigateFromNotification(message.data);
}
void displayLocalNotification(
String title,
String body,
Map<String, dynamic> data
) {
// Your notification display logic
print('Displaying notification: $title - $body');
}
void navigateFromNotification(Map<String, dynamic> data) {
// Your navigation logic based on notification data
final route = data['route'];
print('Navigating to: $route');
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
Firma del método:
static void handlePushNotification(Map<String, dynamic> notificationData)
Para obtener la documentación completa del método, consulte la referencia handlePushNotification.
Configuración nativa de iOS
Aplicación en estado finalizado
Configure su AppDelegate de iOS para pasar opciones de lanzamiento al SDK de Singular para el seguimiento push automático cuando la aplicación se abre desde un estado finalizado.
En AppDelegate.m o AppDelegate.swift, pase las opciones de lanzamiento a Singular SDK:
Implementación Objective-C
// Import at the top of the file
#import "SingularAppDelegate.h"
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Pass launch options to Singular for push tracking
[SingularAppDelegate shared].launchOptions = launchOptions;
return [super application:application
didFinishLaunchingWithOptions:launchOptions];
}
Implementación Swift
import singular_flutter_sdk
override func application(_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// Pass launch options to Singular for push tracking
if let singularAppDelegate = SingularAppDelegate.shared() {
singularAppDelegate.launchOptions = launchOptions
}
return super.application(application,
didFinishLaunchingWithOptions: launchOptions)
}
Manejo automático: Cuando los usuarios tocan las notificaciones push mientras tu app no se está ejecutando, Singular captura automáticamente la carga útil de la notificación durante el lanzamiento de la app a través de las opciones de lanzamiento.
Configuración nativa de Android
Aplicación en segundo o primer plano
Configure su Android MainActivity para pasar notificaciones al SDK de Singular cuando la aplicación esté en segundo o primer plano.
En su MainActivity, anule onNewIntent para pasar intents a Singular:
Implementación Java
// Add imports at the top
import android.content.Intent;
import com.singular.flutter_sdk.SingularBridge;
// Add to MainActivity class
@Override
public void onNewIntent(Intent intent) {
if(intent.getData() != null) {
setIntent(intent);
super.onNewIntent(intent);
// Pass intent to Singular for push tracking
SingularBridge.onNewIntent(intent);
}
}
Implementación Kotlin
// Add imports at the top
import android.content.Intent
import com.singular.flutter_sdk.SingularBridge
// Add to MainActivity class
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (intent.data != null) {
setIntent(intent)
// Pass intent to Singular for push tracking
SingularBridge.onNewIntent(intent)
}
}
Aplicación en estado finalizado
No se requiere ninguna configuración adicional para las aplicaciones Android en estado finalizado. La capa puente de Flutter gestiona este escenario automáticamente cuando los usuarios tocan las notificaciones.
Gestión automática: Cuando los usuarios tocan las notificaciones push mientras su aplicación no se está ejecutando, Singular captura automáticamente los datos de notificación a través de la integración nativa del puente.
Guía de validación
Verificación de la carga útil en la sesión de inicio
Confirme que los enlaces de notificaciones push se pasan correctamente a Singular inspeccionando la llamada a la API de inicio de sesión.
Singular SDK incluye la carga útil de la notificación push en el parámetro singular_link en la solicitud de inicio de sesión cuando los usuarios tocan las notificaciones.
Ejemplo de solicitud de inicio de sesión:
https://sdk-api-v1.singular.net/api/v1/start?
a=<SDK-Key>
&singular_link=https://singularassist2.sng.link/C4nw9/r1m0?_dl=singular%3A%2F%2Ftest&_smtype=3
&i=net.singular.sampleapp
&s=1740905574084
&sdk=Singular/Flutter-v1.8.0
Verificación alternativa: Utilice la consola de Singular SDK para verificar el seguimiento de las notificaciones push. Compruebe el campo Deeplink URL para confirmar que el enlace de seguimiento se ha capturado correctamente.
Configuración avanzada
Configuración de dominios ESP
Configure dominios externos si envuelve enlaces de Singular dentro de dominios de proveedores de servicios de correo electrónico (ESP) u otros dominios de terceros.
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
// Configure ESP domains for wrapped Singular links
SingularConfig config = SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
);
config.espDomains = ['sl.esp.link', 'custom.domain.com'];
Singular.start(config);
Propiedad de configuración:
List<String>? espDomains
Seguridad Nota: de forma predeterminada, sólo se permiten los dominios sng.linkpredefinidos en la página Administrar enlaces de Singular. Configure los dominios ESP explícitamente si utiliza enlaces envueltos.
Para obtener documentación completa sobre la configuración, consulte la referencia espDomains.
Enrutamiento dinámico de enlaces profundos
Implemente múltiples destinos de enlaces profundos desde una única notificación configurando un enlace de seguimiento Singular con redireccionamientos dinámicos.
Ejemplo de uso: Una notificación de noticias de última hora con múltiples opciones de acción
-
Leer últimas noticias:
newsapp://article?id=12345 -
Trending Topics:
newsapp://trending -
Deportes:
newsapp://sports
En lugar de crear varios enlaces de seguimiento, utilice un enlace Singular y anule las redirecciones dinámicamente en función de la selección del usuario. Consulte Anulación de redireccionamientos en enlaces de seguimiento singulares para obtener detalles sobre la implementación.
Ejemplo de implementación completa
Implementación completa de notificaciones push con configuración de Firebase, configuración de Singular y controladores específicos de la plataforma para aplicaciones Flutter.
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';
// Background message handler (top-level function)
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Background message: ${message.messageId}');
// Singular handles background notifications automatically
if (message.data.isNotEmpty) {
print('Background notification data: ${message.data}');
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
// Set background message handler
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
@override
void initState() {
super.initState();
initializeSingular();
setupPushNotifications();
}
void initializeSingular() {
// Configure Singular SDK
SingularConfig config = SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
);
// Configure push link paths
config.pushNotificationsLinkPaths = [
['sng_link'],
['data', 'url'],
['rootObj', 'nestedObj', 'singularLink']
];
// Configure ESP domains if needed
config.espDomains = ['sl.esp.link'];
// Enable logging for debugging
config.enableLogging = true;
// Initialize SDK
Singular.start(config);
}
void setupPushNotifications() async {
// Request permission (iOS)
NotificationSettings settings = await _firebaseMessaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission');
}
// Get FCM token
String? token = await _firebaseMessaging.getToken();
print('FCM Token: $token');
// Handle foreground notifications
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Foreground notification: ${message.messageId}');
handleForegroundNotification(message);
});
// Handle notification that opened app from background
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('Notification opened app: ${message.messageId}');
handleBackgroundNotification(message);
});
// Check for notification that launched the app
RemoteMessage? initialMessage = await _firebaseMessaging.getInitialMessage();
if (initialMessage != null) {
print('App launched from notification: ${initialMessage.messageId}');
handleTerminatedNotification(initialMessage);
}
}
void handleForegroundNotification(RemoteMessage message) {
final title = message.notification?.title ?? '';
final body = message.notification?.body ?? '';
final data = message.data;
print('Foreground - Title: $title, Body: $body');
// Pass notification data to Singular
if (data.isNotEmpty) {
Singular.handlePushNotification(data);
}
// Display notification to user
showNotificationDialog(title, body, data);
}
void handleBackgroundNotification(RemoteMessage message) {
print('Processing background notification');
// Pass notification data to Singular
if (message.data.isNotEmpty) {
Singular.handlePushNotification(message.data);
}
// Navigate based on notification data
navigateFromNotification(message.data);
}
void handleTerminatedNotification(RemoteMessage message) {
print('Processing terminated state notification');
// Pass notification data to Singular
if (message.data.isNotEmpty) {
Singular.handlePushNotification(message.data);
}
// Navigate based on notification data
navigateFromNotification(message.data);
}
void showNotificationDialog(
String title,
String body,
Map<String, dynamic> data
) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text(body),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
navigateFromNotification(data);
},
child: Text('Open'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Dismiss'),
),
],
),
);
}
void navigateFromNotification(Map<String, dynamic> data) {
// Parse notification data for routing
final route = data['route'];
final productId = data['product_id'];
print('Navigating to: $route');
// Navigate to appropriate screen
if (route == 'product' && productId != null) {
navigatorKey.currentState?.pushNamed('/product/$productId');
} else if (route == 'promo') {
navigatorKey.currentState?.pushNamed('/promo');
} else {
navigatorKey.currentState?.pushNamed('/');
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
navigatorKey: navigatorKey,
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/product': (context) => ProductScreen(),
'/promo': (context) => PromoScreen(),
},
);
}
}
// Placeholder screens
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(child: Text('Home Screen')),
);
}
}
class ProductScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Product')),
body: Center(child: Text('Product Screen')),
);
}
}
class PromoScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Promo')),
body: Center(child: Text('Promo Screen')),
);
}
}
Consideraciones importantes
Notas de implementación
-
No hay controlador de devolución de llamada: A diferencia de
singularLinksHandler, la función de notificaciones push no proporciona callbacks de carga útil. Implemente su propia lógica de enlace profundo para dirigir a los usuarios a contenidos específicos dentro de su aplicación. - Flujo de atribución: Cuando los usuarios tocan las notificaciones, Singular recupera la carga útil y la incluye en el evento de inicio de sesión activado por la inicialización del SDK. El backend procesa estos datos para atribuir el punto de contacto de la notificación push y registrar el seguimiento del reenganche.
-
Restricciones de dominio: Por defecto, sólo se permiten dominios de enlace singulares (
sng.link) de la página Gestionar enlaces. Configure dominios ESP explícitamente para enlaces envueltos utilizandoespDomains - Diferencias entre plataformas: iOS requiere la configuración de AppDelegate para el estado finalizado, mientras que Android lo gestiona automáticamente a través del módulo puente.
- Pruebas: Habilite el registro del SDK durante el desarrollo para verificar que los datos de las notificaciones push se capturan y procesan correctamente.
Éxito: Siguiendo estos pasos, su aplicación ahora realiza un seguimiento de las interacciones de notificaciones push con Singular, lo que mejora la información sobre el rendimiento de la campaña y garantiza una atribución precisa del reenganche.