192 lines
5.9 KiB
Dart
192 lines
5.9 KiB
Dart
import 'dart:developer';
|
|
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
import 'package:injectable/injectable.dart';
|
|
|
|
/// Background message handler — harus top-level function
|
|
@pragma('vm:entry-point')
|
|
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
log('[FCM] Background message: ${message.messageId}');
|
|
}
|
|
|
|
@lazySingleton
|
|
class FcmService {
|
|
FcmService(this._messaging, this._localNotifications);
|
|
|
|
final FirebaseMessaging _messaging;
|
|
final FlutterLocalNotificationsPlugin _localNotifications;
|
|
|
|
/// Inisialisasi FCM: minta permission, set handler, ambil token
|
|
Future<void> initialize() async {
|
|
// Setup local notifications
|
|
await _setupLocalNotifications();
|
|
|
|
// Register background handler
|
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
|
|
|
// Minta permission notifikasi (iOS & Android 13+)
|
|
final settings = await _messaging.requestPermission(
|
|
alert: true,
|
|
badge: true,
|
|
sound: true,
|
|
provisional: false,
|
|
);
|
|
|
|
log('[FCM] Permission status: ${settings.authorizationStatus}');
|
|
|
|
if (settings.authorizationStatus == AuthorizationStatus.authorized ||
|
|
settings.authorizationStatus == AuthorizationStatus.provisional) {
|
|
await _setupTokenHandling();
|
|
_setupForegroundHandler();
|
|
_setupOpenedAppHandler();
|
|
}
|
|
}
|
|
|
|
Future<void> _setupLocalNotifications() async {
|
|
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
|
|
const iosSettings = DarwinInitializationSettings(
|
|
requestAlertPermission: true,
|
|
requestBadgePermission: true,
|
|
requestSoundPermission: true,
|
|
);
|
|
|
|
const initSettings = InitializationSettings(
|
|
android: androidSettings,
|
|
iOS: iosSettings,
|
|
);
|
|
|
|
await _localNotifications.initialize(
|
|
initSettings,
|
|
onDidReceiveNotificationResponse: _onNotificationTapped,
|
|
);
|
|
|
|
// Android notification channel
|
|
const channel = AndroidNotificationChannel(
|
|
'high_importance_channel',
|
|
'High Importance Notifications',
|
|
description: 'This channel is used for important notifications.',
|
|
importance: Importance.high,
|
|
);
|
|
|
|
await _localNotifications
|
|
.resolvePlatformSpecificImplementation<
|
|
AndroidFlutterLocalNotificationsPlugin>()
|
|
?.createNotificationChannel(channel);
|
|
}
|
|
|
|
void _onNotificationTapped(NotificationResponse response) {
|
|
log('[FCM] Notification tapped: ${response.payload}');
|
|
// TODO: handle navigation berdasarkan payload
|
|
}
|
|
|
|
Future<void> _setupTokenHandling() async {
|
|
final token = await getToken();
|
|
log('========================================');
|
|
log('[FCM] TOKEN: $token');
|
|
log('========================================');
|
|
// TODO: kirim token ke server jika diperlukan
|
|
|
|
_messaging.onTokenRefresh.listen((newToken) {
|
|
log('========================================');
|
|
log('[FCM] TOKEN REFRESHED: $newToken');
|
|
log('========================================');
|
|
// TODO: kirim token baru ke server jika diperlukan
|
|
});
|
|
}
|
|
|
|
void _setupForegroundHandler() {
|
|
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
|
log('[FCM] Foreground message: ${message.messageId}');
|
|
log('[FCM] Title: ${message.notification?.title}');
|
|
log('[FCM] Body: ${message.notification?.body}');
|
|
log('[FCM] Data: ${message.data}');
|
|
|
|
// Tampilkan notifikasi lokal saat app di foreground
|
|
final notification = message.notification;
|
|
if (notification != null) {
|
|
_showLocalNotification(
|
|
id: message.hashCode,
|
|
title: notification.title ?? '',
|
|
body: notification.body ?? '',
|
|
payload: message.data.toString(),
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
void _setupOpenedAppHandler() {
|
|
// App dibuka dari notifikasi saat terminated
|
|
_messaging.getInitialMessage().then((RemoteMessage? message) {
|
|
if (message != null) {
|
|
log('[FCM] App opened from terminated via: ${message.messageId}');
|
|
_handleMessageNavigation(message);
|
|
}
|
|
});
|
|
|
|
// App dibuka dari notifikasi saat background
|
|
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
|
log('[FCM] App opened from background via: ${message.messageId}');
|
|
_handleMessageNavigation(message);
|
|
});
|
|
}
|
|
|
|
void _handleMessageNavigation(RemoteMessage message) {
|
|
// TODO: tambahkan logika navigasi berdasarkan message.data
|
|
log('[FCM] Handle navigation for: ${message.data}');
|
|
}
|
|
|
|
Future<void> _showLocalNotification({
|
|
required int id,
|
|
required String title,
|
|
required String body,
|
|
String? payload,
|
|
}) async {
|
|
const androidDetails = AndroidNotificationDetails(
|
|
'high_importance_channel',
|
|
'High Importance Notifications',
|
|
channelDescription: 'This channel is used for important notifications.',
|
|
importance: Importance.high,
|
|
priority: Priority.high,
|
|
showWhen: true,
|
|
icon: 'ic_notification',
|
|
);
|
|
|
|
const iosDetails = DarwinNotificationDetails(
|
|
presentAlert: true,
|
|
presentBadge: true,
|
|
presentSound: true,
|
|
);
|
|
|
|
const details = NotificationDetails(
|
|
android: androidDetails,
|
|
iOS: iosDetails,
|
|
);
|
|
|
|
await _localNotifications.show(id, title, body, details, payload: payload);
|
|
log('[FCM] Local notification shown: $title');
|
|
}
|
|
|
|
/// Ambil FCM token perangkat
|
|
Future<String?> getToken() async {
|
|
try {
|
|
return await _messaging.getToken();
|
|
} catch (e) {
|
|
log('[FCM] Failed to get token: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Subscribe ke topic tertentu
|
|
Future<void> subscribeToTopic(String topic) async {
|
|
await _messaging.subscribeToTopic(topic);
|
|
log('[FCM] Subscribed to topic: $topic');
|
|
}
|
|
|
|
/// Unsubscribe dari topic tertentu
|
|
Future<void> unsubscribeFromTopic(String topic) async {
|
|
await _messaging.unsubscribeFromTopic(topic);
|
|
log('[FCM] Unsubscribed from topic: $topic');
|
|
}
|
|
}
|