Flutter platform error when querying for data - firebase

Can I know how can I resolve this issue?
Flutter - PlatformException : error, Invalid document reference. Document references must have an even number of segments, but users has 1, null
The following is my code to query the name of my user.
class Auth {
FirebaseUser mCurrentUser;
FirebaseAuth auth;
final firestoreInstance = Firestore.instance;
String name = '';
String uid = "";
void getCurrentUser () async {
mCurrentUser = await auth.currentUser();
uid = mCurrentUser.uid;
print(uid);
}
void getName1() async {
if(uid != null){
DocumentSnapshot document = await Firestore.instance.collection('User').document(uid).get();
name = document.data['Name'];
}
}

Try the following:
void getName1() async {
FirebaseUser mCurrentUser = await auth.currentUser();
DocumentSnapshot document = await Firestore.instance.collection('User').document(mCurrentUser.uid).get();
name = document.data['Name'];
}
Create a local variable, and retrieve the current useruid inside the method getName1()

Related

Flutter - How to retrieve data from firebase and save in sharedpreference

I'm trying to retrieve user name, photourl and email from user and save this info to shared preferences.
What I tried so far:
Future<bool> _saveNameAndEmailPreference(String name, String email) async {
final pref = await SharedPreferences.getInstance();
await pref.setString('name', name);
await pref.setString('email', email);
return pref.commit();
}
Firebase
DatabaseReference reference =
FirebaseDatabase.instance.reference().child("users").child(uid);
reference.once().then((DataSnapshot dataSnapShot) {
var keys = dataSnapShot.value.keys;
var values = dataSnapShot.value;
for (var key in keys) {
Users userinformation = new Users(
values[key]["name"],
values[key]["email"],
values[key]["photourl"],
);
userinformationList.add(userinformation);
}
setState(() {
nametxt = userinformationList[0].name;
emailtxt = userinformationList[1].email;
_saveNameAndEmailPreference(nametxt, emailtxt);
});
});

Using a Firebase Stream as an input for another Stream in Flutter?

Context: I've got two Firebase Streams that work correctly, and they fetch i) a list of user profiles ('users' collection), and ii) a list of locations belonging to each user profile ('locations' collection), and then map them over to a custom User and Location model.
Users stream:
class DatabaseService {
final String uid;
final String friendUid;
final String locationId;
DatabaseService({ this.uid, this.locationId, this.friendUid });
// collection reference for users
final CollectionReference userCollection = FirebaseFirestore.instance.collection('users');
// get users stream
Stream<List<CustomUserModel>> get users {
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
List<CustomUserModel> userList = [];
List<CustomUserModel> _streamMapper(DocumentSnapshot snapshot) {
CustomUserModel individualUser = CustomUserModel(
uid: snapshot.id,
name: snapshot.data()['name'],
username: snapshot.data()['username'],
email: snapshot.data()['email'],
);
userList.add(individualUser);
return userList;
}
return userCollection.doc(uid).snapshots().map(_streamMapper);
}
and the Location Stream:
// collection reference for location
final CollectionReference locationCollection =
FirebaseFirestore.instance.collection('locations');
Stream<List<Location>> get locations {
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
List<Location> _locationListFromSnapshot(QuerySnapshot snapshot) {
List<Location> locationList = [];
snapshot.docs.forEach((element) {
Location individualLocation = Location(
locationId: element.id,
locationName: element.data()['locationName'],
city: element.data()['city'],
);
locationList.add(individualLocation);
});
return locationList;
}
return userLocationCollection.doc(uid).collection('locations').snapshots()
.map(_locationListFromSnapshot);
}
What I want to do is to generate a custom Stream which outputs all the locations for all users - in other words to use the users stream as an input for the locations stream.
I'm not sure what approach works here - I considered adding the users stream as an input parameter to the locations stream and then creating a for-loop, something like this:
Stream<List<Location>> allLocations(Stream<List<CustomUserModel>> users) {
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
List<Location> locationList = [];
users.forEach((element) {
// append user's locations to empty list
locationList.add(locationCollection.doc(element.first.uid).collection('locations')
.snapshots().map(SOME FUNCTION TO MAP A DOCUMENT SNAPSHOT TO THE CUSTOM LOCATION MODEL)
}
return locationList;
but of course I get an error as this returns a list, not a stream. So I've no idea how to proceed...
I hear your pain. I have been there. You were pretty close. Let me explain how I like to do it.
First of all, some clean up:
It seemed like you were not using these in the allLocations functions, so I deleted them
final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;
Second, I changed the return type of the function from Stream<List<Location>> to Stream<Map<String, List<Location>> where the key of the map would be the userId. I find this type useful, because you don't have to worry about the order of users being in sync with the stream.
Third, when you are creating streams, you cannot return, but have to yield from a function. You also have to mark the function async* (* is not a typo).
With this, I propose you use something like this for your allLocations function:
class DataService {
List<Location> convertToLocations(QuerySnapshot snap) {
// This is the function to convert QuerySnapshot into List<Location>
return [Location()];
}
Stream<Map<String, List<Location>>> allLocations(
Stream<List<CustomUserModel>> usersStream) async* {
Map<String, List<Location>> locationsMap = {};
await for (List<CustomUserModel> users in usersStream) {
for (CustomUserModel user in users) {
final Stream<List<Location>> locationsStream = locationCollection
.doc(user.uid)
.collection('locations')
.snapshots()
.map(convertToLocations);
await for (List<Location> locations in locationsStream) {
locationsMap[user.uid] = locations;
yield locationsMap;
}
}
}
}
}
I hope you like this method. Please let me know if something is not what you want. I can make adjustments.

check if firebase record exist and return bool

Trying to check firebase record and perform subsequent logics,
Future<bool> isAlreadyThere(selectedPropertyId) async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
var myMapQuery = Firestore.instance
.collection("props")
.where('xxx', isEqualTo: xxxid)
.where('yyy', isEqualTo: user.uid);
var querySnapshot= await myMapQuery.getDocuments();
var totalEquals= querySnapshot.documents.length;
return totalEquals > 0;
}
and in the onTap() of widget ,
bool isThere=isAlreadyThere(suggestion.documentID) as bool;
if (isThere) {//do stuff here}
errors,
type 'Future' is not a subtype of type 'bool' in type cast
I know its the casting , but tried removing and some other ways as well , wont work.
await is missing in where the query
Future<bool> isAlreadyThere(selectedPropertyId) async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
var myMapQuery = (await Firestore.instance
.collection("props")
.where('xxx', isEqualTo: xxxid)
.where('yyy', isEqualTo: user.uid));
var querySnapshot= await myMapQuery.getDocuments();
var totalEquals= querySnapshot.documents.length;
return totalEquals > 0;
}
Use method like
isAlreadyThere(suggestion.documentID).then((value) {
if (value) {//do stuff here}
});

Updating User Data in Cloud Firestore

I've been having a problem trying to update data from a logged in user. I have the uid, but there has to be a connection between the uid and the collection of users, so that the program picks the right user to update, but I don't know how to make it.
Here's what I have:
FirebaseUser loggedInUser;
final _firestore = Firestore.instance;
//
double _latitude;
double _longitude;
void getCurrentLocation() async {
try {
Position position = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
setState(() {
_latitude = position.latitude;
_longitude = position.longitude;
});
_firestore
.collection('users')
.document('${loggedInUser.uid}')
.updateData({'location': GeoPoint(_latitude, _longitude)});
} catch (e) {
print(e);
}
}
Here's what I've been getting:
E/flutter ( 9187): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(Error performing updateData, NOT_FOUND: No document to update: projects/app-#####/databases/(default)/documents/users/CGyByl58ELc0zirlVjJpv5OWAc42, null)
So it is using the right uid ("CGyByl58ELc0zirlVjJpv5OWAc42")
Here's a screenshot from the Authentication tab of Firebase:
But what I'm trying to get is the name of the collection of this user in the database:
The user id is different than the document id, that's why you get that error since no document exists with the userId. You need to use the userId as the document id:
void addUser() async{
var firebaseUser = await FirebaseAuth.instance.currentUser();
Firestore.instance.collection("users").document(firebaseUser.uid).setData(
{
"age" : 38,
}).then((_){
print("success!");
});
}
Now you will have the userId as the document id and you can update the document using the userId

Flutter & Firebase: Is there a way I can return a specific field from firebase to a function?

users>user Id then:
My aim is to return the user's key from the document and then be able to use that key in other functions.
getUsersKey() async {
final uid = await getCurrentUser();
Firestore.instance.collection('users').document(uid).get();
// Then I want to return the userKey feild
}
You can write the code below:
Future<void> getUsersKey() async {
final uid = await getCurrentUser();
DocumentSnapshot snapshot =
await Firestore.instance.collection('users').document(uid).get();
userKey = snapshot.data['userKey'] //you can get any field value you want by writing the exact fieldName in the data[fieldName]
}

Resources