How to get the current UserId from Firestore? - flutter

I have a collection users where every user has his own document. Now I want to create a subcollection to store more data related to a specific user.
This is the code so far:
class DatabaseService {
Future isUserRegistered(String uid) async{
return await FirebaseFirestore.instance.collection('users')
.where('uid', isEqualTo: uid)
.get();
}
Future registerNewUser(email, password, uid) async{
return await FirebaseFirestore.instance.collection('users')
.doc(uid).set(
{
"email": email,
"password": password,
"uid": uid,
"token": -1,
"userGoal": false,
"userGender": false,
},
);
}
I created a save function but instead of storing data in a subcollection within the document with the current uid, firestore creates a new document named 'uid'. How to fix that?
Future saveInSubcollectionToRemote() async{
Map<String, dynamic> data = UserManager.userWeights;
return await FirebaseFirestore.instance.collection('users')
.doc('uid')
.collection('weights')
.doc(data['userWeight'])
.set(data);
}
class UserManager {
static Map<String, dynamic> userWeights = {'weights': []};
}
EDIT
This is my google authentication, here i get an id but instead of this id i want the uid
///Authentication
void signInGoogle(context) {
DatabaseService handler = new DatabaseService();
_googleSignIn
.signIn()
.then((userdata) => {
print(userdata.toString()),
handler
.isUserRegistered(userdata.id.toString())
.then((value) => {
if (value.docs.length == 0)
{
print("Not registered"),
registerUserToApp(
context, userdata.email.toString(), "password", userdata.id.toString())
}
else
{
print(value.docs[0].data().toString()),
UserManager.userdata = value.docs[0].data(),
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) {
return NavBar();
},
),
(route) => false,
),
}
})
.catchError((e) => {
showAlertDialog(
context, "Error", ""),
})
})
.catchError(
(e) => {showAlertDialog(context, "Error", "")});
}
void registerUserToApp(context, email, password, uid) {
DatabaseService handler = new DatabaseService();
try {
handler.registerNewUser(email, password, uid).then((value) => {
showAlertDialog(context, "Congratulations!",
"You registered to the app"),
});
} catch (error) {
print(error.toString());
showAlertDialog(context, "Error", "");
}
}
}

The uid in your code is typically the unique identified of the user. Firestore itself has no knowledge of such a uid. Instead, the uid comes from the identity provider that you use.
You could use the user ID from Google Sign-in to identify the user in Firestore, but it's more common to sign in with Firebase (too).
Once you've signed in to Google, you can use those credentials to sign in to Firebase Authentication by calling signInWithCredential as shown in the documentation on social authentication.
Once you're signed in to Firebase Authentication you'll get a UID from Firebase Authentication, which you can then use to identify the user's documents in Firestore (and later also secure access to those documents).
Once you've done that, you can get the current user (and from there their UID) through FirebaseAuth.instance.currentUser (once) or FirebaseAuth.instance.currentUser() (continuous) and then again use that in your Firestore calls.

Using doc('uid') is setting your document id as 'uid'. This is whats causing your issue.
Try this:
Future saveInSubcollectionToRemote(String uid) async{
Map<String, dynamic> data = UserManager.userWeights;
return await FirebaseFirestore.instance.collection('users')
.doc(uid)
.collection('weights')
.doc(data['userWeight'])
.set(data);
}
pass in uid like you did in registerNewUser()

Related

Cloud Firestore Database is not displaying users after authentication?

I am trying to do authentication using createUserWithEmailAndPassword .The user iis getting created since I can signin using the email and password which created using createUserWithEmailAndPassword .But my firestore database is not showing the document which I have created using createUserWithEmailAndPassword().
This is my code:
onPressed: () async {
try {
final newuser = await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: email ?? 'error',
password: password ?? 'error',
);
if (newuser != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => home()),
);
}
} catch (e) {
print(e);
}
I have created 3 users with this method and I'm able to login with these credential but these users are not showing in my Firestore Database.
As #ashish already mentioned, you need to store all your extra parameters in the firestore as documents. A better way to implement this is to use different function to register user and create a user document related to that registered user.
Below is an example I created to help out!
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
void main() {
String email = "test#test.com";
String password = "123456";
String firstName = "test";
String lastName = "test";
String phoneNumber = "09876543456";
/// This is the function to create new user
Future<void> createUser(
email,
password,
firstName,
lastName,
phoneNumber,
/*... and any other params you wish to collect */
) async {
// firebase auth
final FirebaseAuth auth = FirebaseAuth.instance;
// firestore db
FirebaseFirestore db = FirebaseFirestore.instance;
// try-catch creation process to accurate exception
try {
final credential = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
// get user id
String userID = credential.user!.uid;
// creating user profile on db
await db.collection("users").doc(userID).set({
"id": userID,
"firstName": firstName,
"lastName": lastName,
"phoneNumber": phoneNumber,
// ... other params
});
print("Signed up successfully!");
} on FirebaseAuthException catch (error) {
print("Something is wrong: $error");
}
}
/// this is the create button
TextButton(
onPressed: () async {
await createUser(
email,
password,
firstName,
lastName,
phoneNumber,
/*... and any other params you wish to collect */
);
},
child: const Text("Create test user"),
);
}
Let me know if you need anything else :). BYE!!!
To store the data in the database you need to use a Firestore instance to store data in firebase Firestore.
onPressed: () async {
try {
final newuser = await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: email ?? 'error',
password: password ?? 'error',
);
await FirebaseFirestore.instance.collection('Users').doc(newuser!.uid).set({
email: newuser.email
});
if (newuser != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => home()),
);
}
} catch (e) {
print(e);
}
}
You need to put the data which you want to store in firestore in set() function.
Firebase Authentication data are show into the login into firebase then on tap of Authentication options menu -> user section , you can view store 3 users data in this section

FireStore when adding FCM token to User doc, deletes and doesn't store UserId

It's my first time using Firestore Cloud Messaging and I want to get the FCM token for each specific device. For quick development, I added the firebase_auth_ui package, which basically outsources the firebase auth login and registration flow. To capture the user's id and store in their doc, I use a simple function that works fine: and gets the job done:
Future<void> addUserDataToFireStore() async {
CollectionReference users = FirebaseFirestore.instance.collection('users');
String uid = FirebaseAuth.instance.currentUser!.uid;
users.doc(uid).set({
'userId': uid,
// 'displayName': currentUser!.displayName!,
});
}
Now, for some reason when I try to access the registration token, my userId gets deleted. When I try to add the token to the same user doc, the userId gets deleted and the fcm token stays. I generate the token as follows:
generateDeviceToken() async {
String? fcmToken = await FirebaseMessaging.instance.getToken();
final userId = FirebaseAuth.instance.currentUser!.uid;
await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.set({'fcmToken': fcmToken});
}
The issue is when I try to call them both. I can't get the two. The doc will fill with either UserId or FCM, but now both. This is what happens when I try to call both,
Perhaps I should make a method that updates fcm token and not set it everytimg?
When you use "set", the entire document is saved with only that one value. Use "update" to update the document and add your token without removing other content.
generateDeviceToken() async {
String? fcmToken = await FirebaseMessaging.instance.getToken();
final userId = FirebaseAuth.instance.currentUser!.uid;
await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.update({'fcmToken': fcmToken});
}
Future<void> addUserDataToFireStore() async {
CollectionReference users = FirebaseFirestore.instance.collection('users');
String uid = FirebaseAuth.instance.currentUser!.uid;
users.doc(uid).update({
'userId': uid,
// 'displayName': currentUser!.displayName!,
});
}
I ended up changing my logic a little bit. Thanks to #Maniak pointing me in the rigth direction. Solution that worked out was the following:
Future<void> addUserDataToFireStore() async {
final userId = FirebaseAuth.instance.currentUser!.uid;
final userDocRef = FirebaseFirestore.instance.collection('users').doc(userId);
final doc = await userDocRef.get();
if (doc.exists) {
return;
} else {
userDocRef.set({
'userId': userId,
});
}
}
Future<void> generateDeviceToken() async {
String? fcmToken = await FirebaseMessaging.instance.getToken();
final userId = FirebaseAuth.instance.currentUser!.uid;
await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.update({'fcmToken': fcmToken});
}

Firestore: Adding a list of custom objects crashes my app

Im currently writing an app in flutter with a firestore backend. I have a document that stores an array of message objects. Id like to be able to write a list of messages to update this document in certain cases. So far, Im doing the following:
Goal:
Firestore method:
Future<void> updateStingrayMessageLikeCount(
Message? message, String? stingrayid, List<Message?> messages) async {
return FirebaseFirestore.instance
.collection('stingrays')
.doc(stingrayid)
.collection('messages')
.doc(message!.chatId)
.update(
{
'messages': [Message.messageListToJson(messages)]
},
)
.then((value) => print("User Updated"))
.catchError((error) => print("Failed to update user: $error"));
}
Mapping functions:
static Map<String, dynamic> toJson(Message? message) => {
'id': message?.id,
'senderId': message?.senderId,
'receiverId': message?.receiverId,
'message': message?.message,
'dateTime': message?.dateTime,
'timeString': message?.timeString,
'likes': message?.likes,
'chatId': message?.chatId,
'commentCount': message?.commentCount,
'userIdsWhoLiked': message?.userIdsWhoLiked,
};
static List messageListToJson(List<Message?> messages) {
List messageList = [];
for (Message? message in messages) {
messageList.add(Message.toJson(message));
}
return messageList;
}
The error occurs after messageList is returned. When firestore tries to set the document, my entire app crashes and i cant get an error code. Any idea what is going on?
In
'messages': [Message.messageListToJson(messages)]
I have brackets surrounding my list.
This has been a worthwhile 3 hours of debugging :].
You will need to add Await in your Future Async function when you call your firebase data :
Future<void> updateStingrayMessageLikeCount(
Message? message, String? stingrayid, List<Message?> messages) async {
return await FirebaseFirestore.instance
.collection('stingrays')
.doc(stingrayid)
.collection('messages')
.doc(message!.chatId)
.update(
{
'messages': [Message.messageListToJson(messages)]
},
)
.then((value) => print("User Updated"))
.catchError((error) => print("Failed to update user: $error"));
}

How to retrieve current user data from firebase?

I tried this way, but i'm getting an error.
The error:
The method 'data' isn't defined for the type 'CollectionReference'. (undefined_method at [myapp] android\app\lib\useracc.dart:32)
void getData() async{
User? user = await FirebaseAuth.instance.currentUser;
var vari =FirebaseFirestore.instance.collection("users");
setState (() {
name = vari.data()['firstname'];
}
);
}
Signup/Register Page
Future<User?> _register(String fname,String lname ,String email, String password) async{
FirebaseAuth _auth = FirebaseAuth.instance;
FirebaseFirestore _firestore = FirebaseFirestore.instance;
try {
UserCredential userCrendetial = await _auth.createUserWithEmailAndPassword(email: emailController.text, password: passwordController.text);
print("Account created Succesfull");
userCrendetial.user!.updateDisplayName(fname);
userCrendetial.user!.updateDisplayName(lname);
await _firestore.collection('users').doc(_auth.currentUser!.uid).set({
"firstname": fname,
"lastname" : lname,
"email": email,
"uid": _auth.currentUser!.uid,
});
return userCrendetial.user;
} catch (e) {
print(e);
return null;
}
}
This is the user account from where i want to fetch info:
Please help. I'm struck here a long time.
You should retrieve the currentUser document then access its data:
void getData() async{
var vari = await FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser.uid)
.get();
setState (() {
name = vari.data()['firstname'];
});
}
if you've saved your user's details in firestore and its document id is the same as that of user ID (which is preferred for ease of access and control), then:
var vari =FirebaseFirestore.instance.collection("users").doc(user!.uid).get();
This gets the document of the user, and the type is DocumentSnapshot.
Map<String,dynamic> userData = vari as Map<String,dynamic>;
now userData is stored in form of Map. suppose you want to access their 'name', so the syntax now goes like userData['name'].
Similarly other fields can be accessed from variable. It's preferred to store userData in a Provider to access it's contents anywhere in your app.
Full code snippet
void getData() async{
User? user = await FirebaseAuth.instance.currentUser;
var vari =FirebaseFirestore.instance.collection("users").doc(user!.uid).get();
Map<String,dynamic> userData = vari as Map<String,dynamic>;
setState (() {
name = userData['firstname']; //or name = userData['name']
}
);
}

Firestore how to save data in a subcollection

I have a collection users where every user has his own document. Now I want to create a subcollection to store more data related to a specific user.
So far my Code looks like this:
class DatabaseService {
Future isUserRegistered(String uid) async{
return await FirebaseFirestore.instance.collection('users')
.where('uid', isEqualTo: uid)
.get();
}
Future registerNewUser(email, password, uid) async{
return await FirebaseFirestore.instance.collection('users')
.doc(uid).set(
{
"email": email,
"password": password,
"uid": uid,
"token": -1,
"userGoal": false,
"userGender": false,
},
);
}
Future saveToRemote() async{
Map<String, dynamic> data = UserManager.userdata;
return await FirebaseFirestore.instance.collection('users')
.doc(data['uid'])
.set(data);
}
class UserManager {
static Map<String, dynamic> userdata = null;
}
How can I store data in a subcollection?
EDIT
I created a new save function but instead of storing data in a subcollection in the document with the current uid, firestore creates a new document named 'uid'. How to fix that?
Future saveInSubcollectionToRemote() async{
Map<String, dynamic> data = UserManager.userWeights;
return await FirebaseFirestore.instance.collection('users')
.doc('uid')
.collection('weights')
.doc(data['userWeight'])
.set(data);
}
Saving to a subcollection is no different from saving to a top-level collection. You build a path to the CollectionReference under the user's document and call add like this:
FirebaseFirestore.instance
.collection('users').doc(uid)
.collection('subcollection').add(
{
"field": value,
},
);