feat: implement FCM push notifications via firebase_messaging
- Add firebase_core + firebase_messaging to pubspec.yaml - Initialize Firebase in main.dart before runApp - NotificationBloc: request permission, get FCM token, register with backend (PATCH /users/me/fcm-token), show local notification on foreground messages, refresh token on rotation - app_view.dart: call initializePushNotifications() on authenticated state Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
5e92d2b908
commit
79f350e85f
@@ -1,14 +1,22 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:user_repository/user_repository.dart';
|
||||
import 'package:prosappco/constansts.dart' show API_BASE_URL;
|
||||
|
||||
part 'notification_event.dart';
|
||||
part 'notification_state.dart';
|
||||
|
||||
// Stub: Firebase messaging removed. Background handler is no longer needed.
|
||||
Future<void> firebaseMessagingBackgroundHandler(dynamic message) async {}
|
||||
// Handles FCM messages when app is terminated/background
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
// Local notification shown by the system tray automatically for data-only
|
||||
// messages; nothing extra needed here.
|
||||
}
|
||||
|
||||
class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
|
||||
final Future<void> Function() requestLocalNotificationPermission;
|
||||
@@ -31,6 +39,60 @@ class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
|
||||
emit(state.copyWith(status: event.status));
|
||||
}
|
||||
|
||||
// Call once after login to register the FCM token and listen for foreground messages.
|
||||
Future<void> initializePushNotifications() async {
|
||||
try {
|
||||
final messaging = FirebaseMessaging.instance;
|
||||
final settings = await messaging.requestPermission();
|
||||
add(NotificationStatusChanged(
|
||||
settings.authorizationStatus == AuthorizationStatus.authorized ||
|
||||
settings.authorizationStatus == AuthorizationStatus.provisional
|
||||
? AuthorizationStatus.authorized
|
||||
: AuthorizationStatus.denied,
|
||||
));
|
||||
|
||||
final token = await messaging.getToken();
|
||||
if (token != null) await _registerToken(token);
|
||||
|
||||
// Refresh token (e.g. after reinstall)
|
||||
messaging.onTokenRefresh.listen(_registerToken);
|
||||
|
||||
// Show local notification when app is in foreground
|
||||
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
||||
final n = message.notification;
|
||||
if (n != null) {
|
||||
showLocalNotification(
|
||||
id: message.hashCode,
|
||||
title: n.title,
|
||||
body: n.body,
|
||||
data: jsonEncode(message.data),
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
log('NotificationBloc.initializePushNotifications error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _registerToken(String token) async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final jwt = prefs.getString('token');
|
||||
if (jwt == null) return;
|
||||
await http.patch(
|
||||
Uri.parse('$API_BASE_URL/users/me/fcm-token'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $jwt',
|
||||
},
|
||||
body: jsonEncode({'token': token}),
|
||||
);
|
||||
log('FCM token registered');
|
||||
} catch (e) {
|
||||
log('NotificationBloc._registerToken error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void requestPermission() async {
|
||||
try {
|
||||
await requestLocalNotificationPermission();
|
||||
|
||||
Reference in New Issue
Block a user