Firebase OnMessage function refreshes widget - flutter

I am using firebase cloud messaging to push notification on my app that contains a chat page.
I defined my firebase push functions on my main.dart as below:
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
//_showItemDialog(message);
},
onBackgroundMessage: myBackgroundMessageHandler,
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
//_navigateToItemDetail(message);
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
//_navigateToItemDetail(message);
},
);
When chat widget opened and i receive a push notification, my OnMessage method is reached normally.
The question is: what is the best way to refresh my chat page considering that the opened page is not the same one where the reached OnMessage function is declared?

I have used following peice of code for different question on StackOverflow. But the problem there is entirely different from yours, so pasting relevant code.
You can use BLOC here. FCM/NotificationService will send notifications to BLOC/NotificationsBloc and all the widgets that need notifications can subscribe for the notifications. Sample implementation
BLOC
import 'package:rxdart/rxdart.dart';
class LocalNotification {
final String type;
final Map data;
LocalNotification(this.type, this.data);
}
class NotificationsBloc {
NotificationsBloc._internal();
static final NotificationsBloc instance = NotificationsBloc._internal();
final BehaviorSubject<LocalNotification> _notificationsStreamController = BehaviorSubject<LocalNotification>();
Stream<LocalNotification> get notificationsStream {
return _notificationsStreamController;
}
void newNotification(LocalNotification notification) {
_notificationsStreamController.sink.add(notification);
}
void dispose() {
_notificationsStreamController?.close();
}
}
FCM Listener (NotificationService)
import 'package:firebase_messaging/firebase_messaging.dart';
import 'notifications_bloc.dart';
class LocalNotificationService {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
bool _started = false;
LocalNotificationService._internal();
static final LocalNotificationService instance = LocalNotificationService._internal();
// ********************************************************* //
// YOU HAVE TO CALL THIS FROM SOMEWHERE (May be main widget)
// ********************************************************* //
void start() {
if (!_started) {
_start();
_started = true;
_refreshToken();
}
}
void _refreshToken() {
_firebaseMessaging.getToken().then(_tokenRefresh, onError: _tokenRefreshFailure);
}
void _start() {
_firebaseMessaging.requestNotificationPermissions();
_firebaseMessaging.onTokenRefresh.listen(_tokenRefresh, onError: _tokenRefreshFailure);
_firebaseMessaging.configure(
onMessage: _onMessage,
onLaunch: _onLaunch,
onResume: _onResume,
);
}
void _tokenRefresh(String newToken) async {
print(" New FCM Token $newToken");
}
void _tokenRefreshFailure(error) {
print("FCM token refresh failed with error $error");
}
Future<void> _onMessage(Map<String, dynamic> message) async {
print("onMessage $message");
if (message['notification'] != null) {
final notification = LocalNotification("notification", message['notification'] as Map);
NotificationsBloc.instance.newNotification(notification);
return null;
}
if (message['data'] != null) {
final notification = LocalNotification("data", message['data'] as Map);
NotificationsBloc.instance.newNotification(notification);
return null;
}
}
Future<void> _onLaunch(Map<String, dynamic> message) {
print("onLaunch $message");
return null;
}
Future<void> _onResume(Map<String, dynamic> message) {
print("onResume $message");
return null;
}
}
Finally in your Widget
Stream<LocalNotification> _notificationsStream;
#override
void initState() {
super.initState();
_notificationsStream = NotificationsBloc.instance.notificationsStream;
_notificationsStream.listen((notification) {
// TODO: Implement your logic here
print('Notification: $notification');
});
}
#override
void dispose() {
super.dispose();
}
Hope this is what you are looking for.

Related

Can't Read Data of Notification Message - Flutter

I used postman for Creating a Notification.Also I used Firebase Cloud Messaging as backend.
All works, even Notification pops in the android. But I can't read the data in my debug console ,
the Following errors are shown in console:
D/FLTFireMsgReceiver(23051): broadcast received for message
W/FLTFireMsgService(23051): A background message could not be handled in Dart as no onBackgroundMessage handler has been registered.
W/FirebaseMessaging(23051): Missing Default Notification Channel metadata in AndroidManifest. Default value will be used.
The Codes of the Push Notification class:
import 'dart:io';
import 'package:cholachol_drive/globalvariables.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
class PushNotificationService {
// final FirebaseMessaging fcm = FirebaseMessaging.instance;
Future initialize(context) async {
FirebaseMessaging.onMessage.listen((event) {
(Map<String, dynamic> message) async {
// retrieveRideRequestInfo(getRideRequestId(message), context);
print("onMessage: $message");
if (Platform.isAndroid) {
String rideID = message["data"]['ride_id'];
if (rideID != null) {
print("Fucked");
} else {
print('ride_id: $rideID');
}
}
};
});
FirebaseMessaging.onMessageOpenedApp.listen((event) {
(Map<String, dynamic> message) async {
// retrieveRideRequestInfo(getRideRequestId(message), context);
print("onLaunch: $message");
if (Platform.isAndroid) {
String rideID = message["data"]['ride_id'];
if (rideID != null) {
print("Fucked");
} else {
print('ride_id: $rideID');
}
}
};
});
}
Future<String> getToken() async {
String token = await FirebaseMessaging.instance.getToken();
print("token: $token");
DatabaseReference tokerRef = FirebaseDatabase.instance
.ref()
.child("drivers/${currentFirebaseUser.uid}/token");
FirebaseMessaging.instance.subscribeToTopic('alldrivers');
FirebaseMessaging.instance.subscribeToTopic('allusers');
}
}
It seems you don't have onBackgroundMessage implemented in your app. You need to use onBackgroundMessage in order to handle background messages. onMessage will only handle foreground messages.

The method 'configure' isn't defined for the type 'FirebaseMessaging'. Can anyone retype my code correctly? Thanks

Push Notification code.
I don't understand how to implement the new method of implementing a notification in flutter using firebase
import 'dart:io' show Platform;
import 'package:google_maps_flutter/google_maps_flutter.dart';
class PushNotificationService
{
final firebaseMessaging = FirebaseMessaging.instance;
Future initialize(context) async
{
firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
retrieveRideRequestInfo(getRideRequestId(message), context);
},
onLaunch: (Map<String, dynamic> message) async {
retrieveRideRequestInfo(getRideRequestId(message), context);
},
onResume: (Map<String, dynamic> message) async {
retrieveRideRequestInfo(getRideRequestId(message), context);
},
);
}
Seems like you are using the old method. Here is the updated version. Link for your references
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification notification = message.notification;
showNotification(notification);});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print("onMessageOpenedApp: $message");});
FirebaseMessaging.onBackgroundMessage((RemoteMessage message) {
print("onBackgroundMessage: $message");});

flutter firebase cloud messaging

I have 2 projects in my firebase : testProject and mainProject (let's call them like that)
So I implemented the firebase messaging using this tutorial and it works with the json from testProject but after I change to mainProject json, my emulator doesn't receive the notifications. I think this is really weird because in both projects I added the app and I only changed the json.
Do you know why this happens?
this is my code:
class MyAppRC extends StatelessWidget {
static final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
#override
Widget build(BuildContext context) {
final pushNotificationService = PushNotificationService(_firebaseMessaging);
pushNotificationService.initialise();
return MaterialApp(),
home: MyApp(),
);
}
}
Push_notification_service
class PushNotificationService {
final FirebaseMessaging _fcm;
PushNotificationService(this._fcm);
Future initialise() async {
if (Platform.isIOS) {
_fcm.requestNotificationPermissions(IosNotificationSettings());
}
String token = await _fcm.getToken();
debugPrint("FirebaseMessaging token: $token");
_fcm.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
},
);
}
}
Ps. for more details please ask.

Flutter how to access provider.of(context) in FCM backgroundHandler static method?

I have successfully setup background notifications and tested it using postman and all is good.
Now I need to access Provider.of(context) in my backgroundHandler which must be a static method where there is no context.
All I need to do is to perform an action according to the data in the background notification.
Here is my code for initializing FCM (I do it in Splash screen)
Future<void> initFcm() async {
_firebaseMessaging.configure(
onBackgroundMessage: myBackgroundMessageHandler,
onMessage: (msg) async {
print('this is ONMESSAGE $msg');
},
onLaunch: (msg) async {
print('ON LAUNCH');
},
onResume: (msg) async {
print('ON RESUME');
},
);
// For testing purposes print the Firebase Messaging token
deviceToken = await _firebaseMessaging.getToken();
print("FirebaseMessaging token: $deviceToken");
}
static Future<dynamic> myBackgroundMessageHandler(
Map<String, dynamic> message) async {
print(message);
if (message.containsKey('data')) {
// Handle data message
final dynamic data = message['data'];
final orderId = data['order_id'];
//THE PROBLEM IS THAT HERE I DON'T HAVE CONTEXT (coz static method)
Provider.of<Orders>(context, listen: false).setBackgroundStatus(orderId);
}
if (message.containsKey('notification')) {
// Handle notification message
final dynamic notification = message['notification'];
}
// Or do other work.
}
My question is how can I handle tasks in provider in my backgroundHandler which is a static method that doesn't have context?

Opening a popup in a flutter Flutter when onBackgroundMessage gets executed

I have implemented the flutter onBackgroundMessage, that gets triggered when the device receives a Firebase Cloud Messaging data message; i should open a popup, but in this event handler I have no context object. What's the correct way to achieve this ?
I created a class with static methods:
class FirebaseMessagingHandler {
final FirebaseMessaging firebaseMessaging = FirebaseMessaging();
final _bloc = AppModule.to.getBloc<FirebaseMessagingHandlerBloc>();
void setListeners() {
if (Platform.isIOS) _iOSPermission();
getToken();
refreshToken();
}
void getToken() {
firebaseMessaging.getToken().then((token) {
_bloc.saveToken(token);
print('DeviceToken = $token');
});
}
void _iOSPermission() {
firebaseMessaging.configure();
firebaseMessaging.requestNotificationPermissions(IosNotificationSettings(sound: true, badge: true, alert: true));
firebaseMessaging.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
});
}
void refreshToken() {
firebaseMessaging.onTokenRefresh.listen((token) {
_bloc.refreshToken(token);
});
}
void showDialog(BuildContext context, Map<String, dynamic> message) {
// data
}
void showErrorDialog(BuildContext context, dynamic error) {
// data
}
void redirectToPage(BuildContext context, Map<String, dynamic> message) {
// data
}
}
And in my homePage (a page that will always be called when open my app) I call the configure:
class _HomePageState extends State<HomePage> {
final _fcm = FirebaseMessagingHandler();
#override
void initState() {
super.initState();
firebaseCloudMessagingListeners();
}
void firebaseCloudMessagingListeners() {
_fcm.firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
try {
_fcm.showDialog(context, message);
} catch (e) {
_fcm.showErrorDialog(context, e);
}
},
onLaunch: (Map<String, dynamic> message) async {
try {
_fcm.redirectToPage(context, message);
} catch (e) {
_fcm.showErrorDialog(context, e);
}
},
onResume: (Map<String, dynamic> message) async {
try {
_fcm.redirectToPage(context, message);
} catch (e) {
_fcm.showErrorDialog(context, e);
}
},
);
}
}
If you want to display a pop-up within the app, then you don't need onBackgroundMessage - that is only for processing data when a message is received in the background. There is no way to launch the app at the moment the message is received.
However, if a user taps on the notification, the app will launch, and either the onResume or onLaunch callbacks will be called.
You can notify the relevant screen to show a pop up when this happens.
Here's a simple implementation:
In firebase_notification_receiver.dart:
import 'dart:async';
import 'package:firebase_messaging/firebase_messaging.dart';
class NotificationEvent {
final Map<String, dynamic> content;
/// whether the notification was delivered while the app was in the foreground
final bool inApp;
NotificationEvent({this.content, this.inApp = false});
}
class FirebaseNotificationReceiver extends NotificationReceiver {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
StreamController<NotificationEvent> _controller = StreamController<NotificationEvent>.broadcast();
StreamSubscription _streamSubscription;
Function(NotificationEvent) _listener;
init{
// add the rest of the code to initialise firebase here
_firebaseMessaging.configure(
/// Fires when App was in foreground when receiving the notification
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
_controller.sink.add(NotificationEvent(content: message, inApp: true));
},
/// Fires when App was in background when receiving the notification and user has tapped on it
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
_controller.sink.add(NotificationEvent(content: message));
}
/// Fires when App was closed when receiving the notification and user has tapped on it
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
_controller.sink.add(NotificationEvent(content: message));
},
);
_streamSubscription =
_controller.stream.listen(_onStreamEvent, onError: (e) {
print("Notification Stream error $e");
});
}
setListener(Function(NotificationEvent) onData) {
this._listener = onData;
}
}
In main.dart:
// imports go here
void main(){
final notificationReceiver = NotificationReceiver.firebase();
runApp(
MultiProvider(
providers: [
Provider<NotificationReceiver>(
builder: (_) => notificationReceiver),
// more providers go here
],
child: App(), // Your custom app class
),
);
}
In notification_listenable.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class NotificationListenable extends StatefulWidget {
final Widget child;
final Function(NotificationEvent) onData;
const NotificationListenable({#required this.child, this.onData});
#override
_NotificationListenableState createState() => _NotificationListenableState();
}
class _NotificationListenableState extends State<NotificationListenable> {
#override
Widget build(BuildContext context) {
Provider.of<NotificationReceiver>(context).setListener(widget.onData);
return widget.child;
}
}
In my_screen.dart:
/// add your imports here
class MyScreen extends StatefulWidget {
#override
HomePageState createState() => HomePageState();
}
class MyScreenState extends State<MyScreen> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
void _onNotification(NotificationEvent n) {
(_scaffoldKey.currentState)?.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
content: Text("I am a pop up"),
),
),
}
#override
Widget build(BuildContext context) {
return NotificationListenable(
child: YourCustomScreenContent(),
onData: _onNotification,
);
}