I have this function which is called when I push an Upload Button. It should create a collection in Firestore called userPosts with postId, ownerId, and mediaUrl. Everything is working except username because I have to take it from another collection in Firestore called users. So I have this error: Unhandled Exception: Invalid argument: Instance of 'Future <String>' How can I fix this? Thanks!
final userRef = FirebaseFirestore.instance.collection('users');
final postsRef = FirebaseFirestore.instance.collection('posts');
createPostInFirestore({required String mediaUrl}) {
postsRef.doc(user!.uid).collection("userPosts").doc(postId).set({
"postId": postId,
"ownerId": user!.uid,
"username": userRef.doc(user!.uid).get().then((snapshot) {
return snapshot.data()!['username'].toString();
}),
"mediaUrl": mediaUrl,
"timestamp": timestamp,
"likes": {},
});
}
the get() and set() methods are asynchronous ( needs time to resolve ), so we need to make the function asynchronous with the async keyword, then wait to get the username, after that, wait to update the data with set().
try this:
final userRef = FirebaseFirestore.instance.collection('users');
final postsRef = FirebaseFirestore.instance.collection('posts');
Future<void> createPostInFirestore({required String mediaUrl}) async {
final username = await userRef.doc(user!.uid).get().then((snapshot) {
return (snapshot.data() as Map<String, dynamic>)!['username'].toString();
});
await postsRef.doc(user!.uid).collection("userPosts").doc(postId).set({
"postId": postId,
"ownerId": user!.uid,
"username": username,
"mediaUrl": mediaUrl,
"timestamp": timestamp,
"likes": {},
});
}
then on the onPressed or the method which will execute this function, you need to wait for it also like this:
onPressed: () async {
await createPostInFirestore("your string URL here");
}
now it will work fine
Related
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']
}
);
}
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()
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,
},
);
I'm trying to return a int in this function:
Future<int> getID() async {
DocumentSnapshot idSnapshot = await FirebaseFirestore.instance
.collection("Bestellungen")
.doc("default")
.get();
int currentID = idSnapshot["id"] + 1;
return await currentID;
}
and now I want to use it right here in a onTap-methode of my GestureDetector:
onTap: () {
Scaffold.of(context).hideCurrentSnackBar();
FirebaseFirestore.instance
.collection("Bestellungen")
.add({
"id": getID(),
"item": document["titel"],
"done": false,
"time": getTime(),
});
}
However, if I tap my button the application crashs and it showed me following in VS Code:
I think the problem is connected to the Future, async and await.
I also tried to set an await before getID() but for that a async is also needed but I dont know where to include it in my Stateful class if its the right way.
Thanks for you help.
This won't work:
"id": getID(),
Since getID returns a Future<int>, you're trying to set a Future to thd database, and that is not a supported type.
This should work:
"id": await getID(),
As now you are waiting for the Future to be resolved.
I do not know what happened but after the line where I get my data from firebase the class stops
final DocumentSnapshot doc = await userRef.document(user.id).get();
i tried to print something after that line but it won't run, no idea why...
i want my class to redirected the user to create an account
createUserInFireStore() async {
final GoogleSignInAccount user = googleSignIn.currentUser;
final DocumentSnapshot doc = await userRef.document(user.id).get();
if (!doc.exists) {
final username = await Navigator.push(
context, MaterialPageRoute(builder: (context) => CreateAccount()));
userRef.document(user.id).setData({
'id': user.id,
'username': username,
'photoURL': user.photoUrl,
'email': user.email,
'displayName': user.displayName,
'bio': '',
'timeStamp': timeStamp,
});
}}
but everything stops after the lined I mentioned
the console show this
E/flutter (26482): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(error, FirebaseOptions.getProjectId() cannot be null, null)
in your 'CreateUserinFireStore' function, change the code inside the If condition to following,
if (!doc.exists) {
await userRef.document(user.id).setData({
"id": user.id,
"userNAme": userName,
"photoUrl": user.photoUrl,
"email": user.email,
"displayName": user.displayName,
"bio": "",
"timeStamp": timestamp
});
Navigator.of(context)
.push(MaterialPageRoute(builder: (conetext) => CreateAccount()));
}
I think this link will help you
https://firebase.google.com/docs/database/unity/retrieve-data
Maybe the Future userRef.document(user.id).get() just never returns, try a timeout:
final DocumentSnapshot doc = await userRef.document(user.id).get().timeout(Duration(seconds:5));
if you get a timeout error, check the network connection, or if anything else is blocking your request to firebase.