my Querysnapshot Map String become AsyncSnapshot<Object> with no reason - flutter

I have a streamBuilder where the "snapshots()" change from Stream<QuerySnapshot<Map<String, dynamic>>> to AsyncSnapshot<Object?>.
This is weird because I have another app, with identical code and works fine.
The problem is, I cannot access "snapshot.data.docs" and all firestore datas.
Why is happening?
my code:
body: StreamBuilder(
//Stream<QuerySnapshot<Map<String, dynamic>>>
stream: firestore.collection('Books').orderBy('name').snapshots(),
//AsyncSnapshot<Object?>
builder: (context, snapshot ) {
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
{
final document = snapshot.data?; //error here
return ListView.builder(
itemCount: document?.length,
itemBuilder: (context, index) {
return Column(

Declare your StreamBuilder Widget response type as a dynamic, and you should use
snapshot.data.docs without any problem.
body: StreamBuilder<dynamic>(
stream: FirebaseFirestore.instance.collection('Books').orderBy('name').snapshots(),
builder: (context, snapshots) {
if(snapshots.hasData){
print(snapshots.data.docs.toString());
}});

Related

Listview.builder inside StreamBuilder is rebuilding/flickering

So I used Streambuilder to fetch CartItems from Firestore and it starting to flicker and it always goes to the top. Also can't scroll down, it always goes to the top, I used streambuilder in different parts of the app and it works perfectly but here it causing the problem...
Stream<QuerySnapshot<Map<String, dynamic>>> getCartStream() {
CollectionReference<Map<String, dynamic>> ref = FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection("cart");
Stream<QuerySnapshot<Map<String, dynamic>>> stream = ref.snapshots();
return stream;
}
StreamBuilder(
stream: getCarStream(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Container();
} else {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
ProductModel productModel = ProductModel.fromMap(snapshot.data!.docs[index].data());
return CartItem(product: productModel);
},
);
}
},
)),
Rest of the code
Gif of the Problem
just remove the initState(), will work fine. if not then kindly share CARTITEM(..) class.

Modify Future Builder inside Stream Builder to avoid widget flickering

I am using a FutureBuilder inside a StreamBuilder that updates the UI every time a document is added to the activity collection, to get some aditional data from Firestore. The problem is that the FutureBuilder returns a SizedBox widget while the ConnectionState is waiting causing the all the cards to dissapear for a second. I would like to avoid this flickering since it causes a bad ui experience for users.
Is there a way to query the required user data in the activity stream so it all returns at once that way I can remove the FutureBuilder?
If not ... what would be a solution for this?
activityStream() {
return FirebaseFirestore.instance
.collection('activity')
.orderBy('timestamp', descending: true)
.limit(55)
.snapshots();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
StreamBuilder<QuerySnapshot>(
stream: activityStream(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
final activityContent = snapshot.data?.docs
.map((data) => ActivityModel.fromFirestore(data))
.toList();
return Scrollbar(
controller: widget.scrollController,
child: ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: activityContent!.length,
itemBuilder: (context, i) {
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.where('uid', whereIn: activityContent[i].players)
.get(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
final users = snapshot.data!.docs.map((e) {
return UserModel.fromFirestore(e);
}).toList();
return MyWidget(
users: users,
);
},
);
},
),
);
}
},
);

Wait a function to be done in ListView

I need to wait my function to be done in a ListView because of a Firestore request. I tried using Future.wait() but it does not work.
FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection('Statements')
.where('userID',isEqualTo: context.watch<User>().uid)
.get(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if (snapshot.hasError) {return Text("Erreur");}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");}
return Expanded(
child:ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot document = snapshot.data.docs[index];
Future.wait([getStatementData(document)]);
return StatementCard(statement:selectedStatement,
accommodation : relatedAccommodation,
owner : relatedOwner);
},
)
Here is the function called :
Future getStatementData(document) async {
selectedStatement = Statement.fromFirebase(document);
document.data()["accommodation"].get().then((value) {
relatedAccommodation = Accommodation.fromFirebase(value);});
await FirebaseFirestore.instance
.collection('Owners')
.doc(document["ownerList"][0])
.get().then((value) {
print(value.data());
relatedOwner = Owner.fromFirebase(value);
});
}
Should I use another future builder ?
You should do it like this by returning another Future Builder inside :
ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot document = snapshot.data.docs[index];
return FutureBuilder(
future: getStatementData(document) ,
builder: (context, snapshot) {
return snapshot.connectionState == ConnectionState.done
? StatementCard(statement:selectedStatement,
accommodation : relatedAccommodation,
owner : relatedOwner);
: Container();
);
},
)
I think you just missed a simple thing, FutureBuilder also has a connectionstate.done state which you can access. This waits until your future function is Done.
return FutureBuilder(
future: yourFunction,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Center(child: Text('No status',);
break;
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
break;
case ConnectionState.done:
return Text('Im done!') // Your listviewbuilder comes here
break;
default:
}
},
),
doc: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
example FutureBuilder with ListViewBuilder: https://medium.com/nonstopio/flutter-future-builder-with-list-view-builder-d7212314e8c9
[...]connectionState.done = [future] is not null, and has completed. If the future completed successfully, the [AsyncSnapshot.data] will be set to the value to which the future completed. If it completed with an error, [AsyncSnapshot.hasError] will be true and [AsyncSnapshot.error] will be set to the error object.[...]

How do I get only the current users posts to show?

I'm trying to build an app in flutter and I have come up against this problem which I can't seem to find any existing answers. How do I get only the current users posts to show? My posts collection has a user id field which I want to compare with the current user and display only the post where the userId and currentUser are the same.
return FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (ctx, futureSnapshot) {
if (futureSnapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
return StreamBuilder(
stream: Firestore.instance.collection('posts').snapshots(),
builder: (context, streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
final documents = streamSnapshot.data.documents;
return ListView.builder(
itemCount: documents.length,
itemBuilder: (ctx, index) => PostItem(
documents[index]['title'],
documents[index]['imageUrl'],
documents[index]['location']['address'],
));
});
});
here is my post collection structure
You're currently getting all posts with:
Firestore.instance.collection('posts').snapshots()
If you only want posts for the current user, that'd be something like:
var uid = (await FirebaseAuth.instance.currentUser()).uid;
Firestore.instance.collection('posts').where('uid', isEqualTo: uid).snapshots()
The first line determines the UID of the current user, and then the second line uses that to request only documents whose uid field matches the value.
I just realised I hadn't but my future into a variable for the streambuilder where clause !! I was trying to use the original fireauth currentuser!
return FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (ctx, futureSnapshot) {
if (futureSnapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
**final String currentUser** = futureSnapshot.data.uid;
return StreamBuilder(
stream: Firestore.instance
.collection('posts')
.where('userId', isEqualTo: **currentUser**)
.snapshots(),
builder: (context, streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
final documents = streamSnapshot.data.documents;
return ListView.builder(
itemCount: documents.length,
itemBuilder: (ctx, index) => PostItem(
documents[index]['userId'],
documents[index]['title'],
documents[index]['imageUrl'],
documents[index]['location']['address'],
));
});
});
```

Update ListView.builder itemCount when firestore document is added

I have a flutter app where a list is generated with ListView.Builder, and where the itemCount is the number of documents in a firestore collection. This works fine until a new document is added. When that happens I get the error (17 and 18 are just examples).
Invalid value: Not in range 0..17, inclusive: 18
I assume I would need to update the state when a new document is created, but I have no idea how i can call setState when that happens
Here is the relevant part of the code:
child: StreamBuilder(
stream: Firestore.instance.collection('contact').orderBy(sortby, descending: decending).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_personer(context, snapshot.data.documents[index], index),
);
},
),
use setState?
StreamBuilder(builder: (context, snapshot) {
return snapshot.hasData == null ? Container() : _getListView(snapshot);
} , )
_getListView(snapshot) {
setState(() {
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_personer(context, snapshot.data.documents[index], index),
);
});
}
StreamBuilder use QuerySnapshot so list data can change
example code :
StreamBuilder<QuerySnapshot>(
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Text('Loading...');
default:
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return ;
}).toList(),
);
}
},
)