Accessing nested nodes in a Firebase RTDB, using Flutter - firebase

I've got a database that stores user information such as their name and all their friends. The DB look something like this:
{
"users": {
"userID here": {
"bio": "",
"country": "",
"dateOfBirth": "2022-04-13 17:49:45.906226",
"emergencyContacts": {
"mom": {
"emailAdd": "mom#gmail.com",
"name": "mom",
"phoneNumber": "07492017492",
"picUrl": "url here'"
}
},
"friends": {
"auto-generated ID (used push method)": {
"country": "",
"profilePicUrl": "url here",
"userID": "userID here",
"username": "newOne"
}
},
"fullName": "name",
"gender": "Female",
"hobbies": "[]",
"knownMH": "[]",
"lastMessageTime": "2022-04-14 08:44:40.639944",
}
}
I would like to access the "friends" node. While doing so, I'd also like to store the data in a 2d array. So, each sub-array will contain the data about the user we're currently looping through and one of their friends. This should happen until we've looped through all of the user's friends and then move on to the next user.
It's also worth mentioning; I've used a push method for adding friends so I get randomly assigned keys for the nodes.
I've used the method below, but it's not working, it's returning the wrong list of friends and not looping through all users. Please find the code that I've used below (any help is much appreciated):
void _retrieveAllFriendships() {
final allUsersDb = FirebaseDatabase.instance.ref().child('users');
_allUserssStream = allUsersDb.onValue.listen((event) {
if (event.snapshot.exists) {
final data = new Map<dynamic, dynamic>.from(
event.snapshot.value as Map<dynamic, dynamic>);
data.forEach((key, value) {
allUsersDb.child(key).onValue.listen((event) {
final acc = new Map<dynamic, dynamic>.from(
event.snapshot.value as Map<dynamic, dynamic>);
final username = acc['username'] as String;
final profilePicUrl = acc['profilePicUrl'] as String;
final country = acc['country'] as String;
final userID = acc['userID'] as String;
user = new FriendSuggestionModel(
picture: profilePicUrl,
name: username,
location: country,
userID: userID,
);
_friendshipStream = allUsersDb
.child(userID)
.child("friends")
.onValue
.listen((event) {
if (event.snapshot.exists) {
final data = new Map<dynamic, dynamic>.from(
event.snapshot.value as Map<dynamic, dynamic>);
data.forEach((key, value) {
allUsersDb
.child(userID)
.child("friends")
.child(key)
.onValue
.listen((event) {
final friend = new Map<dynamic, dynamic>.from(
event.snapshot.value as Map<dynamic, dynamic>);
final username = friend['username'] as String;
final profilePicUrl = friend['profilePicUrl'] as String;
final country = friend['country'] as String;
final userID = friend['userID'] as String;
friendModel = new FriendSuggestionModel(
picture: profilePicUrl,
name: username,
location: country,
userID: userID);
List<FriendSuggestionModel> friendship = [
user,
friendModel
];
allFriendships.add(friendship);
setState(() {
for (int i = 0; i < allFriendships.length; i++) {
print(
"${allFriendships[i][0].name} is friends with: ${allFriendships[i][1].name}");
}
});
});
});
}
});
});
});
}
});
}

When you attach a listener to allUsersDb.onValue all data under that path is already present in the event.snapshot and you don't need any more listeners for that data.
If you know the name of a child snapshot you want to access, you can get this by snapshot.child("friends"). If you don't know the name of the child snapshots, you can loop over all of them with snapshot.children.forEach.

Related

I am getting "Bad state: No element" when using firstwhere in Flutter List even when the data exists

Found a few similar questions but my scenario is totally different.
My Plans file :
class Plans {
FirebaseFirestore db = FirebaseFirestore.instance;
List<Plan> plans = [];
Plan findByID(String id) {
return plans.firstWhere(
(plan) => plan.id == id,
);
}
Future<List> getPlans() async {
await db.collection('Plans').get().then(
(QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach(
(individualPlan) {
String _id = individualPlan.id;
List _tags = [];
for (var i = 0; i < 4; i++) {
_tags.add(individualPlan["Tags"][i]);
}
final newPlan = Plan(
id: _id,
name: individualPlan["PlanName"],
price: individualPlan["PlanPrice"],
validity: individualPlan["PlanValidity"],
tags: _tags,
);
plans.add(newPlan);
},
);
//foreach ends here
String _id = plans[0].id;
print(findByID(_id).name);
print(findByID(_id).price);
},
);
return plans;
}
}
Here, String _id = plans[0].id; returns an item just fine and the print statements following work very well.
I call the same findById in my details screen as follows :
Plan selectedPlan = _plansService.findByID(id);
This is how I get my id from the listing page to my details screen:
String id = ModalRoute.of(context)!.settings.arguments as String;
print(id);
Here id is not empty and the print statment prints an id that is passed.
I only get the Bad State: no element in the details page while there is an id that is passed to it. I am pretty much doing the same as I am doing in the Plans class but it just wouldn't work.

Error: Expected a value of type 'int', but got one of type 'String'; flutter

I am trying to fetch quizzes data from realtime database using http mathod "GET".
The data is being retreived but it's not showing up in listview and when i print the length of my list it's 0. And this is what the error is being shown in my terminal: Error: Expected a value of type 'int', but got one of type 'String'
I can't figure out what the problem is. Please help me solve this problem because i am trying for around 5 days but can't solve it.
Thank you.
These are my codes for fetching the data in lists.
Future<void> getList() async {
list.clear();
final url = Uri.parse(
'https://testisnotesttheisthelearningapp-default-rtdb.firebaseio.com/Schools/${widget.uid}/ResultsFolder/${widget.id}.json');
final response = await get(url);
print(response.body);
var map = json.decode(response.body) as Map<String, dynamic>;
// print(map);
if (map != null) {
map.forEach((key, value) {
print(value['Name']);
var temp = Result(
value['Percent'],
(value['choice'] as List<dynamic>)
.map((e) => Model(e['index'], e['question'], []))
.toList(),
value['Score'],
(value['question'] as List<dynamic>).map((e) => e).toList(),
value['Name']);
list.add(temp);
});
}
}
This is my data from real-time database in JSON format:
{
"Name" : "Miets Digital",
"Percent" : 100,
"Score" : 2,
"choice" : [ "Correct", "Correct" ],
"question" : [ {
"question" : "WHo is Elon Musk?"
}, {
"question" : "How did Elon musk got rich?"
} ]
}
Try this:-
Future<void> getList() async {
list.clear();
final url = Uri.parse(
'https://testisnotesttheisthelearningapp-default-rtdb.firebaseio.com/Schools/${widget.uid}/ResultsFolder/${widget.id}.json');
final response = await get(url);
print(response.body);
var map = json.decode(response.body) as Map<String, dynamic>;
// print(map);
if (map != null) {
print(map['Name']);
var temp = Result(
map['Percent'],
(map['choice'] as List<dynamic>)
.map((e) => Model(e['index'], e['question'], []))
.toList(),
map['Score'],
(map['question'] as List<dynamic>).map((e) => e).toList(),
map['Name']);
list.add(temp);
}
}
forEach() loop goes through each key value pair in a map. The variables key and value gives you the key and the value the loop is currently working on. If you want to access the values of a map you will have to use the syntax map['key_name'].
This also explains the error, as your Result() constructor is expecting an integer but is getting a string value.
It's not a good practice to parse JSON maps. You just call over the model class
Try to follow the below code
Future<QuizModelResponse> getList() async {
// here write your code
var map = json.decode(response.body);
return QuizModelResponse.fromJsonMap(map);
}
List Response class
class QuizModelResponse {
List<QuizItemModel> content;
QuizModelResponse.fromJsonMap(dynamic data) :
content = List<QuizItemModel>.from(
data.map((it) => QuizItemModel.fromJson(it)));
}
Model class
class QuizItemModel {
String name;
int percent;
int score;
List<String> choice;
List<Question> question;
QuizItemModel(
{this.name, this.percent, this.score, this.choice, this.question});
QuizItemModel.fromJson(Map<String, dynamic> json) {
name = json['Name'];
percent = json['Percent'];
score = json['Score'];
choice = json['choice'].cast<String>();
if (json['question'] != null) {
question = new List<Question>();
json['question'].forEach((v) {
question.add(new Question.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Name'] = this.name;
data['Percent'] = this.percent;
data['Score'] = this.score;
data['choice'] = this.choice;
if (this.question != null) {
data['question'] = this.question.map((v) => v.toJson()).toList();
}
return data;
}
}
class Question {
String question;
Question({this.question});
Question.fromJson(Map<String, dynamic> json) {
question = json['question'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['question'] = this.question;
return data;
}
}

The parameter isn't defined however, I defined it

The parameter isn't defined however, I defined it.
I saved my data onto firebase and also in the PostData object class however, when I try to fetch it, it tells me that the parameter isn't defined.
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<void> fetchProducts() async {
PostData data;
final User user = _auth.currentUser;
final _uid = user.uid;
print('Fetch method is called');
await FirebaseFirestore.instance
.collection('post')
.get()
.then((QuerySnapshot productsSnapshot) {
_products = [];
productsSnapshot.docs.forEach((element) {
// print('element.get(productBrand), ${element.get('productBrand')}');
_products.insert(
0,
PostData(
0,
[],
Timestamp.now().millisecondsSinceEpoch,
name,
token,
image,
[],
title: element.get('productTitle'),
id: element.get('productId'),
description: element.get('productDescription'),
price: double.parse(
element.get('price'),
),
imageUrl: element.get('productImage'),
productCategoryName: element.get('productCategory'),
brand: element.get('productBrand'),
true,
// 0,
//
// [],
// Timestamp.now().millisecondsSinceEpoch,
//
// name,
// token,
// image,
// [],
// "",
// "",
// "",
// 0,
// "",
// "",
// "",
// true,
),);
});
});
}
Here is where I stored it, in PostData however, whenever I try to call it, it tells me that the parameter isn't defined. I am pretty sure I defined it so I don't know what to do or how to fix this issue.
PostData(
int likesCount,
List likes,
int timestamp,
String postersName,
String postersId,
String postersImageUrl,
List postSavedByUsers,
String title,
String id,
String description,
double price,
String imageUrl,
String productCategoryName,
String brand,
)
{
this.likesCount = likesCount;
this.likes = likes;
this.timestamp = timestamp;
this.postersName = postersName;
this.postersId = postersId;
this.postersImageUrl = postersImageUrl;
this.postSavedByUsers = postSavedByUsers;
this.title = title;
this.id = id;
this.description = description;
this.price = price;
this.imageUrl = imageUrl;
this.productCategoryName = productCategoryName;
this.brand = brand;
this.isPopular = isPopular;
}
here is the error i'm getting.
You're getting the error because you defined positional required parameters and not named parameters but you're trying to pass the name of the parameters (title, id, description, price, `imageUrl, productCategoryName, brand).
Solution:
Pass the arguments as positional required parameters (without the parameter names) like below:
PostData(
0,
[],
Timestamp.now().millisecondsSinceEpoch,
name,
token,
image,
[],
element.get('productTitle'),
element.get('productId'),
element.get('productDescription'),
double.parse(
element.get('price'),
),
element.get('productImage'),
element.get('productCategory'),
element.get('productBrand'),
)

Firebase database is getting structured incorrectly

I am developing a Flutter app with Firebase Database. This is a chat app. The database structure I need is as the following.
So, I first create a chat room and then add its members. Here please consider the key 900 as a key in chat_room (i know its not in this example), acting as the foreign key in members node. According to the current structure, I can add any number of members to a chat room and I can easily find them without downloading lot of data. This is achievable by the following code.
fire_database_service.dart
import 'package:firebase_database/firebase_database.dart';
class FireDatabaseService
{
final DatabaseReference _chatRoomReference = FirebaseDatabase.instance.reference().child('chat_room');
//Create chat_room
Future<String> createChatRoom({String lastMessage, String chatMode}) async {
var newChatRoomRef = _chatRoomReference.push();
var newChatRoomKey = newChatRoomRef.key;
await newChatRoomRef.set({
'last_message': lastMessage,
'chat_mode': chatMode,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
return newChatRoomKey;
}
Future<void> addChatMembers(List<ChatMember> chatMembers, String chatRoomID) async
{
for(int i=0; i<chatMembers.length; i++)
{
ChatMember chatMemeber = chatMembers[i];
var newChatMemberReference = FirebaseDatabase.instance.reference().child('members/$chatRoomID/').push();
var newChatMemberKey = newChatMemberReference.key;
await newChatMemberReference.set({
'email': chatMemeber.email,
'userID': chatMemeber.userID,
'user_role': chatMemeber.userRole,
});
}
return null;
}
}
chat.dart
void initState() {
super.initState();
FireDatabaseService firebaseDatabase = FireDatabaseService();
String chatRoomID="";
firebaseDatabase.createChatRoom(
lastMessage: "Test",
chatMode: "Supplier").then((value) async{
print("CHAT ID: "+value);
ChatMember member1 = new ChatMember(
email: "someone#test.com",
userRole: "customer",
userID: 30
);
ChatMember member2 = new ChatMember(
email: "anotherone#test.com",
userRole: "supplier",
userID: 50
);
List<ChatMember> chatMemberList = [member1, member2];
print("BEFORE");
await firebaseDatabase.addChatMembers(chatMemberList, "900");
print("AFTER");
});
}
Please note that in this line await firebaseDatabase.addChatMembers(chatMemberList, "900"); I am hardcoding the value 900. Instead of this, if I pass the unique key of the chat room node, I get this structure, which is completely incorrect of what i need.
I actually need this unique key of chat room to be in members node, so it will act as the foreign key in members node.
Why is this happening and how can I fix this?
I fixed this issue. It was because the chatRoomID is not getting the unique key, it was empty. I fixed it in the following way.
void initState() {
super.initState();
FireDatabaseService firebaseDatabase = FireDatabaseService();
firebaseDatabase
.createChatRoom(lastMessage: "Test", chatMode: "Supplier")
.then((value) async {
print("CHAT ID: " + value);
ChatMember member1 = new ChatMember(
email: "someone#test.com",
userRole: "customer",
userID: 30,
profilePic: "");
ChatMember member2 = new ChatMember(
email: "anotherone#test.com", userRole: "supplier", userID: 50);
List<ChatMember> chatMemberList = [member1, member2];
print("BEFORE");
await firebaseDatabase.addChatMembers(chatMemberList, value);
print("AFTER");
});
}

i want to send the data under the userid and also fetch the same data

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;
});
}
}

Resources