Get Database from firestore and using within RefreshIndicator in Flutter - flutter

Hello everyone, I am getting data from firestore . In this case ı am trying to add in RefreshIndicator() to onRefresh(); when ı am using just a data without query , ı dont see any mistake ,it work clearly like that
tumGonderiler() async {
QuerySnapshot myQuerySnapshot = await akisRef.get();
setState(() {
this.tEdilenGonderiler = myQuerySnapshot.docs.map((e) => e.data()).toList();
});
}
but when ı am trying to query in my collection with where parameter , onRefresh() doesent work in my page , the code that does not work is as follows
tumGonderiler() async {
QuerySnapshot myQuerySnapshot = await akisRef.where("ownerID", whereIn: takipEdilenKullanicilar.map((e) => e.id).toList()).get();
setState(() {
this.tEdilenGonderiler = myQuerySnapshot.docs.map((e) => e.data()).toList();
});
}

I've changed my code a little bit untill now it's working right now you can follow from below
tumGonderiler() async {
QuerySnapshot snapshot1 = await takipEdilenRef.doc(anlikKullanici!.id).collection("takipEdilenler").get();
List<tEdilen> kullaniciress = snapshot1.docs.map((doc) => tEdilen.fromDocument(doc)).toList();
setState(() {
this.takipEdilenKullanicilar = kullaniciress;
});
QuerySnapshot myQuerySnapshot = await akisRef.where("ownerID", whereIn: takipEdilenKullanicilar.map((e) => e.id).toList()).get();
List<Gonderiler> kullanicires = myQuerySnapshot.docs.map((e) => Gonderiler.fromDocument(e)).toList();
setState(() {
this.tEdilenGonderiler = kullanicires;
});
}
return Scaffold(
backgroundColor: Colors.white,
appBar: baslik(context, strBaslik: "Kartlar", geriButonuYokSay: true),
body: RefreshIndicator(
color: Colors.black,
child: ListView(
children: tEdilenGonderiler,
),
onRefresh: () {
return tumGonderiler();
}),
);

Related

Search bar with isar Database

hi and hello everyone ,
i am new with flutter and dart
and i am start using isar database
and everything is good and ok
but i have problem with Search
i create function for search
getAll(String search) async* {
final isar = await db;
final query = isar.books
.where()
.filter()
.titleContains(search)
.build();
await for (final results in query.watch(fireImmediately: true)) {
if (results.isNotEmpty) {
yield results;
}
}
}
and i add Search i home screen like this in TextField :
onChanged: (value) {
isarService.getAll(search: value);
}
,
but not work with me i try everthing but no work
soory for my english
Inside your getAllBooks() just remove .build()
Stream<List<Book>> getAllBooks({String? search}) async* {
print(search);
final isar = await db;
final query = isar.books
.where()
.filter()
.titleContains(search ?? '', caseSensitive: false);
await for (final results in query.watch(fireImmediately: true)) {
if (results.isNotEmpty) {
yield results;
}
}
}
Convert your book_list_screen.dart class in StatefulWidget, and use a variable.
String search = ""; //for searching
Inside your TextField's onChanged()
onChanged: (value) {
setState(() {
search = value;
});
},
and inside your Expanded widget use something like this
Expanded(
child: StreamBuilder<List<Book>>(
stream: widget.isarService.getAllBooks(search: search),
builder: (context, snapshot) {
...
}
),
maybe you should wait for the response:
onChanged: (value) async {
await isarService.getAll(search: value);
}

Flutter function that returns conditional does not give correct result

I have a code like this:
//...
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage("${snapshot.data!.docs[index].data()['urunFotografi']}"),
),
title: Text(snapshot.data!.docs[index].data()["urunAdi"], style: TextStyle(fontSize: 20),),
subtitle: Text(adetVeyaKilo().toString(), style: TextStyle(fontSize: 15),),
),
// ...
// ...
Future<String> adetVeyaKilo() async {
String state = "";
FirebaseFirestore.instance.collection('bolatAktar').where('urunAdi', isEqualTo: urunAdi).get().then((value) {
value.docs.forEach((element) {
if (element.data()["urunBirimi"] == "Adet") {
state = "Adet";
}
if (element.data()["urunBirimi"] == "Kilo") {
state = "Kilo";
}
});
});
return state;
}
// ...
With the codes I wrote, I am trying to make a return according to the data coming from Firebase Firestore.
But when trying to print to subTitle it prints like this: Instance of Future<String>
Why does this problem occur? How can I solve it?
Thanks in advance for your help.
Try this:
Future<String> adetVeyaKilo() async {
String state = "";
await FirebaseFirestore.instance.collection('bolatAktar').where('urunAdi', isEqualTo: urunAdi).get().then((value) {
value.docs.forEach((element) {
if (element.data()["urunBirimi"] == "Adet") {
setState((){
state = "Adet";
});
}
if (element.data()["urunBirimi"] == "Kilo") {
setState((){
state = "Kilo";
});
}
});
You need to write await before FirebaseFirestore.instance.collection('bolatAktar')....
Or you can do like this. This one is better
final response = await FirebaseFirestore.instance.collection('bolatAktar').where('urunAdi', isEqualTo: urunAdi).get();
for(int i=0;i<response.docs.length;i++){
if(response.docs[i].data()["urunBirimi"] == "Adet"){
state = "Adet"
}
if(response.docs[i].data()["urunBirimi"] == "Kilo"){
state = "Kilo";
}
}

How to await or rebuild Consumer when data is loaded

How do I reload Consumer when data is loaded or await for data to load. I am using Future Provider and everything is rebuilding itself when data is loaded (currentPosition Fetched) and using circularProgress() while waiting. But consumer is not rebuilding itself aslo can't use await with consumer package. When I save the code while debugging when it hot reload everything is okay but that's nit a solutiion. I want the consumer auto reload when data is fetched. I am fetching the data to make markers on google_Maps_Flutter
body: (currentPosition != null)
? Consumer<List<Bar>>(builder: (_, places, child) {
List.generate(places.length, (index) async {
print(places.length);
print(index);
print(imageUrl(places[index].photoRef));
List<String> wordList = places[index].name.split(" ");
bitmapIcon = await customBitmapDescriptor(
imageUrl: imageUrl(places[index].photoRef),
title: wordList[0],
);
markers = markerService.getBarMarkers(
places,
markerIcon: this.bitmapIcon,
);
print(markers.isEmpty);
});
Use setState((){}); to rebuild when data is loaded. Add setState((){}); where you want to rebuild e.g. if you want to reload when data is loaded in bitmapIcon then add
bitmapIcon = await convertImageFileToCustomBitmapDescriptor(
imageUrl: imageUrl(places[index].photoRef),
title: wordList[0],
).then((value) {
setState(() {});
});
And if you want to reload when data is loaded in marker then use
setState(() {
markers = markerService.getBarMarkers(
places,
markerIcon: this.bitmapIcon,
);
});
First Scenario
body: (currentPosition != null)
? Consumer<List<Bar>>(builder: (_, places, child) {
List.generate(places.length, (index) async {
print(places.length);
print(index);
print(imageUrl(places[index].photoRef));
List<String> wordList = places[index].name.split(" ");
bitmapIcon =await convertImageFileToCustomBitmapDescriptor(
imageUrl: imageUrl(places[index].photoRef),
title: wordList[0],
).then((value) {
setState(() {});
});
markers = markerService.getBarMarkers(
places,
markerIcon: this.bitmapIcon,
);
print(markers.isEmpty);
});
Second Scenario
body: (currentPosition != null)
? Consumer<List<Bar>>(builder: (_, places, child) {
List.generate(places.length, (index) async {
print(places.length);
print(index);
print(imageUrl(places[index].photoRef));
List<String> wordList = places[index].name.split(" ");
bitmapIcon = await convertImageFileToCustomBitmapDescriptor(
imageUrl: imageUrl(places[index].photoRef),
title: wordList[0],
);
if(!isSet){
setState(() {
markers = markerService.getBarMarkers(
places,
markerIcon: this.bitmapIcon,
);
});
}
print(markers.isEmpty);
});
Thumbs up if this solution helped
Using notifyListener() you can change the state of a consumer.
Below is a sample code. If You need more let me know.
Consumer<DataProviderClass>(
builder: (context, portfolioProvider, _) {
print("state changed");
return RaisedButton(
child: Text("Press"),
onPressed : () =>
Provider.of<DataProviderClass(context).fetchData(),
);
}
);
class DataProviderClass with ChangeNotifier{
fetchData(){
notifyListener();
}
}

How to NOT show the current user in a Grid View?

I have a function called getAllUsers() that returns all users from a database. The problem is that I want GridView.builder() to display all the users except the current user, but despite all the research I did, nothing seems to work out.
If i use the if condition like if(snapshot.data.documents[i].data["username"] != currentUserId within itemBuilder:, it returns a blank tile which represents the current user which creates a gap within the grid view. Thus, it makes the grid view look really bad.
I believe this problem could have been solved if I knew how to include the inequality query in the getAllUsers() method. But my understanding is that Firestore has yet to provide this function/argument.
HomeFragment class
Database _database = Database();
Stream _stream;
String currentUserId;
#override
void initState() {
getCurrentUserId();
getAllUsers();
super.initState();
}
getAllUsers() async {
return await _database.getAllUsers().then((val) {
if (mounted)
setState(() => _stream = val);
});
}
getCurrentUserId() async {
FirebaseUser currentUser = await FirebaseAuth.instance.currentUser();
currentUserId = currentUser.uid;
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _stream,
builder: (context, snapshot) {
return snapshot.data == null ? Center(child: CircularProgressIndicator())
: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child:
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8.0,
mainAxisSpacing: 8.0,
),
itemCount: snapshot.data.documents.length,
itemBuilder: (context, i) {
return Container(
child: Text(snapshot.data.documents[i].data["username"])
);
}
// etc etc..
Database class
getAllUsers() async {
return await _firestore.collection("users").snapshots();
}
I tried to use this, but _stream2 returns null
Stream _stream, _stream2;
getAllUsers() async {
return await _database.getAllUsers().then((val) {
if (mounted) {
List<String> list;
setState(() {
_stream = val;
_stream2 = _stream.where((snapshot) {
_querySnapshot = snapshot;
for (int i = 0; i < _querySnapshot.documents.length; i++)
list.add(_querySnapshot.documents[i].data["userId"]);
return list.contains(currentUserId) == false;
});
});
}
});
}
I also tried this, it is not working
getAllUsers() async {
Stream<QuerySnapshot> snapshots = await _database.getAllUsers();
_stream = snapshots.map((snapshot) {
snapshot.documents.where((documentSnapshot) {
return documentSnapshot.data["userId"] != currentUserId;
});
});
}
Maybe you can try something like this. You filter the query result:
getAllUsers() async {
final Stream<QuerySnapshot> snapshots = await _firestore.collection("users").snapshots();
return snapshots.map((snapshot) {
final result = snapshot.documents
.map((snapshot) => User.fromMap(snapshot.data)
.where((user) => user.id != currentUser.id)
.toList();
return result;
}
}
If you do not have an User class, you can replace some lines with this. But the result will be a list of Map<String, dynamic> instead of a list of User objects.
return snapshots.map((snapshot) {
final result = snapshot.documents
.map((snapshot) => snapshot.data
.where((user) => user['id'] != currentUser.id)
.toList();
return result;
This solution worked well for me.
firestore.collection('your collection').where('x', isNotEqualTo: auth.currentUser!.uid).snapshots();

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!