I managed to get the data from the firebase real-time database, but I can't use it inside the app. Based on the data, I want to show different screens (Activities). For example: If the user paid then show the "Paid" screen, otherwise show the "Pay Now" screen. I couldn't use the variable used inside eg: paid_status , outside from the below scope. So, I declared it as a global variable in another file.
package_stream.listen((DatabaseEvent event) {
globals.paid_status = event.snapshot.value;
});
With the above code, it works but every time, the user closes the app and login again, the variable value is changed or kept old (after the database is updated). I want to get the value from the database every time the user opens the app or logout and login again.
I used below code to get the data:
setState(() {
DatabaseReference paid_stat = db_reference.child('Records/${uid}/Paid');
// Get the Stream
Stream<DatabaseEvent> package_stream = paid_stat.onValue;
// Subscribe to the stream!
package_stream.listen((DatabaseEvent event) {
globals.paid_status = event.snapshot.value;
});
});
I solved it myself :
I created an async Future function using get() to return the snapshot.
Using the snapshot.value, I showed different screens/activities.
I'm trying to implement a normal authentication system in my app, but I'd like to create a new field for each user that is the "uniqueName" so users can search and add each other in their friends list. I was thinking of adding a textField in the signup form for the uniqueName and updating my User class adding a new String in this way:
class User {
String email;
String name;
String uniqueName;
String userID;
String profilePictureURL;
String appIdentifier;
...
}
Now, since I have this method for the email&password signup:
static firebaseSignUpWithEmailAndPassword(String emailAddress,String password,File? image,String name,) async {
try {
auth.UserCredential result = await auth.FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailAddress, password: password);
String profilePicUrl = '';
if (image != null) {
await updateProgress('Uploading image, Please wait...');
profilePicUrl =
await uploadUserImageToFireStorage(image, result.user?.uid ?? '');
}
User user = User(
email: emailAddress,
name: name,
userID: result.user?.uid ?? '',
profilePictureURL: profilePicUrl);
String? errorMessage = await firebaseCreateNewUser(user);
if (errorMessage == null) {
return user;
} else {
return 'Couldn\'t sign up for firebase, Please try again.';
}
}
how do I have to modify it in order to add this new field in the registration? Since I have to check that the uniqueName insert by the user is effectively unique before creating a new user in the database, what can I do?
Furthermore, I think that it would be cool if this check is made concurrently to the filling of the form, how can I do it? (this is not necessary)
Thanks everyone for the answers
You have to save your users in a collection, then check if uniqueName already exists in the collection. If it exists, return error.
Then when a new user account is created, save the uniqueName.
// this function checks if uniqueName already exists
Future<bool> isDuplicateUniqueName(String uniqueName) async {
QuerySnapshot query = await FirebaseFirestore.instance
.collection('PATH_TO_USERS_COLLECTION')
.where('uniqueName', isEqualTo: uniqueName)
.get();
return query.docs.isNotEmpty;
}
// call the above function inside here.
static firebaseSignUpWithEmailAndPassword(String emailAddress, String password, File? image, String name,) async {
if (await isDuplicateUniqueName(name)) {
// UniqueName is duplicate
// return 'Unique name already exists';
}
// ... the rest of your code. Go ahead and create an account.
// remember to save the uniqueName to users collection.
I suggest doing the following steps:
Create your own users collection (for example users) in Firestore, which you might have done already. (I don't think that User is a good class name, since Firebase Authentication is using the same name. Try MyUser or something.)
Add authentication triggers that will ensure that whenever a Firebase user is added or deleted, it will also be added to or deleted from users collection, use Firebase uid as identifier.
Create a solution to check whether a uniqueName already exists in users collection. You can use a Firestore query, but in this case you have to allow unauthenticated access to read users, at least uniqueName field. (Since the user is not authenticated yet at this point.) A Firebase Cloud Function is another option.
When users enter their desired uniqueName, run the check before creating Firebase user. You can do it when user enters this or when you start the signup process.
If uniqueName is unique, you can try to create Firebase user. Be aware, this step can also fail (for example e-mail name taken etc.). Your users document will be created by the authentication trigger you set up in step 2.
Finally, you have to store this uniqueName in users collection. At this point you will have uid of the newly created Firebase user, so you can use Firestore set command with merge option set to true, so you don't overwrite other data.
It is important to note that you can't guarantee that the Firebase trigger already created the new document in users by the time you arrive to point 6, it is very likely that the trigger is still working or not even started yet. That's why you have to use set both in the authentication trigger and in your own code that sets uniqueName: which "arrives" first, will create the document, and the second will update it.
Also, for the same reason, you have to allow inserts and updates into users collection with Firestore rules. This might sound a little scary, but keep in mind that this is only your own user list to keep track of uniqueName, and authentication is based not on this, but on Firebase Authentication's user management which is well protected.
Last comment: this is not a 100% solution. It is quite unlikely, but theoretically can happen, that some else reserves a uniqueName between you check whether it's unique and the user is actually created. To mitigate this, it is a good idead to make the check just before Firebase user is created. Even in this case a slight chance remains for duplicates.
im currently using a streambuilder to get my order data from firestore for a restaurant app and i want to make something when new order arrives, like a notification or something. How can i know if a new order added to database?
You can subscribe to the stream you give in the "stream" field of your StreamBuilder and use that subscription created to call the onData property of StreamSubscription. It will call the function present inside it whenever new data is added.
Example:
Stream stream = <YOUR STREAM HERE>;
StreamSubscription streamSub;
String actionTaken(dynamic data){
return data;
}
//call the code below in initState if you want to start it with the app.
streamSub = stream.listen(actionTaken);
subscription.onData((data){
});
I have a dating kind of app (without chat support) in which I am showing list of all the profiles that matches certain criteria to user. I have used realtime snapshot listener for this.
query.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
return;
}
if (snapshot != null && !snapshot.isEmpty()) {
List<FeedDetails> feedDetailsList = new ArrayList<>();
for (DocumentSnapshot document : snapshot.getDocuments()) {
FeedDetails feedDetails = document.toObject(FeedDetails.class);
feedDetailsList.add(feedDetails);
}
feedItemAdapter.updateData(feedDetailsList);
}
}
});
In individual profile doc, I have 10-12 field with one online/offline info field. So lets say if no other field changed but online/offline status changed for some profiles then listener is going to read that document again. Is there any way to cut down those reads?
I have a 10-12 field with one online/offline info field. So let's say if no other field changed but online/offline status changed for some profiles then the listener is going to read that document again.
There is no way in Cloud Firestore in which you can listen only to a set of properties of a document and exclude others. It's the entire document or nothing. So if this field online=true is changed into online=false, then you will get the entire document.
Cloud Firestore listeners fire on the document level and always return complete documents. Unfortunately, there is no way to request only a part of the document with the client-side SDK, although this option does exist in the server-side SDK's select() method.
If you don't want to get notified for specific fields, consider adding an extra collection with documents that will contain only those fields. So create that additional collection where each document just contains the data you don't need. In this way, you won't be notified for online/offline changes.
I'm trying to create a simple Crud app with Flutter and Firebase which the records (documents created in FireStore) are related to the user who has been Authenticated. Therefore the Crud functions will only be performed by the user who created the record. IE a user will only be able able to edit/update/delete the records they added in the first place.
I have the firebase_auth and crud functions working nicely with firestore. the issues i'm have is with relating the two. I have chosen to use the users email and the unique identifier (i'm not sure if it's better to use the auto generated user id or not). I have created a separate function for simply returning the current user's email as it's being added to the firestore document. The problem is the first time i add a record the user email returns null, If i submit the form again it starts working fine.
String _userEmail;
_getUserAuthEmail() {
FirebaseAuth.instance.currentUser().then((user){
setState((){this._userEmail = user.email;});
});
return this._userEmail;
}
Which is being called from the onPressed event
onPressed: () {
crudObj.addData({
'itemName': this.itemName,
'userEmail': _getUserAuthEmail(),
}).then((result) {
dialogTrigger(context);
}).catchError((e) {
print(e);
});
},
As i'm just starting out please let me know if there is a better approach. Cheers.
You are getting null because you are not waiting for the currentUser method to settle. Change the _getUserEmail method like this:
String _userEmail;
_getUserAuthEmail() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
setState(() {
_userEmail = user.email;
});
return this._userEmail;
}
Also, about this
"I have chosen to use the users email and the unique identifier (i'm not sure if it's better to use the auto generated user id or not)."
I suggest you using the user's uid for saving user related stuff.