diff --git a/lib/app_view.dart b/lib/app_view.dart index d6cd9c8..29a2a79 100644 --- a/lib/app_view.dart +++ b/lib/app_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:prosappco/blocs/notification_bloc/notification_bloc.dart'; import 'package:prosappco/screens/authentication/welcome_screen.dart'; import 'package:prosappco/screens/home/home_screen.dart'; import 'package:prosappco/screens/splash/splash_screen.dart'; @@ -36,7 +37,12 @@ class MyAppView extends StatelessWidget { secondaryContainer: Colors.red, ), ), - home: BlocBuilder( + home: BlocConsumer( + listener: (context, state) { + if (state.status == AuthenticationStatus.authenticated) { + context.read().initializePushNotifications(); + } + }, builder: (context, state) { return getScreen(state); }, diff --git a/lib/blocs/notification_bloc/notification_bloc.dart b/lib/blocs/notification_bloc/notification_bloc.dart index 215dc6e..f4740d2 100644 --- a/lib/blocs/notification_bloc/notification_bloc.dart +++ b/lib/blocs/notification_bloc/notification_bloc.dart @@ -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 firebaseMessagingBackgroundHandler(dynamic message) async {} +// Handles FCM messages when app is terminated/background +@pragma('vm:entry-point') +Future firebaseMessagingBackgroundHandler(RemoteMessage message) async { + // Local notification shown by the system tray automatically for data-only + // messages; nothing extra needed here. +} class NotificationBloc extends Bloc { final Future Function() requestLocalNotificationPermission; @@ -31,6 +39,60 @@ class NotificationBloc extends Bloc { emit(state.copyWith(status: event.status)); } + // Call once after login to register the FCM token and listen for foreground messages. + Future 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 _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(); diff --git a/lib/main.dart b/lib/main.dart index 7df7a55..7b06822 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -16,6 +18,9 @@ import 'simple_bloc_observer.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); + // Inicializacion de notificaciones locales await LocalNotifications.initializeLocalNotifications(); diff --git a/pubspec.yaml b/pubspec.yaml index 9e622e3..ed3d690 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,8 @@ environment: dependencies: animate_do: ^3.0.2 + firebase_core: ^3.0.0 + firebase_messaging: ^15.0.0 animated_splash_screen: ^1.3.0 chat_repository: path: packages/chat_repository