How to recover a specific data in the firestore? - flutter

I have this structure in the database:
I have a function that return the current user logged
FirebaseAuth auth = FirebaseAuth.instance;
auth.currentUser.uid;
And i want to retrieve "requisicoes" when "idUser" == auth.currentUser.uid;
Basically, the user retrieves the requests created by himself.
That's my StreamBuilder
final _controller = StreamController<QuerySnapshot>.broadcast();
FirebaseFirestore db = FirebaseFirestore.instance;
StreamBuilder<QuerySnapshot> addListenersRequests() {
final stream = db
.collection("requisicoes")
.where("usuario.idUser", isEqualTo: idUsuarioLogado)
.snapshots();
stream.listen((dados) {
_controller.add(dados);
});
return null;
}
Note: idUsuarioLogado is returning correctly currentUser
The problem is that I am getting everything in the "requisicoes" collection, when I create a new user, other users' requests appear on the screen
is there a logic problem in StreamBuilder?

As per you problem ,I think you are fetching the data of your current logged uid .But You want to retrieve the details of particulers users created(i,e documendId) . Then you have to fetch documentId first.Like
body: StreamBuilder(
stream:users.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if(!snapshot.hasData){
return Center(
child: CircularProgressIndicator(),
);
}
return ListView(
children: snapshot.data.documents.map((document) {
return Center(
child: Container(
width: MediaQuery.of(context).size.width/2.5,
height: MediaQuery.of(context).size.height/5,
child: Text(document.id),
),
);
}).toList());
}),
You have to fetch document.id. This is your particuler user instead of this.
FirebaseAuth auth = FirebaseAuth.instance;
auth.currentUser.uid;
Note: This will return you current logged userId.
For more details gothrough thislink.

I found the answer here Firestore is ignoring the where clause in the query in flutter
My example is the same as the link above, I have two functions, one addListenersRequests() and the other getCurrentUser(), these two functions I was calling in the initState method, the problem is that getCurrentUser is async, so addListenersRequests () was being executed first, before the value of the variable idUsuarioLogado is filled.
So I merged the two functions into one, so that getCurrentUser can be executed first
Final code:
Future<StreamBuilder<QuerySnapshot>> addListenersRequests() async{
await getDataUser();
final stream = db
.collection("requisicoes")
.where("usuario.idUser", isEqualTo: idUsuarioLogado)
.snapshots();
stream.listen((dados) {
_controller.add(dados);
});
return null;
}
Now it is working correctly.
The strange thing about this story is that the firebase executes the clause and retrieves everything in the collection even when the referenced variable is null

Related

Accessing all docs in subbcollecions

I'm having struggles for the last couple of days, so any help highly appreciates it.
I have an app where everyday users take photos of themself ( I set the date of that day as docId), then in UI, every day has a page ( a carousel) where users can swipe and see the photos belonging to every day.
I attached a screenshot of the Firstore database.
But having a problem reading images , tried every method.
P.s : When I set the DocId for instance: 2023-01-11 it works but it just show the photos of one day , I need to fetch all images from all days.
Method adding data to Firestore:
final photoToDb = db
.collection('photos')
.doc(DateFormat('yyyy-MM-dd').format(newDate))
.collection('Today Photos')
.withConverter(
fromFirestore: PhotoModel.fromFirestore,
toFirestore: ((PhotoModel photoModel, options) =>
photoModel.toFirestore()),
);
photoToDb.add(photo);
} catch (e) {
return ('errro');
}
}
Page where I'm trying to display images ,
lass SugarPhotoPage extends StatefulWidget {
const SugarPhotoPage({
super.key,
});
#override
State<SugarPhotoPage> createState() => _SugarPhotoPageState();
}
class _SugarPhotoPageState extends State<SugarPhotoPage> {
final Stream<QuerySnapshot> _photoStream = FirebaseFirestore.instance
.collection('photos')
.doc()
.collection('Today Photos')
.snapshots();
#override
void initState() {
print('${AppData.userSelectedData}');
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
),
body: StreamBuilder<QuerySnapshot>(
stream: _photoStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
if (snapshot.hasData) {
return SafeArea(
child: Center(
child: ListView(
children: snapshot.data!.docs
.map((DocumentSnapshot documentSnapshot) {
Map<String, dynamic> data =
documentSnapshot.data()! as Map<String, dynamic>;
return Container(
height: 200,
width: 100,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage('${data['ImgUrl']}'),
fit: BoxFit.contain,
),
),
);
}).toList(),
),
),
);
}
return const Text('Loading');
}),
);
}
}
You can't do that with this data structure. Firebase queries are shallow, meaning that you can't query a document together with the documents in sub collections.
In StreamBuilder you can get snapshots of either one specific document by setting :
FirebaseFirestore.instance
.collection(...)
.withConverter<...>(...)
.doc(...)
.snapshots()
or multiple documents:
FirebaseFirestore.instance
.collection(...)
.withConverter<...>(...)
.where(...)
.orderBy(...)
.limit(...)
.snapshots()
In both cases, you will get the data of one or more documents, but if you need the documents in a sub collection, you need to perform another query. For example if you have one document in doc variable, and you need data in its Today Photos sub collection, you need another stream:
doc.collection('Today Photos')
.withConverter<...>(...)
.snapshots()
So with the current data structure you can query into a StreamBuilder all documents in the user's photos collection, but the contents of Today Photos sub collection must be queried separately for each retrieved document of photos collection.
The other option is to change your data structure. You can add the daily photos to the photos collection, let Firebase assign an id to them and add the date as a field. This way you can have one stream for the photos, order them by date, add a limit etc.

Combine two stream-queries in flutter

I want to create a streambuilder to download multiple user-profiles from firebase. But to know which users are needed I have to get the user-ids at first, these are stored in an array. So I created a method to download the array and after this is done the streambuilder loads the user-data for each user-id from the array. Here's the code:
Method to get the array (executed in initState()):
Stream<QuerySnapshot> stream() async* {
job = Job.fromJson(await FirebaseFirestore.instance
.collection("jobs")
.doc(widget.jobId)
.get());
applicants = job.applicants;
await FirebaseFirestore.instance
.collection('users')
.where('uid', whereIn: applicants)
.snapshots();
}
And the streambuilder in the scaffolds' body:
body: isLoading
? Center(child: Container(child: CircularProgressIndicator()))
: applicants.isEmpty
? Center(
child: Text("no values"),
)
: StreamBuilder<QuerySnapshot>(
stream: stream(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else { xy }
So my question is if there's a possibility to combine the first method and the stream. Because at the moment the user can't get any update if an application is withdrawn while using the screen.

Flutter Async: Future.wait() constantly returning null

For some reason, Future.wait() is constantly returning null. I'm not completely certain I am using it correctly.
For context, I have a collection of posts in Firebase. For each post, I can extract the userID assigned to it, then for each post individually I use the userID of the poster to grab the username for display purposes. I grab the Post from a snapshot:
static Future<Post> fromSnapshot(QueryDocumentSnapshot<Object?> doc) async {
final _documentId = doc.id;
final _title = doc.get('title');
final _text = doc.get('text');
final _createdOn = doc.get('createdOn');
final _userID = doc.get('userID');
final userDoc = await FirebaseFirestore.instance.collection('users').doc(_userID).get();
final username = userDoc.get("username");
return Post(documentId: _documentId, title: _title, text: _text, createdOn: _createdOn, username: username);
}
and the extraction of posts occurs in a getPosts() function elsewhere:
Future<List<Post>> getPosts() async {
QuerySnapshot posts = await FirebaseFirestore.instance.collection('posts').get();
final allData = posts.docs.map(
(doc) async => await Post.fromSnapshot(doc)
).toList();
print(allData); // [Instance of 'Future<Post>', Instance of 'Future<Post>', Instance of 'Future<Post>']
final futurePosts = Future.wait(allData);
print(futurePosts); // Instance of 'Future<List<Post>>'
// why does this always return null?
return futurePosts;
}
the problem is it has to be async to extract the posts but also to get the username, meaning it returns a future list of future posts. I want to pass the result of getPosts() to a FutureBuilder, so I need a Future List of posts, and to not make all the posts Future I use Future.wait - but that always seems to return null. Essentially, I am mapping each post in the snapshot to its own Post item, where in the constructor it needs to run a further async call to extract the username. Am I missing something?
Note: even making the Future.wait() await returns null, it just also doesn't return a List of type Future so I can't use it in the FutureBuilder either.
Edit 1:
It turns out that futurePosts is actually an Instance of 'Future<List<Post>>', but when accessing the data within the FutureBuilder, snapshot.data is null:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Feed'),
),
body: FutureBuilder(
future: getPosts(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
print(snapshot.data);
return postsToColumn(context, snapshot.data as List<Post>);
}
return const Center(
child: CircularProgressIndicator(),
);
}
),
);
}
Ok, lots of thanks to #IvoBeckers for helping me pin this down. It turns out, the snapshot actually did have an error, as they said, but this doesn't get printed unless you print it explicitly:
if (snapshot.hasError) {
print(snapshot.error.toString());
}
And the error is
Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist
So it turns out that not every User has a corresponding entry in the users collection with its username, which sounds like something I should have checked before, but I thought such an error would be printed out in the console. Once I updated the users collection, it worked perfectly.

Flutter Fire Store - Assigning individual document fields to variables

I'm attempting to show a user's profile image on their home page by pulling the user's 'imageUrl' from their Fire Store document. I already have the app setup to where the user can upload a new image which updates the 'imageUrl' in Fire Store, but I don't know how to have the 'imageUrl' as a variable so I can show it on the app screen.
I've been reading documentation online but It seems over simplified or out of date. I've tried using StreamBuilder, but it pulls the data from every user in the database instead of for a single user. I just need to know how to pull this one value and use it as a variable in my dart code using "getString()" with a document reference or the collection reference I already have, thank you.
class _UserPageState extends State<UserPage> {
User user = auth.currentUser!;
final CollectionReference collectionReference = FirebaseFirestore.instance.collection('users');
// Get profileImageUrl from users userDoc
String imageUrl = 'test'; // this should be the users imageUrl
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'${user.email}'), // this is being pulled from authentication not firestore
),
body: Center(
child: Column(
children: [
// --------------------------- I tried using a stream builder here ---------------------
StreamBuilder(
stream: collectionReference.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text(
'Something went wrong.'); // A: use incase the data does not load
}
final data = snapshot.requireData;
return ListView.builder(
shrinkWrap: true,
itemCount: data.size,
itemBuilder: (context, index) {
return Text(
// A: Stream builder will update with all of the users email addresses, I want this for one user exclusively
'My email is ${data.docs[index]['email']}');
},
collection('users')
.where("uid", isEqualTo: uid)
.snapshots(),
To filter the data in firestore collection use "where". Store the user uid in offline and query it by where using the stored uid
You can use the following function to get single data from stream.
Stream<UserModel> getSingleStreamData({String? uId}) {
return ref!.where(CommonKeys.id, isEqualTo: uId).snapshots().map((value) => value.docs.first.data());}

LateInitializationError: Field has not been initialized.' .then() command not running after running firebase query in FutureBuilder

I am trying to retrieve a Firestore Snapshot and my code doesn't seem to be working. I made sure fireUser.uid was working, and it printed the right ID but strangely my .then() code isn't running at all I put print('then') in it and isn't appearing on my console
this is where the error is occurring:
FutureBuilder(
future: Future.wait([
DatabaseService.getUserDataFromFirestore(FirebaseAuth.instance.currentUser!),
GeoService.getPosition(),
]),
builder: (context, snap) {
if (snap.connectionState == ConnectionState.done) {
return Frame();
}
else return Container(
color: Colors.black,
child: Center(
child: spinKit,
),
);
}
);
Future with error:
static Future<Userdata> getUserDataFromFirestore (User fireUser) async {
await usersRef.doc(fireUser.uid).get().then((val) {
print('then');
userdata = Userdata.fromDoc(val);
});
return userdata;
}
error message:
LateInitializationError: Field 'userdata' has not been initialized.
I had a different problem then I thought. Firestore must have been updated because the rules of my Firestore database kept me locked out so I updated the rules and now my code works fine. Thanks so much for the help