setup fcm
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
@module
|
||||
abstract class FirebaseDi {
|
||||
@preResolve
|
||||
Future<FirebaseApp> get firebaseApp => Firebase.initializeApp();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
/// Background message handler — must be a top-level function.
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
debugPrint('[FCM] Background message: ${message.messageId}');
|
||||
}
|
||||
|
||||
@lazySingleton
|
||||
class FcmService {
|
||||
final FirebaseMessaging _messaging = FirebaseMessaging.instance;
|
||||
|
||||
final FlutterLocalNotificationsPlugin _localNotifications =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
static const _androidChannel = AndroidNotificationChannel(
|
||||
'high_importance_channel',
|
||||
'High Importance Notifications',
|
||||
description: 'This channel is used for important notifications.',
|
||||
importance: Importance.high,
|
||||
);
|
||||
|
||||
/// Call this once during app startup (after Firebase.initializeApp).
|
||||
Future<void> initialize({
|
||||
void Function(RemoteMessage message)? onMessageTap,
|
||||
}) async {
|
||||
// 1. Request permission (iOS + Android 13+)
|
||||
await _requestPermission();
|
||||
|
||||
// 2. Setup local notifications (needed to show heads-up on Android)
|
||||
await _setupLocalNotifications();
|
||||
|
||||
// 3. Register background handler
|
||||
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
|
||||
|
||||
// 4. Foreground message handler
|
||||
FirebaseMessaging.onMessage.listen((message) {
|
||||
debugPrint('[FCM] Foreground message: ${message.messageId}');
|
||||
_showLocalNotification(message);
|
||||
});
|
||||
|
||||
// 5. App opened from notification (background → foreground)
|
||||
FirebaseMessaging.onMessageOpenedApp.listen((message) {
|
||||
debugPrint('[FCM] Notification tapped (background): ${message.messageId}');
|
||||
onMessageTap?.call(message);
|
||||
});
|
||||
|
||||
// 6. App launched from terminated state via notification
|
||||
final initialMessage = await _messaging.getInitialMessage();
|
||||
if (initialMessage != null) {
|
||||
debugPrint('[FCM] App launched from notification: ${initialMessage.messageId}');
|
||||
onMessageTap?.call(initialMessage);
|
||||
}
|
||||
|
||||
// 7. Print FCM token for debugging
|
||||
final token = await getToken();
|
||||
debugPrint('[FCM] Token: $token');
|
||||
}
|
||||
|
||||
Future<void> _requestPermission() async {
|
||||
final settings = await _messaging.requestPermission(
|
||||
alert: true,
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
debugPrint('[FCM] Permission status: ${settings.authorizationStatus}');
|
||||
}
|
||||
|
||||
Future<void> _setupLocalNotifications() async {
|
||||
// Android init
|
||||
const androidInit = AndroidInitializationSettings('@mipmap/launcher_icon');
|
||||
|
||||
// iOS init
|
||||
const iosInit = DarwinInitializationSettings(
|
||||
requestAlertPermission: false,
|
||||
requestBadgePermission: false,
|
||||
requestSoundPermission: false,
|
||||
);
|
||||
|
||||
const initSettings = InitializationSettings(
|
||||
android: androidInit,
|
||||
iOS: iosInit,
|
||||
);
|
||||
|
||||
await _localNotifications.initialize(
|
||||
initSettings,
|
||||
onDidReceiveNotificationResponse: (details) {
|
||||
debugPrint('[FCM] Local notification tapped: ${details.payload}');
|
||||
},
|
||||
);
|
||||
|
||||
// Create Android notification channel
|
||||
if (Platform.isAndroid) {
|
||||
await _localNotifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.createNotificationChannel(_androidChannel);
|
||||
}
|
||||
|
||||
// iOS: show notification even when app is in foreground
|
||||
await _messaging.setForegroundNotificationPresentationOptions(
|
||||
alert: true,
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
}
|
||||
|
||||
void _showLocalNotification(RemoteMessage message) {
|
||||
final notification = message.notification;
|
||||
if (notification == null) return;
|
||||
|
||||
_localNotifications.show(
|
||||
notification.hashCode,
|
||||
notification.title,
|
||||
notification.body,
|
||||
NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
_androidChannel.id,
|
||||
_androidChannel.name,
|
||||
channelDescription: _androidChannel.description,
|
||||
icon: '@mipmap/launcher_icon',
|
||||
importance: Importance.high,
|
||||
priority: Priority.high,
|
||||
),
|
||||
iOS: const DarwinNotificationDetails(),
|
||||
),
|
||||
payload: jsonEncode(message.data),
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the FCM registration token for this device.
|
||||
Future<String?> getToken() => _messaging.getToken();
|
||||
|
||||
/// Subscribe to a topic (e.g. 'all', 'promo').
|
||||
Future<void> subscribeToTopic(String topic) =>
|
||||
_messaging.subscribeToTopic(topic);
|
||||
|
||||
/// Unsubscribe from a topic.
|
||||
Future<void> unsubscribeFromTopic(String topic) =>
|
||||
_messaging.unsubscribeFromTopic(topic);
|
||||
|
||||
/// Listen for token refresh.
|
||||
Stream<String> get onTokenRefresh => _messaging.onTokenRefresh;
|
||||
}
|
||||
Reference in New Issue
Block a user