Flutter/Firebase read specific user's file from app - flutter

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();

Related

Why device token generated in every run of the flutter application?

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)

how to call field path from another stateful widget?

by getPhoto() methode which is below i upload user Post to fire which will take this path
and every post has document id
uploading methode
FirebaseFirestore fearbase = FirebaseFirestore.instance;
Reference ref=FirebaseStorage.instance
.ref()
.child("${widget.user}/ProfileData")
.child("Url_$postId");
await ref.putFile(file!);
downloadUrl=await ref.getDownloadURL();
// upload image to firestore
var list=[];
await fearbase.collection("users").doc(widget.user)
.collection("PostData").doc(ido)
.set({"PostUrl":downloadUrl,"ownerName":loggedInUser.username,"userId":loggedInUser.uid,"timestemp":postId,"PostId":ido,"like":FieldValue
.arrayUnion(list)})
.whenComplete(() => Fluttertoast.showToast(msg: "Image Uploaded successfully .i."));
// .then((DocumentReference ido) => ido.update({"PostId":ido.id}))
and i want to get like methode on those post by adding a collection to each document a collection that contains every userId who liked that Post
like methode
void addLike(bool liked) {
// ##################################################
String ido=FirebaseFirestore.instance.collection("PostData").doc().id;
CollectionReference collectReef=FirebaseFirestore.instance.collection("users")
.doc(user!.uid).collection("PostData").doc().collection("LikedUser");
liked =!liked;
if(liked){
DocumentReference reef=collectReef
.doc(ido);
reef.update({
'nameAR':loggedInUser.username,
'CreationTime':DateTime.now(),
});
}else{
DocumentReference reef=FirebaseFirestore.instance.collection("users")
.doc(widget.user).collection("PostData").doc().collection("LikedUsers")
.doc(widget.user);
reef.delete();
}
}
the obstacle is faced is i couldn't add that collection inside document
NOTE
am working on two separate stateful widgets

Read values that change from Firebase on Flutter

I want to read and display values from my Firebase realtime database on flutter. I have managed to read and display a value from my database, but it won't get updated on my app when it's changed. I think I have to use onValue(), but I can't get it working using it.
I am using a future builder to display the data on my app, would I need it using onValue()?
Future getVolumesFirst() async {
final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child('path').get();
if (snapshot.exists) {
print(snapshot.value);
return snapshot.value;
} else {
print('No data available.');
}
}
When you call get() you indeed get the value only once, and it doesn't monitor for updates. The Firebase documentation contains a pretty good example of listening for updates:
DatabaseReference starCountRef =
FirebaseDatabase.instance.ref('posts/$postId/starCount');
starCountRef.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value;
updateStarCount(data);
});
Alternatively, you can just use onValue (without listen) and use that value in a StreamBuilder.
Also see:
other questions about using onValue
questions about using onValue with a StreamBuilder

How to get value of variable from function?

I am trying to get value from a function and store it in a variable but I am getting null.
String imgUrl='';
getUrlSP() async {
SharedPreference preferences
=await SharedPreferences.getInstance();
String Url =
preferences.getString('imageUrl').toString();
setState(() {
imgUrl=Url;
});
print('Printing Image Url inside function:${imgUrl}');
}
outside function
print('Image Url :${imgUrl}');
The results I got in the terminal are
I/flutter (32164): Image Url :
I/flutter (32164): Image Url Stored in Prefs
is:https://firebasestorage.googleapis.com/v0/b/veeluser.appspot.com/o/User%20Images%2FdCP6WEESxfYNDIMqtt57n2BsxYf1?alt=media&token=d864a502-209f-4262-9860-b9d4d3222091
_As from the above results that I got in terminal I am not getting the value of imageUrl outside the function._As I am new to flutter so if there is an error or there is any other solution please share it.
That is expected. since your getUrlSP function is declared as async, you'll need to use await when calling it to ensure it completes (and sets imgUrl) before the rest of your code runs.
So:
await getUrlSP();
print('Image Url :${imgUrl}');
I recommend taking the Flutter codelab Asynchronous programming: futures, async, await
should be used setString() to add a string value to the shared preferences .
Example :
String imgUrl='';
getUrlSP() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('imageUrl', imgUrl);
print('==> ${imgUrl} <==');
}

getting image from firebase storage not working

i am new to flutter and i am trying to make an app which take data from user and make a widget out of it (Classified ads), so far everything is working. but the images from firebase storage is not loading, i have the image url in database ,
for the name its working
Text(snapshot.data.documents[index]['item Name'],),
but for images it gives error
index == null
? Image.asset('assets/afghan.png')
: Image.network(snapshot.data.documents[index]['image 1 Url'],),
the ERROR
Invalid argument(s): No host specified in URI file:///data/user/0/com.example.thisOne/cache/image_picker1382407617041908118.jpg
Change this:
Image.network(snapshot.data.documents[index]['image 1 Url'],)
into this:
Image.file(snapshot.data.documents[index]['image 1 Url'],)
The image url is not valid, this is how you have to save the image in the database:
StorageTaskSnapshot snapshot = await storage
.ref()
.child("images/$imageName")
.putFile(file)
.onComplete;
if (snapshot.error == null) {
final String downloadUrl =
await snapshot.ref.getDownloadURL();
await Firestore.instance
.collection("images")
.add({"url": downloadUrl, "name": imageName});
setState(() {
isLoading = false;
});
You are not saving it correctly to the database, you need to get the downloadUrl from firebase storage which will contain a valid url (https://...) and add it to Firestore. Then you can use Image.network