Flutter Firestore pagination - google-cloud-firestore

I'm trying to paginate by using Firestore and I read the document and it implement like this in Swift
let first = db.collection("cities")
.order(by: "population")
.limit(to: 25)
first.addSnapshotListener { (snapshot, error) in
guard let snapshot = snapshot else {
print("Error retrieving cities: \(error.debugDescription)")
return
}
guard let lastSnapshot = snapshot.documents.last else {
// The collection is empty.
return
}
// Construct a new query starting after this document,
// retrieving the next 25 cities.
let next = db.collection("cities")
.order(by: "population")
.start(afterDocument: lastSnapshot)
// Use the query for pagination.
// ...
}
Just for practice, I tried fetched three documents and if button tapped, fetch one more document.
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').limit(3).getDocuments().then((snapshot) {
_lastDocument = snapshot.documents.last;
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
After button tapped tried like this.
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter(_lastDocument).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
But console says this.
The following assertion was thrown while handling a gesture: type
'DocumentSnapshot' is not a subtype of type 'List[dynamic]'
Why do I have to pass list?
Does anyone know how to fix this?
UPDATE
I was able to paginate like so.
class PaginationExample extends StatefulWidget {
#override
_PaginationExampleState createState() => _PaginationExampleState();
}
class _PaginationExampleState extends State<PaginationExample> {
var _restaurants = <Restaurant>[];
var _nomore = false;
var _isFetching = false;
DocumentSnapshot _lastDocument;
ScrollController _controller;
void _fetchDocuments() async {
final QuerySnapshot querySnapshot = await Firestore.instance.collection('restaurants').orderBy('likes').limit(8).getDocuments();
// your logic here
}
Future<Null> _fetchFromLast() async {
final QuerySnapshot querySnapshot = await Firestore.instance.collection('restaurants').orderBy('likes').startAfter([_lastDocument['likes']]).limit(4).getDocuments();
if (querySnapshot.documents.length < 4) {
_nomore = true;
return;
}
_lastDocument = querySnapshot.documents.last;
for (final DocumentSnapshot snapshot in querySnapshot.documents) {
final Restaurant re = Restaurant(snapshot);
_restaurants.add(re);
}
setState(() {});
}
void _scrollListener() async {
if (_nomore) return;
if (_controller.position.pixels == _controller.position.maxScrollExtent && _isFetching == false) {
_isFetching = true;
await _fetchFromLast();
_isFetching = false;
}
}
#override
void initState() {
_fetchDocuments();
_controller = new ScrollController()..addListener(_scrollListener);
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
);
}
}

There is an error here:
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter(_lastDocument).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
startAfter method expects a List value params and you are passing a DocumentSnapshot.
Takes a list of [values], creates and returns a new [Query] that
starts after the provided fields relative to the order of the query.
You could try something like this:
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter([{'name': 'Tom'}]).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});

Paginate just with 2 attrubutes, itemBuilder and query using this package - paginate_firestore
For example,
PaginateFirestore(
itemBuilder: (context, documentSnapshot) => ListTile(
leading: CircleAvatar(child: Icon(Icons.person)),
title: Text(documentSnapshot.data['name']),
subtitle: Text(documentSnapshot.documentID),
),
// orderBy is compulsary to enable pagination
query: Firestore.instance.collection('users').orderBy('name'),
)

This works for me giving realtime pagination
defining functions to fetch data
import 'package:cloud_firestore/cloud_firestore.dart';
import '../../../core/constants/firebase_constants.dart';
class FirebaseProvider {
final FirebaseFirestore _firestore;
FirebaseProvider({required FirebaseFirestore firestore})
: _firestore = firestore;
CollectionReference get _posts =>
_firestore.collection(FirebaseConstants.postsCollection);
Future<List<DocumentSnapshot>> fetchFirstList(
String fromgst, String postType) async {
return (await _posts
.where("fromgst", isEqualTo: fromgst)
.where("postType", isEqualTo: postType)
.orderBy("date", descending: true)
.limit(5)
.get())
.docs;
}
Future<List<DocumentSnapshot>> fetchNextList(String fromgst, String postType,
List<DocumentSnapshot> documentList) async {
return (await _posts
.where("fromgst", isEqualTo: fromgst)
.where("postType", isEqualTo: postType)
.orderBy("date", descending: true)
.startAfterDocument(documentList[documentList.length - 1])
.limit(5)
.get())
.docs;
}
}
separate class to handle pagination
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:growmore/features/home/repository/firebase_provider.dart';
import 'package:rxdart/rxdart.dart';
class PostListBloc {
List<DocumentSnapshot>? documentList;
bool showIndicator = false;
FirebaseProvider? firebaseProvider;
BehaviorSubject<List<DocumentSnapshot>>? postController;
BehaviorSubject<bool>? showIndicatorController;
PostListBloc() {
postController = BehaviorSubject<List<DocumentSnapshot>>();
showIndicatorController = BehaviorSubject<bool>();
firebaseProvider = FirebaseProvider(firestore: FirebaseFirestore.instance);
}
Stream get getShowIndicatorStream => showIndicatorController!.stream;
Stream<List<DocumentSnapshot>> get postStream => postController!.stream;
// This method will automatically fetch first 10 elements from the document list
Future fetchFirstList(String fromgst, String postType) async {
try {
documentList = await firebaseProvider?.fetchFirstList(fromgst, postType);
print("documentList$documentList");
postController?.sink.add(documentList!);
try {
if (documentList!.isEmpty) {
postController?.sink.addError("No Data Available");
}
} catch (e) {
print(e);
}
} on SocketException {
postController?.sink.addError(SocketException("No Internet Connection"));
} catch (e) {
print(e.toString());
postController?.sink.addError(e);
}
}
//This will automatically fetch the next 10 elements from the list
fetchNextPosts(String fromgst, String postType) async {
try {
updateIndicator(true);
List<DocumentSnapshot> newDocumentList = await firebaseProvider!
.fetchNextList(fromgst, postType, documentList!);
print('asca$newDocumentList');
documentList!.addAll(newDocumentList);
postController!.sink.add(documentList!);
try {
if (documentList!.isEmpty) {
postController!.sink.addError("No Data Available");
updateIndicator(false);
}
} catch (e) {
updateIndicator(false);
}
} on SocketException {
postController!.sink.addError(SocketException("No Internet Connection"));
updateIndicator(false);
} catch (e) {
updateIndicator(false);
print(e.toString());
postController!.sink.addError(e);
}
}
//For updating the indicator below every list and paginate*
updateIndicator(bool value) async {
showIndicator = value;
showIndicatorController!.sink.add(value);
}
void dispose() {
postController!.close();
showIndicatorController!.close();
}
}
the ui part
ScrollController controller = ScrollController();
#override
void initState() {
super.initState();
postListBloc = PostListBloc();
print("dvvfe${widget.fromgst}");
postListBloc!.fetchFirstList(widget.fromgst, widget.postType);
controller.addListener(_scrollListener);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<List<DocumentSnapshot>>(
stream: postListBloc!.postStream,
builder: (context, snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data?.length,
shrinkWrap: true,
controller: controller,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
title: Text(snapshot.data![index]['description']),
),
),
);
},
);
} else {
return const CircularProgressIndicator();
}
},
),
);
}
void _scrollListener() {
if (controller.offset >= controller.position.maxScrollExtent &&
!controller.position.outOfRange) {
print("Cavc$controller");
print("at the end of list");
postListBloc!.fetchNextPosts(widget.fromgst, widget.postType);
}
}
}
I found it not open source github repo

Related

How to read List of Instance of '_JsonQuerySnapshot' in Flutter

I am trying to read the data from a list that has the data of Instance of '_JsonQuerySnapshot'.
In the below code I am trying to get the data from DB
getData() async {
var db = FirebaseFirestore.instance;
var categoryList = [];
db.collection('categories')
.doc(value)
.get()
.then((DocumentSnapshot doc) {
var listSubCol = doc["categoryCollection"];
listSubCol.forEach((id) {
db.collection('categories')
.doc(value)
.collection(id)
.get()
.then((snapshot) {
categoryList.add(snapshot);
return categoryList;
});
});
});
}
Below is the code where I am printing the values.
FutureBuilder(
future: getData(),
builder: (BuildContext context, snapshot){
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.done) {
print('Data: ${snapshot.data}');
return Text("Text");
}
return Text("Text Outside");
}
)
I got a null value.
Can anyone please help me with how to get the data?
The getData is a Future<void>, so when the FutureBuilder is completed the snapshot.data is always null.
Change getData to return a List of whatever you are getting.
Future<List<Categories>> getData() async {
var db = FirebaseFirestore.instance;
final List<Categories> categoryList = [];
await db.collection('categories')
.doc(value)
.get()
.then((DocumentSnapshot doc) {
var listSubCol = doc["categoryCollection"];
listSubCol.forEach((id) {
db.collection('categories')
.doc(value)
.collection(id)
.get()
.then((snapshot) {
categoryList.add(snapshot);
return categoryList;
});
});
});
return categoryList;
}
getData() async {
var db = FirebaseFirestore.instance;
await db.collection('categories').doc(value).get().then((DocumentSnapshot doc) {
var listSubCol = doc["categoryCollection"];
listSubCol.forEach((id) {
db.collection('categories').doc(value).collection(id).get().then((snapshot) {
snapshot.docs.forEach((element) {
categoryList.add(element.data());
});
});
return categoryList;
});
});
return categoryList;
}
using the above method i got the data from Firestore.

RangeError index invalid value only valid value is empty 0 see also in Flutter

I'm in a Flutter project using Getx. Every time I enter the screen that lists the records I get an error message as you can see below;
I don't know where I'm going wrong, but I'll leave the main parts of the code. I need to find where I'm going wrong.
Class Repository
Future<List<Post>> getAlbum({
bool isFavoritedPage = false,
bool isNewEdition = false,
}) async {
dio.options.headers['Cookie'] = 'ASP.NET_SessionId=${user.sessionID}';
final response = await dio.get(
isFavoritedPage ? AppConstants.apiFavoritedsPost : AppConstants.apiPosts,
queryParameters: {
'sessionId': user.sessionID,
'CodUserProfile': '${user.codUser!}',
'CodUserLogged': '${user.codUser!}',
'Page': '${page}',
'pagesize': '10',
'myPostOnly': isFavoritedPage ? 'true' : 'false',
},
);
final body = response.data['ListPosts'] as List;
return body.map((post) => Post.fromJson(post)).toList();
}
Class Controller
var lstPost = List<Post>.empty(growable: true).obs;
var page = 1;
var isDataProcessing = false.obs;
// For Pagination
ScrollController scrollController = ScrollController();
var isMoreDataAvailable = true.obs;
#override
void onInit() async {
super.onInit();
// Fetch Data
getPost(page);
//For Pagination
paginateTask();
}
void getPost(var page) {
try {
isMoreDataAvailable(false);
isDataProcessing(true);
getAlbum(page).then((resp) {
isDataProcessing(false);
lstPost.addAll(resp);
}, onError: (err) {
isDataProcessing(false);
showSnackBar("Error", err.toString(), Colors.red);
});
} catch (exception) {
isDataProcessing(false);
showSnackBar("Exception", exception.toString(), Colors.red);
}
}
showSnackBar(String title, String message, Color backgroundColor) {
Get.snackbar(title, message,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: backgroundColor,
colorText: Colors.white);
}
void paginateTask() {
scrollController.addListener(() {
if (scrollController.position.pixels ==
scrollController.position.maxScrollExtent) {
print("reached end");
page++;
getMoreTask(page);
}
});
}
void getMoreTask(var page) {
try {
getAlbum(page).then((resp) {
if (resp.length > 0) {
isMoreDataAvailable(true);
} else {
isMoreDataAvailable(false);
showSnackBar("Message", "Não existem registro", Colors.lightBlueAccent);
}
lstPost.addAll(resp);
}, onError: (err) {
isMoreDataAvailable(false);
showSnackBar("Error", err.toString(), Colors.red);
});
} catch (exception) {
isMoreDataAvailable(false);
showSnackBar("Exception", exception.toString(), Colors.red);
}
}
#override
void onClose() {
searchDrawerEC.dispose();
super.onClose();
}
Future<List<Post>> getAlbum(pagina,[bool isFavoritedPage = false]) async {
final response =
await repository.getAlbum(isFavoritedPage: isFavoritedPage);
return response;
}
Class Page
Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
if (index == controller.lstPost.length - 1 &&
controller.isMoreDataAvailable.value == true) {
return Center(child: CircularProgressIndicator());
}
return PostWidget(post: controller.lstPost[index]);
}
),
),
I'm basing myself on this github project.
https://github.com/RipplesCode/FlutterGetXTodoAppWithLaravel/tree/master/lib/app/modules/home
I don't use getx, but I see something odd in your Listview.builder. It feels as if you're abusing it a little, to also show the "no data" case, and there's also no count. I think it should have a count, so something like this:
if (lstPost.isEmpty) {
return Center(child: CircularProgressIndicator());
} else {
return ListView.builder(
itemCount: lstPost.length,
itemBuilder: (BuildContext context, int index) {
return PostWidget(...);
}
);
}

Pagination for Flutter ListView.builder [duplicate]

I'm trying to paginate by using Firestore and I read the document and it implement like this in Swift
let first = db.collection("cities")
.order(by: "population")
.limit(to: 25)
first.addSnapshotListener { (snapshot, error) in
guard let snapshot = snapshot else {
print("Error retrieving cities: \(error.debugDescription)")
return
}
guard let lastSnapshot = snapshot.documents.last else {
// The collection is empty.
return
}
// Construct a new query starting after this document,
// retrieving the next 25 cities.
let next = db.collection("cities")
.order(by: "population")
.start(afterDocument: lastSnapshot)
// Use the query for pagination.
// ...
}
Just for practice, I tried fetched three documents and if button tapped, fetch one more document.
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').limit(3).getDocuments().then((snapshot) {
_lastDocument = snapshot.documents.last;
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
After button tapped tried like this.
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter(_lastDocument).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
But console says this.
The following assertion was thrown while handling a gesture: type
'DocumentSnapshot' is not a subtype of type 'List[dynamic]'
Why do I have to pass list?
Does anyone know how to fix this?
UPDATE
I was able to paginate like so.
class PaginationExample extends StatefulWidget {
#override
_PaginationExampleState createState() => _PaginationExampleState();
}
class _PaginationExampleState extends State<PaginationExample> {
var _restaurants = <Restaurant>[];
var _nomore = false;
var _isFetching = false;
DocumentSnapshot _lastDocument;
ScrollController _controller;
void _fetchDocuments() async {
final QuerySnapshot querySnapshot = await Firestore.instance.collection('restaurants').orderBy('likes').limit(8).getDocuments();
// your logic here
}
Future<Null> _fetchFromLast() async {
final QuerySnapshot querySnapshot = await Firestore.instance.collection('restaurants').orderBy('likes').startAfter([_lastDocument['likes']]).limit(4).getDocuments();
if (querySnapshot.documents.length < 4) {
_nomore = true;
return;
}
_lastDocument = querySnapshot.documents.last;
for (final DocumentSnapshot snapshot in querySnapshot.documents) {
final Restaurant re = Restaurant(snapshot);
_restaurants.add(re);
}
setState(() {});
}
void _scrollListener() async {
if (_nomore) return;
if (_controller.position.pixels == _controller.position.maxScrollExtent && _isFetching == false) {
_isFetching = true;
await _fetchFromLast();
_isFetching = false;
}
}
#override
void initState() {
_fetchDocuments();
_controller = new ScrollController()..addListener(_scrollListener);
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
);
}
}
There is an error here:
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter(_lastDocument).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
startAfter method expects a List value params and you are passing a DocumentSnapshot.
Takes a list of [values], creates and returns a new [Query] that
starts after the provided fields relative to the order of the query.
You could try something like this:
Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter([{'name': 'Tom'}]).limit(1).getDocuments().then((snapshot) {
snapshot.documents.forEach((snap) {
print(snap.data);
});
});
Paginate just with 2 attrubutes, itemBuilder and query using this package - paginate_firestore
For example,
PaginateFirestore(
itemBuilder: (context, documentSnapshot) => ListTile(
leading: CircleAvatar(child: Icon(Icons.person)),
title: Text(documentSnapshot.data['name']),
subtitle: Text(documentSnapshot.documentID),
),
// orderBy is compulsary to enable pagination
query: Firestore.instance.collection('users').orderBy('name'),
)
This works for me giving realtime pagination
defining functions to fetch data
import 'package:cloud_firestore/cloud_firestore.dart';
import '../../../core/constants/firebase_constants.dart';
class FirebaseProvider {
final FirebaseFirestore _firestore;
FirebaseProvider({required FirebaseFirestore firestore})
: _firestore = firestore;
CollectionReference get _posts =>
_firestore.collection(FirebaseConstants.postsCollection);
Future<List<DocumentSnapshot>> fetchFirstList(
String fromgst, String postType) async {
return (await _posts
.where("fromgst", isEqualTo: fromgst)
.where("postType", isEqualTo: postType)
.orderBy("date", descending: true)
.limit(5)
.get())
.docs;
}
Future<List<DocumentSnapshot>> fetchNextList(String fromgst, String postType,
List<DocumentSnapshot> documentList) async {
return (await _posts
.where("fromgst", isEqualTo: fromgst)
.where("postType", isEqualTo: postType)
.orderBy("date", descending: true)
.startAfterDocument(documentList[documentList.length - 1])
.limit(5)
.get())
.docs;
}
}
separate class to handle pagination
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:growmore/features/home/repository/firebase_provider.dart';
import 'package:rxdart/rxdart.dart';
class PostListBloc {
List<DocumentSnapshot>? documentList;
bool showIndicator = false;
FirebaseProvider? firebaseProvider;
BehaviorSubject<List<DocumentSnapshot>>? postController;
BehaviorSubject<bool>? showIndicatorController;
PostListBloc() {
postController = BehaviorSubject<List<DocumentSnapshot>>();
showIndicatorController = BehaviorSubject<bool>();
firebaseProvider = FirebaseProvider(firestore: FirebaseFirestore.instance);
}
Stream get getShowIndicatorStream => showIndicatorController!.stream;
Stream<List<DocumentSnapshot>> get postStream => postController!.stream;
// This method will automatically fetch first 10 elements from the document list
Future fetchFirstList(String fromgst, String postType) async {
try {
documentList = await firebaseProvider?.fetchFirstList(fromgst, postType);
print("documentList$documentList");
postController?.sink.add(documentList!);
try {
if (documentList!.isEmpty) {
postController?.sink.addError("No Data Available");
}
} catch (e) {
print(e);
}
} on SocketException {
postController?.sink.addError(SocketException("No Internet Connection"));
} catch (e) {
print(e.toString());
postController?.sink.addError(e);
}
}
//This will automatically fetch the next 10 elements from the list
fetchNextPosts(String fromgst, String postType) async {
try {
updateIndicator(true);
List<DocumentSnapshot> newDocumentList = await firebaseProvider!
.fetchNextList(fromgst, postType, documentList!);
print('asca$newDocumentList');
documentList!.addAll(newDocumentList);
postController!.sink.add(documentList!);
try {
if (documentList!.isEmpty) {
postController!.sink.addError("No Data Available");
updateIndicator(false);
}
} catch (e) {
updateIndicator(false);
}
} on SocketException {
postController!.sink.addError(SocketException("No Internet Connection"));
updateIndicator(false);
} catch (e) {
updateIndicator(false);
print(e.toString());
postController!.sink.addError(e);
}
}
//For updating the indicator below every list and paginate*
updateIndicator(bool value) async {
showIndicator = value;
showIndicatorController!.sink.add(value);
}
void dispose() {
postController!.close();
showIndicatorController!.close();
}
}
the ui part
ScrollController controller = ScrollController();
#override
void initState() {
super.initState();
postListBloc = PostListBloc();
print("dvvfe${widget.fromgst}");
postListBloc!.fetchFirstList(widget.fromgst, widget.postType);
controller.addListener(_scrollListener);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<List<DocumentSnapshot>>(
stream: postListBloc!.postStream,
builder: (context, snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data?.length,
shrinkWrap: true,
controller: controller,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
title: Text(snapshot.data![index]['description']),
),
),
);
},
);
} else {
return const CircularProgressIndicator();
}
},
),
);
}
void _scrollListener() {
if (controller.offset >= controller.position.maxScrollExtent &&
!controller.position.outOfRange) {
print("Cavc$controller");
print("at the end of list");
postListBloc!.fetchNextPosts(widget.fromgst, widget.postType);
}
}
}
I found it not open source github repo

How can I update a DateTime?

I'm developing a realtime chat app with fisebase. The problem is that the time of a sent message does not update and I don't know what else can I do.
Here's some part of my code:
import 'package:flutter/material.dart';
import 'package:heyou/helper/constants.dart';
import 'package:heyou/screens/conversation_screen/send_menu_items.dart';
import 'package:heyou/services/database.dart';
import 'package:intl/intl.dart';
class ConversationScreen extends StatefulWidget {
final String chatScreenId;
ConversationScreen(this.chatScreenId);
#override
_ConversationScreenState createState() => _ConversationScreenState();
}
class _ConversationScreenState extends State<ConversationScreen> {
DateTime _currentDate = new DateTime.now();
DatabaseMethods databaseMethods = new DatabaseMethods();
TextEditingController messageController = new TextEditingController();
Stream chatMessageStream;
Widget chatMessageList() {
return StreamBuilder(
stream: chatMessageStream,
builder: (context, snapshot) {
return snapshot.hasData ? ListView.builder(
padding: EdgeInsets.only(bottom: 70.0),
itemCount: snapshot.data.documents.length,
reverse: true,
itemBuilder: (context, index) {
return MessageTile(
snapshot.data.documents[index].data['message'],
snapshot.data.documents[index].data['sendBy'] == Constants.myName,
snapshot.data.documents[index].data['time'],
snapshot.data.documents[index].data['messageTimeTile'],
);
}
) : Container();
},
);
}
sendMessage() {
if(messageController.text.isNotEmpty) {
Map<String, dynamic> messageMap = {
'message': messageController.text,
'sendBy': Constants.myName,
'time': DateTime.now().toString(),
'messageTimeTile': new DateFormat.Hms().format(_currentDate).toString(),
};
databaseMethods.addConversationMessages(widget.chatScreenId, messageMap);
messageController.text = '';
}
}
#override
void initState(){
databaseMethods.getConversationMessages(widget.chatScreenId).then((value) {
setState(() {
chatMessageStream = value;
});
});
super.initState();
}
}
And here's my database code:
import 'package:cloud_firestore/cloud_firestore.dart';
class DatabaseMethods {
getUserByUsername(String username) async {
return await Firestore.instance.collection('users').where('name', isEqualTo: username).getDocuments();
}
getUserByUserEmail(String userEmail) async {
return await Firestore.instance.collection('users').where('email', isEqualTo: userEmail).getDocuments();
}
uploadUserInfo(userMap) {
Firestore.instance.collection('users').add(userMap);
}
createChatScreen(String chatScreenId, chatScreenMap) {
Firestore.instance.collection('ChatScreen').document(chatScreenId).setData(chatScreenMap).catchError((e){
print(e.toString());
});
}
addConversationMessages(String chatScreenId, messageMap) {
Firestore.instance.collection('ChatScreen').document(chatScreenId).collection('chats').add(messageMap)
.catchError((e){
print(e.toString());
});
}
getConversationMessages(String chatScreenId) async {
return Firestore.instance.collection('ChatScreen').document(chatScreenId).collection('chats')
.orderBy('time', descending: true).snapshots();
}
getHomeScreen(String userName) async {
return Firestore.instance.collection('ChatScreen').where('users', arrayContains: userName).snapshots();
}
}
I'm trying to fix it by myself but I can't, that's why I'm here.
When you create _currentDate, it is never updated. Consequently, it will always be the time at which the State object was created. You can either use Midhun MP's suggestion in the comments, or replacing DateTime _currentDate = new DateTime.now(); with DateTime get _currentDate => DateTime.now(); to always get a copy of the current date, in case you use it in multiple places.

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();