flutter Firebase Messaging and sqflite - firebase

I try to send a message from my website to users with Firebase and store the message in the user device with sqflite plugin.
When the application in the foreground, everything works good and message insert in the database. But when the application in the background or completely close it just displays notification and data not inserted in the database.
edit:
After setting click_action for notification, Clicking on the notification data will be saved but if the user dismisses notification data lost.
code
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
#override
void initState() {
prepareFirebaseCloudMessaging();
super.initState();
}
void prepareFirebaseCloudMessaging() {
if (Platform.isIOS) IOSPermission();
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
notificationDataHandler(message);
},
onResume: (Map<String, dynamic> message) async {
notificationDataHandler(message);
},
onLaunch: (Map<String, dynamic> message) async {
notificationDataHandler(message);
},
);
}
void notificationDataHandler(Map<String, dynamic> message) async {
try {
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'MY_DATABASE_FILE.db');
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE Messages (id INTEGER PRIMARY KEY, message TEXT, date TEXT, seen INTEGER)');
});
Message messageStore = Message(
null, message["data"]["message"], message["data"]["date"], false);
await database.insert("Messages", messageStore.toMap());
database.close();
}catch(e){
debugPrint(e.toString());
}
}

Related

Flutter: access SharedPreferences from onBackgroundMessage method of FirebaseMessaging

In my Flutter app I want to save data received from a Firebase Data Message to SharedPreferences.
I have the FirebaseMessaging object configured like this:
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) {
// this works fine
print("onMessage: ");
},
onLaunch: (Map<String, dynamic> data) {
print("onLaunch: ");
},
onResume: (Map<String, dynamic> data) {
print("onResume: ");
},
onBackgroundMessage: myBackgroundMessageHandler,
);
And this is the top-level myBackgroundMessageHandler method:
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) {
// This doesn't work
print("background message"); // this isn't even printed!
SharedPreferences.getInstance().then(...) // here I have to save some data
}
The problem is that myBackgroundMessageHandler seems not to be called at all.
However, the notification is received on the phone.
I also tryed to remove the notification field from the message and in that case myBackgroundMessageHandler is called but the notification doesn't arrive. Is there a way to both receive the notification and trigger the method?

Flutter fcm redirect issue

Hi in one of my flutter project, I am using firebase messaging. First a splash screen and second, the main page of the application. In second page, I implemented the firebase.configure method in the init state as follows. The _navigateToItemDetail method leads to an another page
#override
void initState() {
super.initState();
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
setState(() {
_newNotification = true;
});
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
_navigateToItemDetail(message);
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
_navigateToItemDetail(message);
},
);
and I cam calling the web service for this page after this. But the above method will launch after the webservice calls. So that will cause error in the page redirection. I just put a delay of 4 seconds in web service call, then it will works fine. Is there is any method to solve the issue ? async method available for firebase config ?
I think you need 'onBackgroundMessage'
firebaseMessaging.configure(
//...
onBackgroundMessage: Platform.isIOS ? null : myBackgroundMessageHandler
//...
)
//...
static Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
//Do here something
}
as you can see, it only work on android

firebase flutter push notificaiton

I am trying to send push notification from my php application to my flutter apps via Firebase messaging service. while I start the emulator it receives a token from firebase, then i copy that token and can send message. But I want to store that token to my mysql database once i receive the token. I have 2 scenario. For the first time if user install the apps, i'll insert the token to my database so that i can send message using that token. Now I am confused how to update that token every time the user login(not inserting new token again and again to database).
void setupNotification() async{
_firebaseMessaging.
_firebaseMessaging.getToken().then((token){
//insert the token to user database
saveToken(token);
});
Stream<String> fcmStream = _firebaseMessaging.onTokenRefresh;
fcmStream.listen((token) {
//always update the user database with new token
saveToken(token);
});
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async{
print("message while app is open: $message");
},
onResume: (Map<String, dynamic> message) async{
print("message: $message");
},
onLaunch: (Map<String, dynamic> message) async{
print("message: $message");
}
);
}
I mean how to identify that the token is new or old(updated) ?
You need to create a Locally stored json file in your app using path_provider plugin.This file will get initialized everytime user opens the app and will contain a user Model containing token of user.On the very first page of the app in initState check if the user is not null.
IF NULL:
Get fcm token and store it in database.After storing it in database write the token in your Locally stored file.
ELSE:
Do nothing
Next time when same user opens the app again in initstate it will check if user is not null.This time it will not add a new token in database as our code to insert token into database won't execute.
Example:
We will store user as follows.
class User {
String token;
String platform;
User({
this.token = '',
this.platform = '',
});
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['token'] = this.token;
data['platform'] = this.platform;
return data;
}
User.fromJson(Map<String, dynamic> json): this(
platform: json["platform"],
token: json["token"],
);
}
Local Data file:
class LocalData {
static User _user;
static User get user => _user;
/// Flag for Authentication.
static bool get isTokenAdded=> _user != null;
static loadData() async {
final file = File((await getApplicationDocumentsDirectory()).path + '/data.json');
try {
final data = jsonDecode(await file.readAsString());
_user = User.fromJson(data['user']);
} catch (e) {
print(e);
}
}
static writeData() async {
final file = File((await getApplicationDocumentsDirectory()).path + '/data.json');
await file.writeAsString(jsonEncode({
'user': _user?.toJson(),
}));
}
static void addToken(User user) {
LocalData._user = user;
writeData();
}
static void removeToken() {
LocalData._user = null;
writeData();
}
}
App's main function:
void main() async {
/// Load Local Data.
WidgetsFlutterBinding.ensureInitialized();
await LocalData.loadData();
runApp(YourApp());
}
Now in initState of your apps first page you can check if token is already added in database by
if(LocalData.isTokenAdded()){
//do nothing
}else{
//get token and insert it into database then write it in file:
LocalData.user.token = token;
LocalData.writeData();
}

How to handle fcm when app in in background and play sound on notification arrival in flutter

I want to play a sound when a notification arrives, but I am not able to play. If the app is opened the sound is played and is working fine. But when the app is killed I see the notification but the sound is not played.
Please help how to do it.
#override
void initState()
{
super.initState();
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print('on message $message');
String key = message["data"]["fcm_call_api"];
if (key == "approval") {}
if(Platform.isAndroid)``
{
_playSound();
}
},
onResume: (Map<String, dynamic> message) async {
},
onLaunch: (Map<String, dynamic> message) async {
_playSound();
},
);
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true));
_firebaseMessaging.onIosSettingsRegistered
.listen((IosNotificationSettings settings) {
});
_firebaseMessaging.getToken().then((String token) {
assert(token != null);
setState(() {
_homeScreenText = "Push Messaging token: $token";
});
});
}
void _playSound() {
AudioCache player = new AudioCache();
const alarmAudioPath = "notification_tone.mp3";
player.play(alarmAudioPath);
}
You did not specify on which platform you experience this problem, but overall background messages with the firebase messaging plugin is very limited right now, but there is a PR actively fixing this: https://github.com/flutter/plugins/pull/1900 (related issue: https://github.com/flutter/flutter/issues/22072)
Right now it is documented behavior that messages arriving in the background are only delivered once it is brought back into foreground (on android). See it's documentation at https://github.com/flutter/plugins/tree/master/packages/firebase_messaging#receiving-messages:

Converting Firebase Token to String using Flutter

I have an application that is supposed to send the Firebase Token from my Flutter app to an ASP.Net App Server. The endpoint on the app server works - the request from the Flutter app to the App Server is not working.
The reason it is not working is because when I try to send the token, the token doesn't appear to have arrived yet - it's of type Future. How do I turn that token into a string when it finally arrives?
I've tried turning the token directly into a string in the fcmStream.Listen function, I've also tried turning it into a string using _firebaseMessaging.getToken. Neither of them work
FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
#override
void initState() {
// TODO: implement initState
super.initState();
location.onLocationChanged().listen((value) {
if (this.mounted) {
setState(() {
currentLocation = value;
});
}
});
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) {
print('on message $message');
},
onResume: (Map<String, dynamic> message) {
print('on resume $message');
},
onLaunch: (Map<String, dynamic> message) {
print('on launch $message');
},
);
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true));
String clientToken = _firebaseMessaging.getToken().then((token) {
print("Token Init: " + token.toString());
}
).toString();
BackendService.postToken(clientToken.toString(), "email#gmail.com");
#override
Stream<String> fcmStream = _firebaseMessaging.onTokenRefresh;
fcmStream.listen((token) {
/*print("Token On Refresh: " + token);
BackendService.postToken(token.toString(), "email#gmail.com");*/
}
);
fcmStream.toString();
class BackendService {
static Future<String> postToken(String token, String hostEmail) async {
final responseBody = (await http.get(
'realurlomitted/.../Meets/RegisterDevice?token=$token&hostEmail=$hostEmail')).body;
print(" Response: " + responseBody.toString());
return responseBody.toString();
}
}
Whenever the token.toString prints, it prints the token just fine - I can see that. It just seems like whenever it tries to make the post using http, the token hasn't arrived from whatever getToken is.
If I can turn that Futrure into a string by awaiting it or something, it would solve my problem so that the $token parameter is the token as a string.
More specifically, my request URL should look like:
https://-----/Meets/RegisterDevice?token=c6V49umapn0:Jdsf90832094890s324&hostEmail=email#gmail.com
But it looks like:
https://-----/Meets/RegisterDevice?token=instance of Future<Dynamic>&hostEmail=email#gmail.com
In the Flutter debugger
As you said, awaiting the future will solve your problem. You can write an async function and put the code in your initState inside it and use await, or you can do this:
_firebaseMessaging.getToken().then((token) {
final tokenStr = token.toString();
// do whatever you want with the token here
}
);
This style is now available
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
user.getIdToken().then((token) {
Map<dynamic,dynamic> tokenMap = token.claims;
print(tokenMap['sub']);
});
}
});
so this complete code
#override
void initState() {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
user.getIdToken().then((token) {
Map<dynamic,dynamic> tokenMap = token.claims;
print(tokenMap['sub']);
});
}
});
super.initState();
}

Resources