Goal: send notification for all user devices.
Problem description: I've problem with sending notification for one user. After registration, I store FCM token in database for user to send notification for him. Problem is that when user will register multiple times - same token will be stored for more than one user.
Example:
user register with emails test#gmail.com and test2#gmail.com
user login to test#gmail.com
other user send notification to test#gmail.com
notification will be send on both accounts (test#gmail.com and test2#gmail.com) instead of only to test#gmail.com
So here's my question - how to send notification to all devices connected to user instead of all users connected with device?
QuerySnapshot ref = await Firestore.instance.collection('users')
.document(_textController.text)
.collection('tokens')
.getDocuments();
ref.documents.forEach((snapshot) async {
http.Response response = await http.post(
'https://fcm.googleapis.com/fcm/send',
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$serverKey',
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{
'body': 'this is a body',
'title': 'this is a title'
},
'priority': 'high',
'data': <String, dynamic>{
'click_action':
'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done'
},
'to': snapshot.data['token'],
},
),
);
});
Thanks for help!
I FOUND SOLUTION!
All you have to do if you have the same problem is deleting token on logout.
final Firestore store = Firestore.instance;
final FirebaseAuth auth = FirebaseAuth.instance
String token = await _fcm.getToken();
await store
<reference to token document>
.delete();
await auth.signOut();
Related
I am trying to send a text from my device using oneSignal using the token Id, My tokenId shows correctly in the firebase console However as I try to send it I get an error type 'String' is not a subtype of type 'List<String>' Please help me fix it
class _NewMessageState extends State<NewMessage> {
Future<Response> sendNotification(
List<String> tokenIdList, String contents, String heading) async {
return await post(
Uri.parse('https://onesignal.com/api/v1/notifications'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, dynamic>{
"app_id":
"385efff8-************-0fe852ac796c", //kAppId is the App Id that one get from the OneSignal When the application is registered.
"include_player_ids":
tokenIdList, //tokenIdList Is the List of All the Token Id to to Whom notification must be sent.
// android_accent_color reprsent the color of the heading text in the notifiction
"android_accent_color": "FF9976D2",
"small_icon": "ic_stat_onesignal_default",
"large_icon":
"https://www.filepicker.io/api/file/zPloHSmnQsix82nlj9Aj?filename=name.jpg",
"headings": {"en": heading},
"contents": {"en": contents},
}),
);
}
var _enterMessage = '';
//controller for textfield to clear it
final _controller = new TextEditingController();
void _sendMessage() async {
//send message
FocusScope.of(context).unfocus();
final user = FirebaseAuth.instance
.currentUser; //gets the current logged in user gives data like userId
final userData = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get(); //get username
//create new message
FirebaseFirestore.instance.collection('chat').add(
//no authentication token required
{
'text': _enterMessage,
'createdAt':
Timestamp.now(), //TimeStamp is from cloud firestore package\
'userId': user
.uid, //to seperate chat left and right , differentiate between sender and receiver
'username': userData[
'username'], //the message now fetched will already have usernmae
'userImage': userData['image_url'], //access to url
'tokenId': userData['tokenId'],
}, //createdAT to sort the messages in order
);
_controller.clear(); //clear text field
sendNotification(userData["tokenId"], _enterMessage,
"Testing a message");
}
}
The error is on sendNotification(userData["tokenId"], _enterMessage, The data with the tokenId is added correctly in 'chat' collection of firebase. I just can't figure out to use onesignal sendNotification
tokenIdList is list of strings are id as number?
For a simple fix just removed theon List from on List<String>
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
print('Handling a background message ${message.messageId}');
}
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
I can display background notifications with these snippet codes. But is there any way to not display some specific notification in the background?
For example I don't want to display notifications with data = {title = "call"}
from the sender device u have to send data only notification
await http.post(
Uri.parse('https://fcm.googleapis.com/fcm/send'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'key=$serverKey',
},
body: jsonEncode(
<String, dynamic>{
'data': <String, dynamic>{
'id': '2',
'status': 'done'
},
'to': userToken,
"collapse_key": uid,
},
),
);
add conditions in your _firebaseMessagingBackgroundHandler like
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
if(message.data["title"]=="Call")
{
// show nothing
}
else
{
// show notifications
}
}
I am new to react native and I am stuck to send push notification from client side. i have FCM token of every user but need to send a notification to specific user. if you want more clarity please comment and let me know the solution of this
I saw your problem and I have a solution for you. I am already doing it and here's my code.
export const sendPushNotification = async (token, title, body,) => {
//console.log("token==>", token);
const FIREBASE_API_KEY ="<your firebase cloud messaging server key>"
const message = {
registration_ids: [token],
notification: {
title: title,
body: body,
vibrate: 1,
sound: 1,
show_in_foreground: true,
priority: "high",
content_available: true
},
};
let headers = new Headers({
"Content-Type": "application/json",
Authorization: "key=" + FIREBASE_API_KEY
});
let response = await fetch("https://fcm.googleapis.com/fcm/send", {
method: "POST",
headers,
body: JSON.stringify(message)
});
// console.log("=><*", response);
response = await response.json();
// console.log("=><*", response);
};
you can use this function like this
sendPushNotification(
FCMToken,
'title of message',
`this is body of message`,
);
hope it will worked on your side
I'm developing a Flutter app (using Firebase Firestore and FCM) that aims to send notifications to all devices where the user has signed in. When the user signs out from a device, the token will be removed from Firebase. I am looking for a solution to remove the token when user uninstall the app, and in a way, able to store/update the multiple device tokens for a single user without storing the token based on the android id.
What I am using for sending notification:
final FirebaseMessaging firebaseMessaging = FirebaseMessaging();
Future<Map<String, dynamic>> sendAndRetrieveMessage(context) async {
await firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true, provisional: false),
);
await http.post(
'https://fcm.googleapis.com/fcm/send',
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$server_token',
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{
'body': '${widget.content}',
'title': '${widget.title}',
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'status': 'done',
'body': '${widget.content}',
'title': '${widget.title}',
},
'registration_ids': user_tokens,
},
),
).then((http.Response response){
final int statusCode = response.statusCode;
print("RESPONSE BODY: " + response.body);
print("STATUS CODE: " + statusCode.toString());
if (statusCode < 200 || statusCode > 400 || response.body == null) {
throw new Exception("Error while fetching data");
}
});
final Completer<Map<String, dynamic>> completer =
Completer<Map<String, dynamic>>();
firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
completer.complete(message);
},
onLaunch: (Map<String, dynamic> message) async {
completer.complete(message);
},
onResume: (Map<String, dynamic> message) async {
completer.complete(message);
},
);
return completer.future;
}
I have tried these methods for my notification module:
Update signed-in token in Firebase, but updating only one string also means only sending to the latest signed-in device of the user ignoring the other devices
Subscribe user to user_id topic, but topics are limited to 5 per sending notification. My app needs to send to a list of specific users (undetermined by a common topic, but a set of multiple conditions) and I want to avoid using loops as the notification function needs to work simultaneously for all receiving users
Read HTTP response message to detect failed token, the response returns "NotRegistered" but the response does not return the specific failed token
I would like to know if this is doable in Flutter and if there are solutions/simpler methods for this problem. Thank you
Currently I have topics to send push notifications. In particular
all: this is a topic in which I send notifications to everyone
paid: this is a topic in which I send notification only to a small group of users
Today I was upgrading Flutter to the newest firebase package and I encountered this:
<String, dynamic>{
'notification': <String, dynamic>{
'body': 'this is a body',
'title': 'this is a title'
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done'
},
'to': await firebaseMessaging.getToken(),
},
But wait, isn't 'to' meant to send notifications only to a specific device? Why does
'to': await firebaseMessaging.getToken(),
this send a message to all devices? I am confused because the doc says that 'to' is for specific targets too. Thanks!
The to property in an FCM message determines where it is sent to. The value of the to property can be the device token of a single device, it can be the ID of a device group, or it can be a topic.
It sounds like in your actual code you pass a topic in to, while in the sample in your question it passes a device token.
you can use this Firestore api by google to send push notifications
Future<bool> callOnFcmApiSendPushNotifications(List <String> userToken) async
{
final postUrl = 'https://fcm.googleapis.com/fcm/send';
final data = {
"registration_ids" : userToken,
"collapse_key" : "type_a",
"notification" : {
"title": 'NewTextTitle',
"body" : 'NewTextBody',
}
};
final headers = {
'content-type': 'application/json',
'Authorization': constant.firebaseTokenAPIFCM // 'key=YOUR_SERVER_KEY'
};
final response = await http.post(postUrl,
body: json.encode(data),
encoding: Encoding.getByName('utf-8'),
headers: headers);
if (response.statusCode == 200) {
// on success do sth
print('test ok push CFM');
return true;
} else {
print(' CFM error');
// on failure do sth
return false;
}
}