I want to fetch the particular users' liked posts and show them in Staggered grid view. I can able to do this in FutureBuilder. But in FutureBuilder because of the whereIn
limit of 10, I cannot fetch more. I know it can be implemented using StreamBuilder.But I am helpless and don't know how to do it.
This is my firebase collection.
Below is the code for fetch data:
final FirebaseFirestore firestore = FirebaseFirestore.instance;
getData() async {
SharedPreferences sp = await SharedPreferences.getInstance();
String _uid = sp.getString('uid');
final DocumentReference ref = firestore.collection('users').doc(_uid);
DocumentSnapshot snap = await ref.get();
List d = snap['loved items'];
List filteredData = [];
if (d.isNotEmpty) {
await firestore
.collection('contents')
.where('timestamp', whereIn: d)
.get()
.then((QuerySnapshot snap) {
filteredData = snap.docs;
});
}
notifyListeners();
return filteredData;
}
This is FutureBuilder Code:
body: sb.guestUser == true
? EmptyPage(
icon: FontAwesomeIcons.heart,
title: 'No wallpapers found.\n Sign in to access this feature',
)
: FutureBuilder(
future: context.watch<BookmarkBloc>().getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data.length == 0)
return EmptyPage(
icon: FontAwesomeIcons.heart,
title: 'No wallpapers found',
);
return _buildList(snapshot);
} else if (snapshot.hasError) {
return Center(
child: Text(snapshot.error),
);
}
return Center(
child: CupertinoActivityIndicator(),
);
},
),
),
);
}
Widget _buildList(snapshot) {
return StaggeredGridView.countBuilder(
crossAxisCount: 4,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
List d = snapshot.data;
return InkWell(
child: Stack(
children: <Widget>[
Hero(
tag: 'bookmark$index',
child: cachedImage(d[index]['image url'])),
Positioned(
bottom: 15,
left: 12,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
d[index]['category'],
style: TextStyle(color: Colors.white, fontSize: 18),
)
],
),
),
Positioned(
right: 10,
top: 20,
child: Row(
children: [
Icon(Icons.favorite,
color: Colors.white.withOpacity(0.5), size: 25),
Text(
d[index]['loves'].toString(),
style: TextStyle(
color: Colors.white.withOpacity(0.7),
fontSize: 16,
fontWeight: FontWeight.w600),
),
],
),
),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsPage(
tag: 'bookmark$index',
imageUrl: d[index]['image url'],
catagory: d[index]['category'],
timestamp: d[index]['timestamp'],
)));
},
);
},
staggeredTileBuilder: (int index) =>
new StaggeredTile.count(2, index.isEven ? 4 : 3),
mainAxisSpacing: 10,
crossAxisSpacing: 10,
padding: EdgeInsets.all(15),
);
}
}
I referred internet and I don't exactly know how to convert FutureBuilder into StreamBuilder which returns a list of snapshots. Is there any other way to do it ?
CodePudding user response:
First, the "whereIn
limit of 10" is a Firestore hard constraint. It means that whether you use FutureBuilder
or StreamBuilder
, that limit-of-10 constraint still applies.
Now, if you still want to switch to StreamBuilder
StreamBuilder<QuerySnapshot>(
stream: _yourStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
And you will need to adjust this to become a Stream
await firestore
.collection('contents')
.where('timestamp', whereIn: d)
.get()
.then((QuerySnapshot snap) {
filteredData = snap.docs;
});
You need to change the .get( )
to .snapshots()
and need to use snapshot.data.docs.map
More details in the Stream example here https://firebase.flutter.dev/docs/firestore/usage
EDIT:
Concerning the 10-items hard constraint: Depending on your specific project you can either:
query multiple times then merge locally in your application (while removing duplicates), or
another solution is to do some filtering client-side in your application (basically, bring more from FireStore and filter in flutter), or
another solution is to create new fields that are a combination of other fields (for example in your case, it could be "month" or "weekNumber" so instead of 7 days you have a week in 1 condition)
CodePudding user response:
Firestore does not allow you to make an array membership query with more that 10 records. If you check this documentation you will see that:
The where() method takes three parameters: a field to filter on, a comparison operator, and a value. The ‘in’ operator out of the many comparison operators of where() method is used to combine upto 10 equality (==) clauses on the same field with a logical OR.
If you use more than 10 records, Firestore will not accept it and you will get an error. I suggest that you break the “d” array into multiple arrays with a max of 10 records to make the query, this will allow Firestore to operate the whereIn within its limits and it will work.
Also a similar stackoverflow thread suggests using chunkSizeCollection which splits the array in chunks of 10 elements.