I am trying to retrieve the values of doctors map object as list of {times, email} for a specific date.
this is the code i have tried so far:
Stream<List> fromDateToList(int day, int month, int year){
var val = usersInfoCollection
.doc(uid).get();
Map<String, dynamic> data = {};
return usersInfoCollection
.doc(uid)
.snapshots()
.map((doc) {
if (doc['doctors'] is Map) {
return doc['doctors'].values.toList();
} else {
return [];
}
});
}
Related
I have two queries for Firebase. One that returns a list of posts ("PostsModel") and one that returns a list of liked posts for a particular user ("LikedPostsModel"). I want to set an attribute called "liked" within "PostsModel" to true if it's also in the liked posts streams.
Pseudo Code:
/* Returns PostsModel, ex. [PostsModel(id: 1, liked: false), PostsModel(id: 2, liked: false), PostsModel(id: 3, liked: false), ...] */
Stream<List<PostsModel>> getListOfPosts();
/* Returns LikedPostsModel, ex. [LikedPostsModel(postsmodel_id: 2), ...] */
Stream<List<LikedPostsModel>> getListOfLikedPosts();
/* Returns combining PostsModel and LikedPostsModel, ex. [PostsModel(id: 1, liked: false), PostsModel(id: 2, liked: true), PostsModel(id: 3, liked: false), ...] */
Stream<List<LikedPostsModel>> getCombinedPosts() {
var listOfPosts = getListOfPosts();
var listOfLikedPosts = getListOfLikedPosts();
if PostsModel.id within listOfPosts is matches LikedPostsModel.postsmodel_id within listOfLikedPosts, then set PostsModel.liked = true;
return Stream<List<PostsModel>> with some fields "liked" set to true;
}
Actual Code:
My query to retrieve all posts:
Stream<List<PostsModel>> getListOfPostsStream() {
return _firestore
.collection("posts")
.snapshots()
.map((QuerySnapshot<Map<String, dynamic>> query) {
List<PostModel> postsModelList = [];
query.docs.forEach((element) {
postsModelList.add(PostsModel.fromDocumentSnapshot(element));
});
return postsModelList;
});
}
My query to retrieve liked posts:
#override
Stream<List<LikedPostsModel>> getLikedPostsByUser(User user) {
return _firestore
.collection("users")
.doc(user.id)
.collection("liked_posts")
.snapshots()
.map((QuerySnapshot<Map<String, dynamic>> query) {
List<LikedPostsModel> likedModelList = [];
query.docs.forEach((element) {
likedModelList.add(LikedPostsModel.fromDocumentSnapshot(element));
});
return likedModelList;
});
}
PostsModel
class PostsModel {
late String id;
late String bodyText;
bool liked = false;
PostsModel(
this.id,
this.bodyText,
);
...
}
LikedPostsModel
class LikedPostsModel {
late String id;
late String postsmodel_id;
bool liked;
LikedPostsModel(
this.id,
this.postsmodel_id,
this.liked
);
...
}
Any help is appreciated. Thanks!
To achieve it you should use the combineLatest2 method of the RxDart package.
I used it when developing a chat project. You know, at the conversations page of WhatsApp lists all chats and shows us the talker and the last message, message time, and seen information. My problem was my talker detail values and chat metadatas were in separate collections and I had to show the profile image and name of a talker and the last message, last message time and isSeen of the chat at the same time.
Here is an example from my chat project;
Stream<List<Chats>> getChats() {
final _userId = FirebaseAuth.instance.currentUser?.uid;
return _firestore
.collection('Chats')
.where('members', arrayContains: _userId)
.orderBy("lastMessageDate", descending: true)
.snapshots()
.map((convert) {
return convert.docs.map((f) {
String? talkerId;
var members = f.data()['members'];
if (members.length == 2) {
talkerId = members[0] != _userId ? members[0] : members[1];
}
Stream<ChatMetadata> chatStream = Stream.value(f).map<ChatMetadata>(
(document) => ChatMetadata.fromMap(
f.reference.path,
document.data(),
),
);
Stream<Talker> talkerStream = _firestore
.collection("Users")
.where('uid', isEqualTo: talkerId)
.snapshots()
.map<Talker>(
(querySnapshot) => querySnapshot.docs
.map((e) => Talker.fromMap(
e.data(),
))
.first,
);
return Rx.combineLatest2(
chatStream,
talkerStream,
(ChatMetadata chatMetadata, Talker talker) =>
Chats.combine(chatMetadata, talker),
);
});
}).switchMap((observables) {
return observables.isNotEmpty
? rx.Rx.combineLatestList(observables)
: Stream.value([]);
});
}
As you can see the getChats() method returns a Stream<List<Chats>>. The Chats model is a class that combines two other classes like ChatMetadata and Talker. Here is my Chats model;
import '/model/talker.dart';
import '/model/chat_metadata.dart';
class Chats {
String? docId;
String? talkerId;
String? talkerUsername;
String? talkerUnvan;
String? talkerProfileImage;
String? lastMessage;
DateTime? dateTime;
String? lastMessageOwner;
bool? isSeen;
List? members;
Chats({
this.docId,
this.talkerId,
this.talkerUsername,
this.talkerUnvan,
this.isSeen,
this.talkerProfileImage,
this.lastMessage,
this.dateTime,
this.lastMessageOwner,
this.members,
});
factory Chats.combine(ChatMetadata chat, Talker talker) {
return Chats(
docId: chat.docId,
talkerId: talker.id,
talkerUsername: talker.username,
talkerUnvan: talker.unvan,
isSeen: chat.isSeen,
talkerProfileImage: talker.photoUrl,
lastMessage: chat.lastMessage,
dateTime: chat.lastMessageDate,
lastMessageOwner: chat.lastMessageOwner,
members: chat.members,
);
}
}
You have to pay attention to the Rx.combineLatest2 method and switchMap method at the end of the code.
You can research all of them but in summary, the combineLatest2() merges the given Streams into a single Stream sequence by using the [combiner] function whenever any of the stream sequences emits an item. Then switchMap() converts each emitted item into a Stream using the given mapper function. Because the getChats() method has to returns the Stream<List<Chats>>.
You can store your likes in a Map rather than a List.
Stream<Map<String, bool>> getLikedPostsByUser(User user) {
return _firestore
.collection("users")
.doc(user.id)
.collection("liked_posts")
.snapshots()
.map((QuerySnapshot<Map<String, dynamic>> query) {
final likedPostsMap = HashMap<String, bool>();
query.docs.forEach((element) {
final model = LikedPostsModel.fromDocumentSnapshot(element);
likedPostsMap[model.postmodel_id] = model.liked;
});
return likedPostsMap;
});
}
Then use the resulting Map for a constant time lookup when populating your list of posts.
Stream<List<PostsModel>> getListOfPostsStream(Map<String, bool> likedPosts) {
return _firestore
.collection("posts")
.snapshots()
.map((QuerySnapshot<Map<String, dynamic>> query) {
List<PostModel> postsModelList = [];
query.docs.forEach((element) {
final model = PostsModel.fromDocumentSnapshot(element);
model.liked = likedPosts[model.id] ?? false;
postsModelList.add(model);
});
return postsModelList;
});
}
To improve performance, likes can be persisted locally so they don't have to be fetched each time your app is opened. Add a timestamp to your likes document to limit your query to changes since the last time you fetched.
I want to retrieve data from firebase via "continent" value
here is my database
this is my code:
DatabaseReference ref = FirebaseDatabase.instance
.reference()
.child("Country/")
.orderByChild("continent")
.equalTo("Asia");
ref.once()
.then((DataSnapshot datasnapshot) {
datalist.clear();
var keys=datasnapshot.value.keys;
var values=datasnapshot.value;
for(var key in keys) {
Country data = new Country(
values[key]['Country'],
values[key]['continent'],
values[key]['Capital'],
);
}
datalist.add(data);
}
what is wrong?
DatabaseReference ref = FirebaseDatabase.instance
.reference()
.child("Country/")
.orderByChild("continent")
.equalTo("Asia");
ref.once()
.then((DataSnapshot snapshot) {
Map<dynamic, dynamic> values = snapshot.value;
values.forEach((key, values) {
print(values["continent"]);
});
});
From the 9.0.0 version of firebase_database, many breaking changes applied. Now its something like:
Query ref = FirebaseDatabase.instance.ref()
.child("Country/")
.orderByChild("continent")
.equalTo("Asia");
ref.once()
.then((DatabaseEvent event) {
if(event.snapshot.value == null) return;
var values = event.snapshot.value;
print(values);
});
I am newbie, and I am using package grouped_list to group the list with data from the SQLite database, grouped_list works fine with an existing list just like in its example, but when I replace it equals list with data retrieved from SQLite database then it doesn't work and I get error:
The following NoSuchMethodError was thrown building GroupedListView<dynamic, String>(dirty, state: _GroupedListViewState<dynamic, String>#c2ba2):
Class 'DrinkDatabase' has no instance method '[]'.
Receiver: Instance of 'DrinkDatabase'
Tried calling: []("drinkGroup")
Below is my code. If you need more information please let me know, please help, thank you!
GroupedListView<dynamic, String>(
shrinkWrap: true,
elements: homeController.drinkList,
groupBy: (element) => element['drinkGroup'],
groupComparator: (value1, value2) => value2.compareTo(value1),
itemComparator: (item1, item2) =>
item1['drinkDateTime'].compareTo(item2['drinkDateTime']),
order: GroupedListOrder.DESC,
useStickyGroupSeparators: true,
groupSeparatorBuilder: (String value) =>
Container(),
itemBuilder: (c, element) {
return Container();
},
)
This is how I declare the list:
List drinkList = List<DrinkDatabase>();
And class DrinkDatabase:
class DrinkDatabase {
int drinkId;
int drinkVolume;
String drinkGroup;
String drinkCategory;
String drinkDateTime;
DrinkDatabase({
this.drinkId,
this.drinkVolume,
this.drinkGroup,
this.drinkCategory,
this.drinkDateTime,
});
Map<String, dynamic> toMap() {
return {
"drinkId": drinkId,
"drinkVolume": drinkVolume,
"drinkGroup": drinkGroup,
"drinkCategory": drinkCategory,
"drinkDateTime": drinkDateTime,
};
}
#override
String toString() {
return 'drinkTable('
'"drinkId": ${this.drinkId}, '
'"drinkVolume": ${this.drinkVolume}, '
'"drinkGroup": ${this.drinkGroup}, '
'"drinkCategory": ${this.drinkCategory}, '
'"drinkDateTime": ${this.drinkDateTime}, ';
}
}
What I did and it worked for me
The way to declare the list
List drinkList = [];
Then replace the line
elements: homeController.drinkList,
with
elements: drinkList,
To build the list from the database query result list
Future<void> getSampleList() async {
drinkList.clear();
List<Map<String, dynamic>> result =
await DrinkDatabaseHelper.instance.queryAll();
print('result= $result');
var map = new Map<String, dynamic>();
for (int i = 0; i < result.length; i++) {
map['drinkId'] = result[i]['drinkId'].toString();
map['drinkVolume'] = result[i]['drinkVolume'].toString();
map['drinkGroup'] = result[i]['drinkGroup'];
map['drinkCategory'] = result[i]['drinkCategory'];
map['drinkDateTime'] = result[i]['drinkDateTime'];
drinkList.add(Map<dynamic, String>.from(map));
}
In DrinkDatabaseHelper
Future<List<Map<String, dynamic>>> queryAll() async {
Database db = await instance.database;
return await db.query(_tableName);
}
If you have more questions let me know
i am not able to send it under the current userid and also not able to fetch it for current userid.
basically i want to send the data under the userid and also fetch the same data.
So for that i want to change the current document name as the user id. but whenever i do that and i call _sendToServer() in an on pressed button it gives me error.
_sendToServer() {
if (_key.currentState.validate()) {
_key.currentState.save();
DatabaseReference ref = FirebaseDatabase.instance.reference();
final Firestore _db = Firestore.instance;
var data = {
"name": name,
"profession": profession,
"message": message,
};
_db
.collection('Profile')
.document('KoMna0Hv7VXoeABwFTGH7LTo1No2')
.setData(data)
.then((v) {
_key.currentState.reset();
});
}
}
also while fetching data i am not able to do this. as i am getting error in the below code.
fetchUser() async{
Future<List<Text>> getAllProfiles() async {
List<Text> returnList = [];
final Firestore _db = Firestore.instance;
await _db.collection("profile").getDocuments().then((QuerySnapshot snapshot) {
snapshot.documents.forEach((doc) {
var keys = snapshot.value.keys;
var data = snapshot.value;
allData.clear();
for (var key in keys) {
myData d = new myData(
data[key]['name'],
data[key]['message'],
data[key]['profession'],
);
allData.add(d);
}
setState(() {
print('Length : ${allData.length}');
});
});
});
return returnList;
}
}
i must provide these key value pair for fetching the data but unfortunately i am not able to do so.
I have added the orderByChild('id') and equalTo('${user.uid}') filed in the code with firebase user. and i also one more item to my list which is data[key]['id'], my current user id. this way everytime the user tries to fetch the data it will look into the list item for current userid and if it matches it will fetch that particular database only.
#override
// ignore: must_call_super
void initState() {
FirebaseAuth.instance.currentUser().then((user) {
fetchUser(user);
});
}
fetchUser(FirebaseUser user) {
DatabaseReference ref = FirebaseDatabase.instance.reference();
ref
.child('node-name')
.orderByChild('id')
.equalTo('${user.uid}')
.once()
.then((DataSnapshot snap) {
var keys = snap.value.keys;
var data = snap.value;
print(snap.value.toString());
allData.clear();
for (var key in keys) {
myData d = new myData(
data[key]['name'],
data[key]['number'],
data[key]['address'],
data[key]['id'],
data[key]['location'],
data[key]['website'],
);
allData.add(d);
}
setState(() {
print('Length : ${allData.length}');
});
});
}
_sendToServer() async{
FirebaseUser user = await FirebaseAuth.instance.currentUser();
if (_key.currentState.validate()) {
_key.currentState.save();
DatabaseReference ref = FirebaseDatabase.instance.reference();
var data = {
"id": user.uid,
"name": name,
"number": number,
"address": address,
"location":location,
"website":website,
};
ref.child('node-name').child(user.uid).set(data).then((v) {
_key.currentState.reset();
});
} else {
setState(() {
_autovalidate = true;
});
}
}
The data from the cloud_firestore database is in the form of JSON. However, how to transform the data from JSON in a List of Map?
The dummy data in my firestore
Data to List of Map:
final CollectionReference ref = Firestore.instance.collection('food');
List<Map<String, dynamic>> listOfMaps = [];
await ref.getDocuments().then((QuerySnapshot snapshot) {
listOfMaps =
snapshot.documents.map((DocumentSnapshot documentSnapshot) {
return documentSnapshot.data;
}).toList();
});
print(listOfMaps);
Just in case if You want to use better way. Parse data to List of Objects:
1) create a model class:
class Food {
String affordability;
String title;
Food.fromJson(Map<String, dynamic> jsonData) {
this.affordability = jsonData['affordability'];
this.title = jsonData['title'];
}
}
2) convert to list of Food:
final CollectionReference ref = Firestore.instance.collection('food');
List<Food> list = [];
await ref.getDocuments().then((QuerySnapshot snapshot) {
list = snapshot.documents.map((DocumentSnapshot documentSnapshot) {
return Food.fromJson(documentSnapshot.data);
}).toList();
});
print(list);