A simple Query in flutter/firebase database - firebase

I try to experience Firebase Live database with flutter.
I just would like to get a value in the datasnapshot of the firebase response.
My Firebase
My Code
static Future<User> getUser(String userKey) async {
Completer<User> completer = new Completer<User>();
String accountKey = await Preferences.getAccountKey();
FirebaseDatabase.instance
.reference()
.child("accounts")
.child(accountKey)
.child("users")
.childOrderBy("Group_id")
.equals("54")
.once()
.then((DataSnapshot snapshot) {
var user = new User.fromSnapShot(snapshot.key, snapshot.value);
completer.complete(user);
});
return completer.future;
}
}
class User {
final String key;
String firstName;
Todo.fromJson(this.key, Map data) {
firstname= data['Firstname'];
if (firstname== null) {
firstname= '';
}
}
}
I got Null value for firstname.
I guess I should navigate to the child of snapshot.value. But impossible to manage with foreach, or Map(), ...
Kind regards, Jerome

You are querying with a query and the documentation for Queries (here in JavaScript, but it is valid for all languages), says that "even when there is only a single match for the query, the snapshot is still a list; it just contains a single item. To access the item, you need to loop over the result."
I don't know exactly how you should loop, in Flutter/Dart, over the children of the snapshot but you should do something like the following (in JavaScript):
snapshot.forEach(function(childSnapshot) {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
// ...
});
and assuming that your query returns only one record ("one single match"), use the child snapshot when you do
var user = new User.fromSnapShot(childSnapshot.key, childSnapshot.value);

This will give you Users in reusable dialog. There might be slight disservice to yourself if you don't use stream and stream-builders, the solution below is a one time fetch of the users' collection on FirebaseDB.
class User {
String firstName, groupID, lastName, pictureURL, userID;
User({this.firstName, this.groupID, this.lastName, this.pictureURL, this.userID});
factory User.fromJSON(Map<dynamic, dynamic> user) => User(firstName: user["Firstname"], groupID: user["Group_id"], lastName: user["Lastname"], pictureURL: user["Picturelink"], userID: user["User_id"]);
}
Future<List<User>> users = Firestore.instance.collection("users").snapshots().asyncMap((users) {
return users.documents.map((user) => User.fromJSON(user.data)).toList();
}).single;

Related

Flutter Firebase async query not retrieving data inside a stream function

I am trying to query a User from firebase within another query but for some reason but I can't get the code to work
The function the wont run is await usersRef.doc(uid).get(); and can be found here:
static getUserData(String uid) async {
return await usersRef.doc(uid).get();
}
static DirectMessageListModel getDocData(QueryDocumentSnapshot qdoc, String uid) {
Userdata postUser = Userdata.fromDoc(getUserData(uid));
return DirectMessageListModel.fromDoc(qdoc, postUser);
}
static DirectMessageListModel fromDoc(QueryDocumentSnapshot doc, Userdata altUser) {
return DirectMessageListModel(
doc['chatId'],
doc['lastMsgContent'],
doc['lastMsgType'],
altUser
);
}
parent function:
Stream<List<DirectMessageListModel>> getMeassageList(){
var snaps = FirebaseFirestore.instance.collection('directMessages').where('users', arrayContains: userdata!.uid).snapshots();
List<String> usersListElement = [];
return snaps.map((event) { return event.docs.map((e) {
usersListElement = [e.get('users')[0], e.get('users')[1]];
usersListElement.remove(userdata!.uid);
return DirectMessageListModel.getDocData(e, usersListElement.first);
}).toList();
});
}
You forgot to wait for the future getUserData(uid) to complete.
Try this:
static Future<DocumentSnapshot<Object>> getUserData(String uid) async {
return await usersRef.doc(uid).get();
}
static DirectMessageListModel getDocData(
QueryDocumentSnapshot qdoc,
String uid,
) async {
Userdata postUser = Userdata.fromDoc(await getUserData(uid)); // await here
return DirectMessageListModel.fromDoc(qdoc, postUser);
}
..
// parent function.
// Also wait for the future in the parent function.
// UPDATE BELOW! Define the parent function like this:
Stream<List<Future<DirectMessageListModel>>> getMeassageList() {
var snaps = FirebaseFirestore.instance
.collection('directMessages')
.where('users', arrayContains: userdata!.uid)
.snapshots();
List<String> usersListElement = [];
return snaps.map((event) {
return event.docs.map((e) async {
usersListElement = [e.get('users')[0], e.get('users')[1]];
usersListElement.remove(userdata!.uid);
return await DirectMessageListModel.getDocData(e, usersListElement.first);
}).toList();
});
}
NB: You are fetching user data (either sender/receiver) for each message in directMessages collection. It might be better to store just sender/receiver name in directMessages collection and simply display that. Then if the user clicks on a message, you can then fetch the full sender/receiver data.

I want to get document id from my firestore

I can't get document id from firestore check out the screenshot, you will understand better.
Future updateUserData(String name, String address, String description,
String image, String rating) async {
return await collectionReference.document(uid).setData({
'name': name,
'address': address,
'description': description,
'image': image,
'rating': rating,
'berberid' : ######, // what should I write here
});
}
Instead of null I want to pass that id
Couple of method I already tried
This is returning null
String docid() {
String id;
collectionReference.getDocuments().then((value) => {
value.documents.forEach((element) {
id = element.documentID;
}),
});
return id;
}
This returns the name of collection, such as berbers
documentReference.documentID
Data is loaded from Firebase asynchronously. That means that in this code:
String id;
collectionReference.getDocuments().then((value) =>{
value.documents.forEach((element) {
id= element.documentID;
}),
});
return id;
}
By the time the return id statement executes, the id= element.documentID hasn't run yet. You can most easily check this for yourself by putting breakpoints on these lines and running the code in the debugger, or by adding some log statements:
print("Before getDocuments()");
collectionReference.getDocuments().then((value) =>{
print("Got documents");
});
print("Aftter getDocuments()");
When you run this code, it prints:
Before getDocuments()
After getDocuments()
Got documents
This is likely not what you expected, but does explain why you get null from the function: the data hasn't loaded yet by the time your function exits.
This is completely normal when dealing with asynchronous operations, which is pretty much any modern web/cloud API. So you'll have to deal with it in your code.
The quickest way to do that is to move the code that needs the data into the then() callback that gets the data. So instead of return id you actually put the code that uses the id into the method:
collectionReference.getDocuments().then((value) =>{
String id;
value.documents.forEach((element) {
id= element.documentID;
}),
print(id); // use the id here
});
This could also be a call to your updateUserData function.
Alternatively, you can use async / await and return a Future from your function:
Future<string> getDocumentId() async {
String id;
var value = await collectionReference.getDocuments();
value.documents.forEach((element) {
id= element.documentID;
}),
return id;
}
To call this function you then do:
getDocumentId().then((id) =>{
print(id); // use id here
})
Or you can use another await and do:
var id = await getDocumentId()
print(id);
Here's what I think you should do, you add the documentID to barberid when you create the document instead of updating it which is weird how the user know ?. Here's how you do it:
await Firestore.instance.collection("your collection name").add({
'name': name,
'address': address,
'description': description,
'image': image,
'rating': rating,
}).then((value) => Firestore.instance
.document(value.path)
.updateData({"barberid": value.documentID}));
value there is DocumentReference that's how you get the documentID and then you can update it with Firestore.instance.document("document path") fill the document path with value.path, and update the barberid with documentID should work just fine.

Firebase duplicates presumably because of offline behavior

I have an app (Flutter / Firebase) where people save their predictions for soccer matches.
The way it works is: every person has a document per round of soccer matches.
So when saving a prediction, I check if there is a document for that person for that round, if it does, I write into it, if it doesn't I create a new document.
It was working fine until the amount of users and their circumstances increased. Now, I'm getting a few duplicates and I'm guessing it's because the person saving picks has no internet connection so it doesn't find a document and tries to write a new one.
What would be the best approach to fix this?
Designing a specific reference path for the documents?
Checking for internet connection before attempting any writes?
Or something else?
Future<String> saveToFirebase() async {
return await Firestore.instance.collection('picks')
.add(this.toJson())
.then((value) => Future.value(value.documentID))
.catchError((onError) => Future.error(onError));
}
Future<DocumentReference> picksDocumentReferenceFor(User user, String round, String groupId, String leagueId, { bool create = false, String roundName, String seasonId }) async {
String path = 'picks/';
return Firestore.instance
.collection('picks')
.where('groupId', isEqualTo: groupId)
.where('userId', isEqualTo: user.id)
.where('round', isEqualTo: round).getDocuments()
.then((value) async {
String docPath;
if (value.documents.isEmpty) {
//No document found.. need to create it.
if (create) {
await PicksDocument().saveToFirebase()
.then((documentId) {
docPath = path + documentId;
});
}
} else {
docPath = path + value.documents.first.documentID;
}
print(docPath);
DocumentReference ref;
if (docPath != null) ref = Firestore.instance.document(docPath);
return Future.value(ref);
});
}

How to retrieve value of child data from firebase DB?

I am trying to grab the children data from a certain movie title in a database, but I can only get an output of "Instance of Datasnapshot."
Here is the setup of the DB with the highlighted information I am trying to store in a list:
I tried using the following code with no success:
Future<List<String>> getMovieDetails(String movieName) async {
DataSnapshot movies = await DBRef.child("Movies").child(movieName).once().then((DataSnapshot datasnapshot)
{
print(datasnapshot.value.toString());
});
var moviesMap = Map<String, dynamic>.from(movies.value);
List<String> moviesList = [];
moviesMap.forEach((key, value){
moviesList.add(key);
print('My-Key $key');
print('Movie List: $moviesList');
});
return moviesList;
}
Note: I am passing the selected movie name so I only grab the child information from the movie the user selects. This portion is correctly, if the user clicks on the list tile of Batman, the title will be passed to this getMovieDetails() function.
Try the following:
Future<List<String>> getMovieDetails(String movieName) async {
DataSnapshot movies = await FirebaseDatabase.instance
.reference()
.child("Movies")
.child(movieName)
.once();
var moviesMap = Map<String, dynamic>.from(movies.value);
List<String> moviesList = [];
moviesMap.forEach((key, value) {
moviesList.add(value);
print('My-Key $key');
print('My-Value $value');
});
return moviesList;
}
}
You dont have to use then() since you are using await. Also when you call this method, you need to do for example:
await getMovieDetails("Batman");
I will make the above answer as correct, but the biggest issue was when I did:
moviesList.add(key)
When it should be:
moviesList.add(value)

How do I get the surrounding data related to my userId using flutter and firebase

While using flutter I am able to successfully get the UserId, however I want to be able get more user data (using the UserId)
Surrounding Information:
With the userId how would I go about printing the users; name bio, membership... etc?
Since you are using Realtime Database, then to get the other data, you can do the following:
db = FirebaseDatabase.instance.reference().child("Users");
db.once().then((DataSnapshot snapshot){
Map<dynamic, dynamic> values = snapshot.value;
values.forEach((key,values) {
print(values);
print(values["name"]);
});
});
First add a reference to node Users then use the forEach method to iterate inside the retrieved Map and retrieve the other values.
Try like this :
Future<dynamic> getWeightinKeg() async {
final DocumentReference document = Firestore.instance.collection('you_collection_name').document(user_id);
await document.get().then<dynamic>(( DocumentSnapshot snapshot) async {
final dynamic data = snapshot.data;
print(data['name'].toString())
//Do whatever you want to do with data here.
});
}
getUsers() async {
//here fbPath is reference to your users path
fbPath.once().then((user){
if(user.value !=null){
Map.from(user.value).forEach((k,v){
//here users is List<Map>
setState((){
users.add(v);
});
}
}
});
}
//Or
getUsers() async {
//here fbPath is reference to your users path
//and userListener is StreamSubscription
userListener = fbPath.onChildAdded.listen((user){
//here users is List<Map>
setState((){
users.add(Map.from(user.snapshot.value));
});
});
}
//and cancel in dispose method by calling
userListener.cancel();

Resources