Flutter How to apply search filter in a querysnapshot used in Listview builder - flutter

This is my firebase data request code
and this is my future builder based on the qn.docs
How to search within the snapshot and ListView.builder to use the filtered set.
previously I was using a Local List and used to search as on Itemchanged
thanks in advance for your guidance.
I am new to flutter. so please explain with an example
Future <QuerySnapshot> getSpeakernames() async {
QuerySnapshot qn = await
FirebaseFirestore.instance.collection('speakernames').orderBy('speakername').get();
return qn;
}
Center(
child: Container(
child: ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
DocumentSnapshot data = snapshot.data.docs[index];
return ListTile(title: Text(data.get("speakername")),
subtitle: Text(data.get("speakerdegree")), )
onItemChanged(String value) {
setState(() {
data.get("speakername").
newspeakernames = speakernames
.where((string) =>
string.toLowerCase().contains(value.toLowerCase()))
.toList();
});
}

you can use ".Where" property of Firestore
like below
Future <QuerySnapshot> getSpeakernames() async {
QuerySnapshot qn = await FirebaseFirestore.instance.collection('speakernames')
.where('speakername' , isEqualTo : SubramanianV).orderBy('speakername').get();
return qn;
}

You can use isGreaterThanOrEqualTo.
Example:
class doctorName {
getDoctorByName(String doctorName, String last) async {
return await Firestore.instance
.collection("Doctor")
.where("name", isGreaterThanOrEqualTo: doctorName)
.where("name", isLessThan: last)
//.where("name", isEqualTo: doctorName)
.getDocuments();
}
}

Related

How to delete documents that contain a certain value in one of the fields. Firestore, Flutter

enter image description here
I have these documents, which contain data about each task I add to the list in my app.
child: StreamBuilder(
stream: _tasks.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.hasData) {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return GestureDetector(
onLongPress: () => _update(documentSnapshot),
child: ListTile(
)
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
I am using a stream builder to build the list. Each of the tasks have a checkmark and when i click it, It updates the value in firestore inside the IsDone field accordingly. I want to click a button outside the stream builder to delete the checked tasks. How do I loop through all the documents and find all the documents that contain the value true and delete them?
I tried this but im doing doing something wrong and it isnt changing anything:
void _delete() {
var docs = _tasks.doc().snapshots();
docs.forEach((doc){
if(doc.data()==true){
_tasks.doc(doc.id).delete();
}
});
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('You have successfully deleted a product')));
}
var datas = await FirebaseCalls.firebaseFirestore
.collection("collection_id")
.where("status", isEqualTo: true)
.get();
datas.docs.forEach((element) {
FirebaseFirestore.instance
.collection("collection_id")
.doc(element.id)
.delete();
});
or you can do like this as well.
var datas =
await FirebaseCalls.firebaseFirestore.collection("collection_id").get();
datas.docs
.where((element) => element["status"] == true)
.toList()
.forEach((el) {
FirebaseFirestore.instance
.collection("collection_id")
.doc(el.id)
.delete();
});
You can delete by doing this below:
find the particular document as per your query and get its document and collection ID.
FirebaseFirestore.instance
.collection("collection_id")
.doc("doc_id")
.delete();

How can I convert FutureBuilder code to StreamBuilder?

I am trying to get data from Firestore and pass that data to screen using stream. I have done this using FutureBuilder, this solution works as followed, but I need to use StreamBuilder Can anyone help me find the problem?
Future<List<Business>> list(FirebaseFirestore _firesore) async {
CollectionReference _col = _firesore.collection('Buisiness');
var _result = await _col.get();
var _docs = _result.docs;
return List.generate(_docs.length, (index) {
var satir = _docs[index].data();
return Business.fromMap(satir as Map<String, dynamic>);
});
}
This Code works in FutureBuilder but not StreamBuilder
StreamBuilder<List<Business>>(
stream: _firestorelist.list(_firestore), // Error Here
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Business>? data = snapshot.data;
return ListView.builder(
itemCount: data!.length,
itemBuilder: (context, index) {
var result = data[index];
return ListTile(
title: Text(result.nereden),
subtitle: Text(result.nereye),
trailing: Text(result.fiyat),
);
},
);
} else {
return CircularProgressIndicator();
}
},
)```
You can write your data source method as
Stream<List<Business>> list(FirebaseFirestore _firesore) {
CollectionReference _col = _firesore.collection('Buisiness');
final _snap = _col.snapshots();
return _snap.map((event) => event.docs
.map<Business>((e) => Business.fromMap(e.data() as Map<String, dynamic>))
.toList());
}
The current method is a One-time Read method, You can get snapshots from the specific collection.
You can change the method like this and Then use it as stream in streamBuilder:
list(FirebaseFirestore _firesore) async {
CollectionReference _col = _firesore.collection('Buisiness');
var _result = await _col.snapshots();
return _result;
}

Flutter SqlBrite not rebuilding streambuilder list

I am trying to build a chat storage system with firebase, sqlite and sqlBrite.
The aim of this is to stream the newmessages without having to rebuild the page. The stream from sqlBrite is only rebuilding on setstate eg.when the keyboard is drawn back.
How can i get the stream to automatically update on save.
The db document
///INSERT INTO DB
Future<int> insertNewMessage(String id, int result, BriteDatabase briteDb,
Map<String, dynamic> row) async {
messageList.add(id);
await ifexists(id, messageId, briteDb)
? print('message already In')
: result = await briteDb.insert(messageTable, row);
return result;
}
////STREAM MESSAGES
Stream<List<Map<String, dynamic>>> getMessageMapListbyId(
{String sendId, String receiveId, database}) async* {
try {
BriteDatabase briteDb = await database;
yield* briteDb.createQuery(messageTable,
distinct: false,
where:
' $senderId=? $receiverId = ? ',
whereArgs: [
sendId,
receiverId,
])});
provider document
///ADD MESSAGES
addMessageTodb(message) async {
await ldbH
.msg_insertMessage(
message.id, modelFuncs.messageMaping(message, msgFuncs))
.then((value) async {
await getMessageYieldBase(message.senderId, message.receiverId);
});}
///STREAM NEW DATA
getMessageYieldBase(senderId, receiverId) async* {
yield* ldbH.msg_getAllMessagesbyId(senderId, receiverId);}
The ui side
StreamBuilder(
stream: messageStream.getMessageYieldBase(
widget._currentUserId, widget.receiver.uid),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
var d = snapshot.data;
var newList = snapshot.hasData ? d.reversed.toList() : [];
return
ListView.builder(
reverse: true,
padding: EdgeInsets.all(10),
controller: widget._listScrollController,
itemCount: newList.length,
itemBuilder: (BuildContext context, int index) {
return DisplayMessage(
currentUserId: widget._currentUserId,
receiver: widget.receiver,
message: newList[index],
);
});
})
So the new texts keep coming only when the page rebuilds in some sort of way.
Any help rendered is appreciated.
If you are facing this problem use the moor library sqlbrite won't work but this is a link to help....
https://resocoder.com/2019/06/26/moor-room-for-flutter-tables-queries-fluent-sqlite-database/
Matt Rešetár explains in detail so it will be easy to implement...

Flutter - NoSuchMethodError: Class '_MapStream<QuerySnapshotPlatform, QuerySnapshot>' has no instance method 'then'

I wanted to retrieve data from my sub collection. It should return the list of friendid.
But I keep getting the NoSuchMethodError snapshot has no instance method then error with the code below.
The error is at firebaseMethods.getFriend(Constant.currentId).then((value) line.
Widget friendList() {
return StreamBuilder(
stream: friendlistStream,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return FriendTile(
snapshot.data.docs[index].data()["friendid"]);
},
)
: Container();
},
);
}
#override
void initState() {
getUserFriend();
super.initState();
}
getUserFriend() async {
Constant.currentId =
await HelperFunctions.getUserIdSharedPreference(Constant.currentId);
setState(() {
firebaseMethods.getFriend(Constant.currentId).then((value) {
setState(() {
friendlistStream = value;
});
});
});
}
The code for firestore is as below.
getFriend(String ownerid) {
return FirebaseFirestore.instance
.collection("users")
.doc(ownerid)
.collection("friends")
.snapshots();
}
I had tried hardcoding the Constant.currentId to the actual ID that I wanted to retrieve but still having the same error. What should I do to display the list of friendid correctly?
Future getFriend(String ownerid) async {
return await FirebaseFirestore.instance
.collection("users")
.doc(ownerid)
.collection("friends")
.get();
}
.then() is used for futures so your getFriend() method needs to return a Future
If you want to use the Stream than you need to use a StreamBuilder instead of calling a function in initState
This might help: https://www.youtube.com/watch?v=MkKEWHfy99Y&ab_channel=GoogleDevelopers

how can I add lazy loading to this list?

This is the how I fetch the posts in postList from firebase firestore, I need a function that works to get more posts on scroll. This next set of posts have to start after the last post that is displayed in this initial list and add to this list as the user scrolls as long as there are posts in the firestore.
class _FeedScreenState extends State<FeedScreen> {
List<Post> _posts = [];
ScrollController _controller = ScrollController();
#override
void initState() {
super.initState();
_setupFeed();
_controller.addListener(_scrollListener);
}
_scrollListener() {
setState(() {
if (_controller.position.atEdge) {
if (_controller.position.pixels == 0) {
} else {
_getMore();
}
}
});
}
_setupFeed() async {
List<Post> posts = await DatabaseService.getFeedPosts(widget.currentUserId);
setState(() {
_posts = posts;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
'New List',
style: TextStyle(
color: Colors.black,
fontSize: 35.0,
),
),
),
body: RefreshIndicator(
onRefresh: () => _setupFeed(),
child: ListView.builder(
controller: _controller,
itemCount: _posts.length,
itemBuilder: (BuildContext context, int index) {
Post post = _posts[index];
return FutureBuilder(
future: DatabaseService.getUserWithId(post.authorId),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return SizedBox.shrink();
}
User author = snapshot.data;
return PostView(
currentUserId: widget.currentUserId,
post: post,
author: author,
);
},
);
},
),
),
);
}
}
this is how i fetch the list of posts
static Future<List<Post>> getFeedPosts(String userId) async {
QuerySnapshot feedSnapshot = await feedsRef
.document(userId)
.collection('userFeed')
.orderBy('timestamp', descending: true)
.limit(30)
.getDocuments();
List<Post> posts =
feedSnapshot.documents.map((doc) => Post.fromDoc(doc)).toList();
return posts;
}
na2axl answer was almost right. I will add here an explanation and example of how to use startAfter()
If you check the documentation on pagination, you will see that you need to use startAfter() referencing whatever filter you used. In your case you are ordering using timestamp so your next query should look like this:
static Future<List<Post>> getNextFeedPosts(String userId, TimeStamp timestamp) async {
QuerySnapshot feedSnapshot = await feedsRef
.document(userId)
.collection('userFeed')
.orderBy('timestamp', descending: true)
//Here you need to let Firebase know which is the last document you fetched
//using its timesTamp
.startAfter(timestamp)
.limit(30)
.getDocuments();
List<Post> posts =
feedSnapshot.documents.map((doc) => Post.fromDoc(doc)).toList();
return posts;
}
This means that your next query will still be ordered by a timestamp but the first document retrieved will be after the timestamp on startAfter
I hope this helps, however, you can check the documentation as there are other examples!
I think doing this will solve your issue:
You have to edit your getFeedPosts to collect your posts starting at a given index:
I'm not familiar to FireStore, I've found the startAt() method on docs
EDIT: I've misunderstood a Firestore concept, so I've change startAt() to startAfter() following Francisco Javier Snchez advice
static Future<List<Post>> getFeedPosts(String userId, TimeStamp start) async {
QuerySnapshot feedSnapshot = await feedsRef
.document(userId)
.collection('userFeed')
.orderBy('timestamp', descending: true)
.startAfter(start)
.limit(30)
.getDocuments();
List<Post> posts =
feedSnapshot.documents.map((doc) => Post.fromDoc(doc)).toList();
return posts;
}
Now you can query it like this:
_getMore() async {
// You have to give the timestamp of the last post here
// Change this line by the right way...
List<Post> posts = await DatabaseService.getFeedPosts(widget.currentUserId, _posts[_posts.length - 1].timestamp);
setState(() {
// Do += instead of =, += will add fetched posts to the current list, = will overwrite the whole list
_posts += posts;
});
}
Hope this will help!