import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:zixflow/zixflow.dart';
import 'dart:convert';
// Background message handler (top-level function)
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
// Track delivery
final deliveryId = message.data['Zixflow-Delivery-ID'] ?? '';
final deliveryToken = message.data['Zixflow-Delivery-Token'] ?? '';
if (deliveryId.isNotEmpty && deliveryToken.isNotEmpty) {
Zixflow.instance.trackMetric(
deliveryID: deliveryId,
deviceToken: deliveryToken,
event: MetricEvent.delivered,
);
}
}
class PushNotificationHandler {
final FlutterLocalNotificationsPlugin _localNotifications =
FlutterLocalNotificationsPlugin();
String? _fcmToken;
Future<void> initialize() async {
// Register background handler
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// Get and cache FCM token
_fcmToken = await FirebaseMessaging.instance.getToken();
if (_fcmToken != null) {
Zixflow.instance.registerDeviceToken(deviceToken: _fcmToken!);
}
// Listen for token refresh
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
_fcmToken = newToken;
Zixflow.instance.registerDeviceToken(deviceToken: newToken);
});
// Initialize local notifications
await _initializeLocalNotifications();
// Set up message listeners
_setupMessageListeners();
}
Future<void> _initializeLocalNotifications() async {
const androidSettings = AndroidInitializationSettings('@drawable/ic_notification');
const iosSettings = DarwinInitializationSettings();
await _localNotifications.initialize(
InitializationSettings(android: androidSettings, iOS: iosSettings),
onDidReceiveNotificationResponse: _handleNotificationResponse,
);
}
void _setupMessageListeners() {
// Foreground messages
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
_trackDelivery(message);
_showLocalNotification(message);
});
// Background tap
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
_trackOpened(message.data);
_handleDeeplink(message.data['deeplink_url']);
});
// Terminated tap
FirebaseMessaging.instance.getInitialMessage().then((message) {
if (message != null) {
_trackOpened(message.data);
_handleDeeplink(message.data['deeplink_url']);
}
});
}
void _trackDelivery(RemoteMessage message) {
final deliveryId = message.data['Zixflow-Delivery-ID'] ?? '';
final deliveryToken = message.data['Zixflow-Delivery-Token'] ?? _fcmToken ?? '';
if (deliveryId.isNotEmpty && deliveryToken.isNotEmpty) {
Zixflow.instance.trackMetric(
deliveryID: deliveryId,
deviceToken: deliveryToken,
event: MetricEvent.delivered,
);
}
}
void _trackOpened(Map<String, dynamic> data) {
final deliveryId = data['Zixflow-Delivery-ID'] ?? '';
final deliveryToken = data['Zixflow-Delivery-Token'] ?? _fcmToken ?? '';
if (deliveryId.isNotEmpty && deliveryToken.isNotEmpty) {
Zixflow.instance.trackMetric(
deliveryID: deliveryId,
deviceToken: deliveryToken,
event: MetricEvent.opened,
);
}
}
void _handleNotificationResponse(NotificationResponse response) {
if (response.payload == null) return;
final payload = jsonDecode(response.payload!) as Map<String, dynamic>;
// Track opened
_trackOpened(payload);
// If action button was tapped
if (response.actionId != null) {
_trackActionClick(payload, response.actionId!);
}
// Handle navigation
_handleDeeplink(payload['deeplink_url']);
}
void _trackActionClick(Map<String, dynamic> payload, String actionId) {
final actionIndex = int.tryParse(actionId.replaceAll(RegExp(r'\D'), '')) ?? -1;
final buttons = _parseButtons(payload['action_buttons']);
final actionName = (actionIndex >= 0 && actionIndex < buttons.length)
? buttons[actionIndex]['name'] ?? 'Action ${actionIndex + 1}'
: 'Action ${actionIndex + 1}';
final actionDeeplink = (actionIndex >= 0 && actionIndex < buttons.length)
? buttons[actionIndex]['deeplink'] ?? ''
: '';
Zixflow.instance.track(
name: 'Push Notification Action Clicked',
properties: {
'Zixflow-Delivery-ID': payload['Zixflow-Delivery-ID'] ?? '',
'Zixflow-Delivery-Token': payload['Zixflow-Delivery-Token'] ?? '',
'action_index': actionIndex,
'action_name': actionName,
'action_deeplink': actionDeeplink,
},
);
}
List<Map<String, dynamic>> _parseButtons(dynamic raw) {
if (raw == null) return [];
try {
final decoded = raw is String ? jsonDecode(raw) : raw;
return List<Map<String, dynamic>>.from(decoded as List);
} catch (_) {
return [];
}
}
Future<void> _showLocalNotification(RemoteMessage message) async {
// Implementation for showing local notification
}
void _handleDeeplink(String? deeplink) {
if (deeplink == null || deeplink.isEmpty) return;
// Implement your deeplink navigation logic
print('Navigating to: $deeplink');
}
}