how to retrive value from a firestore flutter where query - flutter

I started flutter recently, and I try to retrieve the data from a query I made using 'where' , but the only thing I got back is "Instance of '_JsonQueryDocumentSnapshot'".
I tried different thing , but nothing work or i do it badly
this is my code :
CollectionReference users =
FirebaseFirestore.instance.collection('users');
final documents =
await users.where("username", isEqualTo: "username").get();
documents.docs.forEach((element) {
print(element);
});
I have also tried to use Future but without success :
class finduser extends StatelessWidget {
final String username;
finduser(this.username);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder(
future: users.where('username', isEqualTo: '${username}').get(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
print("wrong");
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
print("doesnt exist");
return Text("User does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data! as Map<String, dynamic>;
print(snapshot.data!);
return Text("${data}");
}
return Text("loading");
},
);
}
}
for the moment, all usernames are just "username"
Thank you for the help

When you get your documents like this :
CollectionReference users =
FirebaseFirestore.instance.collection('users');
final documents =
await users.where("username", isEqualTo: "username").get();
documents.docs.forEach((element) {
print(element);
});
You are trying to print an instance of a QueryDocumentSnapshot
This QueryDocumentSnapshot has a method .data() which returns a Map<String,dynamic> aka JSON.
So in order to print the content of your Document, do this :
documents.docs.forEach((element) {
print(MyClass.fromJson(element.data()));
});
This data by itself will not be very useful so I recommend creating a factory method for your class :
class MyClass {
final String username;
const MyClass({required this.username});
factory MyClass.fromJson(Map<String, dynamic> json) =>
MyClass(username: json['username'] as String);
}
Now you can call MyClass.fromJson(element.data()); and get a new instance of your class this way.

I have searched a lot but i see you have written code right.
The only thing that came to my mind is that you didn't initialize your firebase to your flutter project (you should do it in any flutter project to be able to use flutter).
link of the document:
https://firebase.flutter.dev/docs/overview#initializing-flutterfire

In your first code snippet you are printing element, which are instances of the QueryDocumentSnapshot class. Since you're not accessing specific data of the document snapshot, you get its default toString implementation, which apparently just shows the class name.
A bit more meaningful be to print the document id:
documents.docs.forEach((doc) {
print(doc.id);
});
Or a field from the document, like the username:
documents.docs.forEach((doc) {
print(doc.get("username"));
});

Run this code, it will work.
I also faced this similar problem, so I used this work around.
Map<String, dynamic> data = {};
FirebaseFirestore.instance.collection('users').where("username", isEqualTo: "username").get().then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((value){
data = value.data()!;
print('printing uid ${data['uid']}');
print('printing username--${data['username']}');
print('printing all data--$data');
});
});

Related

StreamBuilder doesn't updates UI when Firestore data changes

My goal:
I want to retrieve a list of documents from the Firebase Firestore using a Stream to update the interface in real time when the Firestore data changes.
The problem:
I am able to download the data from Firestore with the structure I need, but when I make changes in firestore the interface does not update in real time. When I reload the page, it updates, but that is not the behavior I need.
This is the Stream I have created:
Stream<DayModel> getInstances(String selectedDay, String userUid) async* {
DayModel retval = DayModel();
List<InstanceModel> instances = [];
int index = 0;
try {
final QuerySnapshot<Map<String, dynamic>> querySnapshot =
await FirebaseFirestore.instance
.collection('instances')
.doc(selectedDay)
.collection('instancesUid')
.where("instanceUsersUid", arrayContains: userUid)
.get();
instances = querySnapshot.docs
.map((instance) => InstanceModel.fromSnapshot(instance))
.toList();
for (InstanceModel instance in instances) {
final DocumentSnapshot<Map<String, dynamic>> instanceQuery =
await FirebaseFirestore.instance
.collection('instances')
.doc(selectedDay)
.collection('instancesUid')
.doc(instance.uid)
.get();
instance = InstanceModel.fromMap(instanceQuery);
instances[index] = instance;
index++;
}
retval.instances = instances;
yield retval;
} on Exception catch (e) {
print(e);
}
}
StreamBuilder code:
body: StreamBuilder<DayModel>(
stream:
OurDatabase().getInstances(selectedDay, _currentUser!.uid!),
builder:
(BuildContext context, AsyncSnapshot<DayModel> snapshot) {
if (snapshot.hasError) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
return Center(
child: snapshot.data!.instances!.isNotEmpty
? Text(snapshot.data!.instances![0].uid!)
: Text('No tienes instancias!'),
);
})
Maybe it's because I'm not returning the Stream with a QuerySnapshot?
I have read in other similar posts that it could be a problem with the keys, but I have tried several different combinations and it has not worked.
Do you have any idea what could be happening?
Thank you for your time.

Firebase - i got empty widget and my circularprogress always running

help me to show my amount around my widget
i got the data when i print(nominal.data()) but i cant get any data from result when i print it
this is my code
myService
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:limbong/model/amount_model.dart';
class AmountService {
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
Stream\<List\<AmountModel\>\> getAmount() {
try {
return firebaseFirestore
.collection('amount')
.orderBy('createdAt', descending: true)
.limit(1)
.snapshots()
.map((QuerySnapshot nominal) {
var result = nominal.docs.map\<AmountModel\>((DocumentSnapshot nominal) {
print(nominal.data()); // i got my data here
return AmountModel.fromJson(nominal.data() as Map\<String, dynamic\>);
}).toList();
print(result); but i havent see the data here
return result;
});
} catch (e) {
throw Exception(e);
}
}
} `
on this model i think i havent any problem yet but i wanna some advice to make it better
MyModel
class AmountModel {
late DateTime createdAt;
late double currentBalance;
late double executableBalance;
AmountModel(
{required this.createdAt,
required this.currentBalance,
required this.executableBalance});
AmountModel.fromJson(Map\<String, dynamic\> json) {
createdAt = DateTime.parse(json\['createdAt'\]);
currentBalance = json\['current_balance'\];
executableBalance = json\['executable_balance'\];
}
Map\<String, dynamic\> toJson() {
return {
'createdAt': createdAt.toString(),
'current_balance': currentBalance,
'executable_balance': executableBalance,
};
}
}
when call the service, i got nothing and the circularprogress always run
MyWidget
Widget balanceWidget() {
return StreamBuilder\<List\<AmountModel\>\>(
stream: AmountService().getAmount(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return BalanceWidget(
amount: snapshot.data!\[snapshot.data!.length - 1\]);
}
return const Center(child: CircularProgressIndicator());
});
}`
help me
i want to show the the nominal.data instead blank result or maybe i got the real result
the real result to make my firebase database show in my widget

Flutter - How can I get the Firestore items that contain their id in an array in another table as snapshots?

How can I get Firestore items containing their id in an array in another table as snapshots in Flutter? I am attaching the code that I have that works perfectly for me doing a "get", but I can't find a way to convert this to Stream and print it on the screen with the StreamBuilder instead of with the FutureBuilder and update it with each change
Future<List<DocumentSnapshot<Map<String, dynamic>>>?> getPools() async {
List<DocumentSnapshot<Map<String, dynamic>>> pools = [];
final user = FirebaseAuth.instance.currentUser;
final DbUser? dbUser = await dbUserAPI.getDbUser(user);
if (dbUser != null) {
for (var pool in dbUser.pools) {
final result = await FirebaseFirestore.instance
.collection('pools')
.doc(pool)
.get();
pools.add(result);
}
return pools;
}
if (kDebugMode) {
print('Error al leer el usuario de FireStore');
}
return null;
}
In the dbUsersAPI.getDbUsers function I retrieve the user data from the "Users" table and then I get the value ".pools", which is an array of Strings with the ids of the items I want to retrieve.
I have tried many ways and to play with Streams but I am always getting a Future or Stream when I only want to get a Stream of the items that I am filtering.
I have tried with the where clause but it does not update the values. I think the problem is that I don't know how to manage the Future returned by the dbUsersAPI.getDbUsers function.
Well for displaying data using a StreamBuilder you need to fetch data in streams by generating a requests that ends with .snapshots() method instead of a .get() method.
A pretty simple scenario will be,
DbUser? dbUser;
getDbUser() async {
final user = FirebaseAuth.instance.currentUser;
final DbUser? _dbUser = await dbUserAPI.getDbUser(user);
if(_dbUser != null){
dbUser = _dbUser;
}
}
#override
void initState(){
getDbUser();
super.initState();
}
#override
void build(BuildContext context){
return ListView.builder(
itemCount: dbUser!.pools.length,
itemBuilder: (context, index){
final pool = dbUser!.pools[index];
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('pools')
.doc(pool)
.snapshots(),
builder: (context, snapshots){
return Container();
}
}
);
);
}

Cant retrieve certain fields from the parsed list of the Firestore database

I am building a restaurant app in which I have used Firestore as my backend. I have stored the details of the menu in a collection Menu and each menu item in specific documents. Firstly, is that a good data model, or should I have the whole menu in the same document?
Secondly, the problem is while I retrieve the the collection and the docs, I am not being able to access some fields. If there are 4 documents and all of them contains the field 'Name' and data in the field. But when I fetch the data, parse it inot the list and have the command Menu[index]['Name] only two of the names in the documents are displayed while the other two return null.
class MenuController extends GetxController {
final CollectionReference _menulsit =
FirebaseFirestore.instance.collection('Menu');
Future getmenu() async {
List Menulist = [];
try {
await _menulsit.get().then((QuerySnapshot snapshot) {
snapshot.docs.forEach((element) {
Menulist.add(element.data());
});
});
return Menulist;
} catch (e) {
return null;
}
}
}
While I parse it into a list and print the list, the data is retrieved and is printed in the console. There is the field 'Name' printed on the console but when I try to access it from the list it returns null. The same fields on some documents are returned but the same fields on some other documents are returned to be null.
I have used the list from the class, made a method, and provided a list here with the data retrieved. I need to use the data in a listview.seperated.
class _FoodmenuState extends State<Foodmenu> {
List menulist = [];
#override
void initState() {
super.initState();
fetchmenu();
}
Future fetchmenu() async {
dynamic resultmenu = await MenuController().getmenu();
if (resultmenu == null) {
return Text('Unable to retrive data');
} else {
setState(() {
menulist = resultmenu;
});
}
}
#override
Widget build(BuildContext context) {
return Column(children: [
Container(
height: 228,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemBuilder: ((context, index) => _menucontent(index, menulist)),
separatorBuilder: ((context, index) {
return SizedBox(
width: 18,
);
}),
itemCount: menulist.length))
]);
}
}
While I print the list there is the field "Name" but I can't access it.
Print(menu)
I/flutter (31598): [{Name: Cheese Burger}, {Name : Buffalo Wings}, {Name : Pasta Bolognese }, {Name : Chicken MoMo}]
Print(menu[1])
I/flutter (31598): {Name : Buffalo Wings}
Print(menu[1]['Name']
I/flutter (31598): null
Its better to store each menu item in separate document as it would enable to fetch only some menu items based on some conditions.
Regarding menu[1]['Name'] value printed as null:
As Print(menu) is giving correct JSON response, I think there is extra space after Name word in firestore document. You might have added this data manually :). Please refer below screenshot.
First of all you should consider defining data types as it would lower chances of error and provide better suggestion if data types are mentioned. Make a class of MenuItem and make menu items as it might help when you want to add any item in overall app. Below is a example to help you understand.
class MenuItem {
final String name;
final int price;
final String description;
//you can add extra field here
MenuItem(
{required this.name, required this.price, required this.description});
MenuItem.fromJson(Map<String, dynamic> json)
: name = json['name'],
price = json['price'],
description = json['description'];
}
Future getmenu() async {
List<MenuItem> menulist = [];
try {
await _menulsit.get().then((QuerySnapshot snapshot) {
snapshot.docs.forEach((element) {
menulist.add(MenuItem.fromJson(element.data()));
});
});
return menulist;
} catch (e) {
return menulist;
}
}
Now if you want to access name you can try like this menulist[0].name beside this if you type menulist[0]. it will give you suggestion whatever a menuItem can hold.
EDIT:
check it out and cast it from beginning as if Object to <Map<String, dynamic>> error occurs
final CollectionReference<Map<String, dynamic>> _menulsit =
FirebaseFirestore.instance.collection('Menu');
Future getmenu() async {
List<MenuItem> menulist = [];
try {
await _menulsit.get().then((QuerySnapshot<Map<String, dynamic>> snapshot) {
snapshot.docs.forEach((element) {
menulist.add(MenuItem.fromJson(element.data()));
});
});
return menulist;
} catch (e) {
return null;
}
}

Flutter Firestore returns error without any problem in the code

The following code returns error "NoSuchMethodError"
StreamBuilder(
stream: SalaryService.getSingle(),
builder: (_, snapshot) {
if (snapshot.data() != null) {
print('step 3');
return Text(
snapshot.data['value'].toString(),
);
} else {
return Text(
"Nil",
);
}
},
),
class SalaryService {
static Stream<DocumentSnapshot> getSingle() {
Stream<DocumentSnapshot> snapshot = FirebaseFirestore.instance
.doc(userId + '/salary' + todayYM)
.snapshots();
snapshot.forEach(
(element) {
// prints all the documents available
// in the collection
print(element.data().toString());
// print((element.data() != null).toString());
},
);
return snapshot;
}
}
The cloudstore document does not exist to begin with until the user updates his salary hence the if else used.
P.S.: I am a rookie
Two things:
It is best practice to check whether the QueryDocumentSnapshot returned has data, plus check whether the document reference exists first by casting it as a DocumentSnapshot as opposed to pull the data straight up, as in:
if (snapshot.hasData && (snapshot.data as DocumentSnapshot).exists) { // ... }
you cannot pull the properties out of the snapshot like snapshot.data['field'] without pulling the data out first as a Map<String, dynamic>; you at least have to do first is snapshot.data() (after checking that it exists), then pull the fields out of the returned map, as in:
Map<String, dynamic> docData = (snapshot.data as DocumentSnapshot).data() as Map<String, dynamic>;
print(docData['value']);
Check this Gist for the full code (replace with your Firebase config settings at the top if you want to test it by running it on DartPad.dev.