I'm using firebase cloud messaging to send notifications to devices. The problem is that the device token regenrated and added to firestore with different id in every run of the application. I want it to be generated juste once for the first installation of the application.
this is my code :
Future init() async {
_firebaseMessaging.getToken().then((token) {
saveTokens(token);
});
}
Future<void> saveTokens(var token) async {
try {
await _firestore.collection('deviceTokens').add({
'token': token,
});
} catch (e) {
print(e);
}
}
this is how I call it in the main():
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await _msgService.init();
// testFirestore();
FirebaseMessaging.onBackgroundMessage(_messageHandler);
this is _messageHandler function:
Future<void> _messageHandler(RemoteMessage message) async {
print(
'background message ${message.notification!.body} + ${message.notification!.title}');
}
Actually token only refresh on one of that cases:
The app deletes Instance ID
The app is restored on a new device
The user uninstalls/reinstall the app
The user clears app data.
So you need to check in your firebase collection if your token (getted on getToken()) is saved yet before add it. If it already exists in your database, don't save it.
For example:
Future<bool> doesTokenAlreadyExist(String token) async {
final QuerySnapshot result = await Firestore.instance
.collection('deviceTokens')
.where('token', isEqualTo: token)
.limit(1)
.getDocuments();
final List<DocumentSnapshot> documents = result.documents;
return documents.length == 1;
}
The registration token may change when:
The app is restored on a new device
The user uninstalls/reinstall the app
The user clears app data.
More :
Update from Play Store - Token remains same.
When close the application and reopen it - Token remains same.
I recommend you should record that token for the user every time your app launches. Then, you don't face any problems.
(add function to init state of home page of your app)
Related
On startup I call restorePurchases() to get the previous bought subscriptions.
This works fine but I also get subscriptions which where canceled by the user before!
They have status PurchaseStatus.restored and pendingCompletePurchase is true even though I called completePurchase().
How do I know if the purchase is still valid and not canceled by the user?
Future<void> initializeProAccountPurchase(context) async {
available = await IAPService.checkIAPAvailability();
if (available) {
await IAPService.restorePurchases().onError((error, stackTrace) =>
{print("error restore"), print(error.userInfo)});
await getProAccountDetails({'subs_item_purchase', 'pro_account'});
}
}
I have a background notification handler in my flutter app, which is a top-level function like so:
Future<void> _onBackgroundMessage(RemoteMessage message) async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final chatClient = StreamChatClient(STREAM_API_KEY);
print(Utils.user?.uid);
print(Utils.user?.streamToken);
chatClient.connectUser(
su.User(id: Utils.user?.uid ?? ''),
Utils.user?.streamToken ?? '',
connectWebSocket: false,
);
NotificationUtils.handleGetStreamNotification(message, chatClient);
SharedPreferences prefs = await SharedPreferences.getInstance();
int appBadgeCounter = prefs.getInt(appBadgePrefsKey) ?? 0;
FlutterAppBadger.updateBadgeCount(appBadgeCounter + 1);
}
In the notification handler, I need to connect to a separate server using a uid and a token. I have a Utils singleton class where I store an app user as a variable. I initialize this Utils app variable in my home page stateful widget class and persist it throughout the app. This way, I can minimize my number of database calls for user data.
However, in this top-level notification handler, the Utils.user is always null. This top level function exists in my home page stateful widget class, but it is a top level function still.
I want to avoid making database calls for every single notification that the app receives. Is there a way to get this Utils. user data without getting null??
you have to setUser before using it in chatClient.connectUser and along with you can check if user is null or not if null initialize it then it stops does not make extra calls.
Future<void> _onBackgroundMessage(RemoteMessage message) async {
...
await Firebase.initializeApp();
final chatClient = StreamChatClient(STREAM_API_KEY);
if(Utils.user != null) {
Utils.setUser();
}
print(Utils.user?.uid);
print(Utils.user?.streamToken);
...
}
I have question regarding Firebase, I'm making a Flutter App that uses Firebase for user authentication with email & password which is already done, now each user would have specific document (.pdf) uploaded in Storage via console and only that user would have access to his own document that would be shown in the app once he's logged in.
My question is, based on quick research I've noticed that I cannot set a unique ID to a file in storage (if i upload an image for example), so how can I determine that only a specific user can have access to a specific file. I've also taken a look into Firebase Security Rules but I'm not sure if that's enough to determine or do I need custom written code in Flutter as well?
Thanks.
Expanding on #brookyounas's answer:
Get the pdf location from firebase storage then you should have a 'users' collection with documentId of the current user and save the pdf location (link) in that document.
For getting this storage file link, use:
String fileUrl = "";
Future uploadFile() async {
showLoading();
String fileName = ""; //give your file name or just use the firebaseUserId
var ref = FirebaseStorage.instance
.ref()
.child('UserPDFs')
.child('$fileName.pdf');
await ref.putFile(file!).then((val) async {
fileUrl = await val.ref.getDownloadURL();
setState(() {});
log("file url: $fileUrl");
await FirebaseFirestore.instance.collection("users").update({"pdfFileUrl":fileUrl});
dismissLoadingWidget();
});
}
so every time user is logged-in, get the current user-id and then use it.
var firebaseUserId = await FirebaseAuth.instance.currentUser().uid;
String pdfFileUrl = "";
#override
void initState() {
super.initState();
setState(() {
getPdf();
});
}
void getPdf(){
Firestore.instance
.collection("users")
.doc(firebaseUserId) //changed to .doc because .document is deprecated
.get().then({
pdfFileUrl = value[fileUrl];
});
after this use this link with : syncfusion_flutter_pdfviewer - a package for viewing pdfs online
more on this here: A StackOverflow post about this
get .pdf location from firebase storage(access token) then you should have 'users' collection with documentId of current user and save .pdf location in the document.
so every time user is logged-in, get the current user-id and then use it.
var firebaseUserId = await FirebaseAuth.instance.currentUser().uid;
#override
void initState() {
super.initState();
setState(() {
getPdf();
});
}
void getPdf(){
Firestore.instance
.collection("users")
.doc(firebaseUserId) //changed to .doc because .document is deprecated
.get();
I want to create user journey's using AWS Pinpoint. My mobile app is made with Flutter and using Amplify package to create events)
pubspec.yaml
amplify_flutter: ^0.2.4
amplify_analytics_pinpoint: ^0.2.4
I am calling this method to add the event
Future<void> logLogin(String userId) async {
await addEvent('_app.login', {PinPointAnalyticsKeys.userIdProperty: userId});
}
Future<void> addEvent(String eventName, Map<String, String> properties) async {
AnalyticsEvent event = AnalyticsEvent(eventName);
properties.forEach((key, value) {
event.properties.addStringProperty(key, value);
});
Amplify.Analytics.recordEvent(event: event);
await Amplify.Analytics.flushEvents();
}
Method handling the login:
Future<bool> performLogin(String username, String password) async {
//Code that handle login using email/password => userId
await _pinPoint.logLogin(userId);
}
My test journey: a user from a specific segment who does login should get an email right away.
I am having 2 issues:
Events get added to Pinpoint but after a delay (between 10 and 20min approximatively). Shouldn't it get added right away?
When event gets added (after the delay) no journey does get triggered, so no email sent, and the Pinpoint Journey Metrics doesn't change. What could be the reason?
Thank you all for your assistance, if I missed some details please let me know.
I have an API that I'm checking, and when the response changes I need to send a notification to the user. I would like to know how to do this without FCM Push Notifications.
I'm using flutter-local-notifications and background fetch (https://github.com/transistorsoft/flutter_background_fetch) to do it. On the background fetch docs it says that background fetch will do your function once every 15 minutes, which is good enough for me.
This is my initPlatformState():
Future<void> initPlatformState() async {
// Load persisted fetch events from SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
String json = prefs.getString(EVENTS_KEY);
if (json != null) {
setState(() {
_events = jsonDecode(json).cast<String>();
});
}
// Configure BackgroundFetch.
BackgroundFetch.configure(
BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
forceReload: true,
startOnBoot: true,
),
_onBackgroundFetch)
.then((int status) {
print('[BackgroundFetch] SUCCESS: $status');
setState(() {
_status = status;
});
}).catchError((e) {
print('[BackgroundFetch] ERROR: $e');
setState(() {
_status = e;
});
});
// Optionally query the current BackgroundFetch status.
int status = await BackgroundFetch.status;
setState(() {
_status = status;
});
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
}
I'm assuming that what's in the function that gets called in the fetch isn't needed for the question.
I tried this on my phone and simulator, and I used Xcode -> Simulate Background Fetch and it ran properly. It also ran properly when I opened the app. Unfortunately, it didn't run after 15 minutes. Is there something I'm missing?
How would I change my code to make the background fetch to happen every 15 minutes?
Yes, I spent lot of time trying to solve this but was not successful so I switched, I guessed what you are trying to do is to handle your own notification without Firebase or from an API like me, well this is it, after my search I was able to do this with the help of work manager package. So easy to use and implement, try it.
https://pub.dev/packages/workmanager