Define number of Firestore documents in Snapshot.data.lenght flutter - firebase

I my app user are going to create by TextForm Field a collection into FIrestore and this collection has some documents.
Into my StreamBuilder I have set up stream and I can get the documents but I cant retrieve the Number of the documents created by the user logged with snapshot.data.lenght which I get the error:
Class 'DocumentSnapshot' has no instance getter 'lenght'.
Receiver: Instance of 'DocumentSnapshot'
Tried calling: lenght
The code:
class CollectData extends StatefulWidget {
#override
_CollectDataState createState() => _CollectDataState();
}
class _CollectDataState extends State<CollectData> {
final String phone;
final String wife;
final String location;
_CollectDataState({this.phone, this.wife, this.location,});
Stream<DocumentSnapshot> getDatabase() async* {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
yield* Firestore.instance
.collection('dataCollection')
.document(user.uid)
.snapshots();
}
#override
Widget build(BuildContext context,) {
return StreamBuilder(
stream: getDatabase(),
builder: (context, snapshot,) {
if (snapshot.data != null) {
return Column(
children: <Widget>[
Container(
height: 500,
child: ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.lenght,
itemBuilder: (BuildContext context, int) {
return Card(
color: Color(0xFF1f2032),
elevation: 15,
child: Container(
width: 60,
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Card(
color: Color(0xfffeaf0d),
child: Container(
height: 40,
width: 40,
child: Icon(
Icons.contacts,
color: Colors.white,
size: 25,
)),
),
Text(
snapshot.data['phone'],
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
],
),
),
);
},
),
),
],
);
} else
return NoData();
},
);
}
}

A lot of things have changed in the newest version of Flutter and Firebase.
Where you call the StreamBuilder you have to pass in the QuerySnapshot type like this:
return StreamBuilder<QuerySnapshot>(...
And change your itemCount line to:
itemCount: streamSnapshot.data!.docs.length,
That should solve the problem for you.

DocumentSnapshot doesn't have any length property because, when you are doing this:
Stream<DocumentSnapshot> getDatabase() async* {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
yield* Firestore.instance
.collection('dataCollection')
.document(user.uid)
.snapshots();
}
It means you are only retrieving 1 document, since each document id is unique then the above will give you only one document.
You can add here:
itemCount: 1
If you want a list of documents then you have to do the following:
Stream<QuerySnapshot> getDatabase() async* {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
yield* Firestore.instance
.collection('dataCollection')
.snapshots();
}
and then in itemCount:
itemCount: snapshot.data.documents.length

there seems to be typo in lenght, you meant length, right?
your error message clearly complaining about typo
Class 'DocumentSnapshot' has no instance getter 'lenght'.
plz just try replacing this line itemCount: snapshot.data.lenght, with itemCount: snapshot.data.length,

Related

Flutter Firebase return a Querysnapshot and DocumentSnapshot in the same widget

I've spent weeks trying to sort this issue and can't seem to sort it.
I have a database with two collections userTable and userMoods
I have a future builder which is returning the name, however I am querying the userMood table to return the last created document.
I cannot seem to find a way to get this data back out.
Picture of data I am trying to retrieve:
Code is as follows:
class CorrectMood extends StatefulWidget {
const CorrectMood({Key? key}) : super(key: key);
#override
_CorrectMoodState createState() => _CorrectMoodState();
}
class _CorrectMoodState extends State<CorrectMood> {
Future<DocumentSnapshot<Map<String, dynamic>>>? _fetchedData;
#override
void initState() {
super.initState();
_fetchedData = getData();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _fetchedData,
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(
title: const Text('Display the Picture'),
backgroundColor: kPrimaryColor,
),
// The image is stored as a file on the device. Use the `Image.file`
// constructor with the given path to display the image.
body: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 20.0, 8.0, 8.0),
child: Column(
children: [
Center(
child:
Text(
"${snapshot.data!.data()!["firstName"]} \n\n "
"We have predicted your mood as:\n\n "
//"${DatabaseService.getMood()}\n\n"
"Please select a reason associated to your mood",
style: const TextStyle(
color: Colors.black, fontSize: 15),
textAlign: TextAlign.center,
),
),
],
),
),
],
),
),
);
}else {
return CircularProgressIndicator();
}
},
);
}
}
Future<DocumentSnapshot<Map<String, dynamic>>> getData() async {
var currentUser = FirebaseAuth.instance.currentUser;
return await FirebaseFirestore.instance
.collection('USER_TABLE')
.doc(currentUser!.uid)
.get();
}
Future<QuerySnapshot<Map<String, dynamic>>> getMood() async {
var currentUser = FirebaseAuth.instance.currentUser;
return await FirebaseFirestore.instance
.collection('userMood')
.where('userId' == currentUser!.uid)
.orderBy('createdAt', descending: true)
.limit(1)
.get();
Any help is greatly appreciated!
you can use getData() directly to your FutureBuilder.
by the way I cannot where you are calling getMood() function.

Flutter app cannot write to the Firestore document created manually or by another user

In my flutter Firestore messaging app. Two users are creating a different collections with the same id.
I log in with an X account and message Y account. But when I log in with the Y account and try to see the messages sent by the X account, it creates a new document with the same chatRoomID.
Note:- For a very short span of time I can see the messages if I try after sending so many of them.
Here is what I can think of the problem.
Firstly, The rules of cloud Firestore might not be according to the need.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
Secondly, I think in my conversation.dart file, my stream builder might have the issue.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("ChatRoom")
.doc(widget.chatRoomID)
.collection("Messages")
.orderBy("Time")
.snapshots(),
builder: (context, snapshot) {
print('Snapshot had data - ${snapshot.hasData}');
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return Text(
snapshot.data.docs[index].get("Message"));
});
}),
Thirdly, In chatRoomScreen( where all the chats are supposed to be shown), when the user taps on a particular user in listTile.
createChatRoomAndStartConversation(snapshot.docs[i].get("Name"));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Conversation(
chatRoomID: getChatRoomID(
snapshot.docs[i].get("Name"), Constants.userName),
uid: snapshot.docs[i].id,
)));
},
and the createChatRoomFunction is
createChatRoomAndStartConversation(String username) {
DataBaseServices dbs = new DataBaseServices();
List<String> users = [username, Constants.userName];
users.sort();
print("Your username is ${Constants.userName}");
String chatRoomID = getChatRoomID(username, Constants.userName);
Map<String, dynamic> chatRoomMap = {
"ChatRoomID": chatRoomID,
"Users": users,
};
dbs.createChatRoom(chatRoomID, chatRoomMap);
print("Chat rooom creation successfull. ChatRoomID - $chatRoomID");
}
createChatRoom in databaseaServices class is,
createChatRoom(String chatRoomID, chatRoomMap) async {
return await FirebaseFirestore.instance
.collection("ChatRoom")
.doc(chatRoomID)
.set(chatRoomMap)
.catchError((e) {
print(e);
});
}
To make it reproducible...
If the user is logged in ChatRoomScreen will be shown to the user.
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.symmetric(horizontal: 24),
color: Colors.black.withRed(40),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
SizedBox(
height: 220,
),
Text(
"Messages",
style: TextStyle(
color: Colors.white,
fontSize: 25,
fontWeight: FontWeight.w600,
),
),
Spacer(),
GestureDetector(
onTap: () {
showSearch(
context: context, delegate: UserSearchDelegate());
},
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Color(0xff444446),
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.add,
color: Colors.white,
),
),
),
],
)
],
),
),
);
}
}
It would be a great help if you can figure out the issue for me. Thank you.

Flutter/ Firestore : Class 'QuerySnapshot' has no instance getter 'document'

I have app that I want to retreive data which are messages represented in uid document from Firestore database as explained here and these messages stored like so :
ChatRoom->chatRoomId->chat-> uid-> messages
but I receive this error :
The following NoSuchMethodError was thrown building StreamBuilder(dirty, state: _StreamBuilderBaseState<dynamic,
AsyncSnapshot>#56cb5): Class 'QuerySnapshot' has no instance
getter 'document'. Receiver: Instance of 'QuerySnapshot' Tried
calling: document
The relevant error-causing widget was: StreamBuilder
file:///Users/ahmedhussain/Downloads/khamsat/Client%20Apps/HPX-KSA/hpx_ksa/lib/Screens/messages.dart:21:12
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 _MessagesState.chatRoomList. (package:hpxksa/Screens/messages.dart:25:38)
Here is my code:
class _MessagesState extends State<Messages> {
Stream chatRoomsStream;
Widget chatRoomList(){
return StreamBuilder(
stream: chatRoomsStream,
builder: (context, snapshot){
return snapshot.hasData ? ListView.builder(
itemCount: snapshot.data.document.length,
itemBuilder: (context, index){
return ChatRoomTile(
username: snapshot.data.documents[index].data["chatRoomId"]
.toString().replaceAll("_", "").replaceAll(Constants.myName, "replace"),
chatRoomId:snapshot.data.documents[index].data["chatRoomId"]
);
}) : Container();
}
);
}
getUserInfogetChats() {
DatabaseService().getChatRooms(Constants.myName).then((value) {
setState(() {
chatRoomsStream = value;
});
});
}
#override
void initState() {
getUserInfogetChats();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: chatRoomList(),
);
}
}
class ChatRoomTile extends StatelessWidget {
final String username;
final String chatRoomId;
ChatRoomTile({this.username, this.chatRoomId});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context)=>Conversation(chatRoomId: chatRoomId,)));
},
child: Container(
color: Colors.black26,
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
children: <Widget>[
Container(
height: 40,
width: 40,
alignment: Alignment.center,
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius: BorderRadius.circular(40),
),
child: Text("${username.substring(0,1).toUpperCase()}"),
),
SizedBox(width: 8,),
Text(username),
],
),
),
);
}
}
Here is my get function to retreive chats that contains user name:
getChatRooms(String username)async{
return await Firestore.instance.collection("ChatRoom").
where("users", arrayContains: username).
snapshots();
}
The error that you received is quite clear about what the issue is. QuerySnapshot doesn't have a document property. You likely intended to use the documents property, which is more consistent with your attempt to use a ListView.
Changing instances of snapshot.data.document to snapshot.data.documents will solve this particular issue.
return StreamBuilder(
stream: chatRoomStream,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return ChatRoomTile(
**snapshot.data.docs[index].data()['chatRoomId']**);
},
)
: Container();
},
);

How to fix issue with firestore collection snapshot

I'm trying to order my messages collection from Firebase from newest to oldest. But my messages display randomly once after the last and once before the last.
Despite my final who order my collection by reverse
I've tried to find any subject to orders by realtime but I can't find anything about this on Flutter and Firestore. (Maybe I'm blind or I'm not looking well)
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _fireStore.collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
final messageSender = message.data['sender'];
final messageText = message.data['text'];
final currentUser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: messageSender,
text: messageText,
isMe: currentUser == messageSender,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
children: messageBubbles,
),
);
},
);
}
}
I add my data into this widget :
FlatButton(
onPressed: () {
messageTextController.clear();
_fireStore.collection('messages').add(
{'text': messageText, 'sender': loggedInUser.email});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
while adding data you can add a timestamp /server timestamp to your message doc and then use it to arrange your data example "timestamp": Timestamp.now(), then query your data .orderby('timestamp' ,descending :true)

Gridview.builder with Firebase realtime database and futurebuilder

Coming from Firestore, I am a little bit struggling how to receive data from Firebase real time database. I just want a nice grid view of images which are loaded from the realtime database.
Error: flutter: The following NoSuchMethodError was thrown building:
flutter: Class 'DataSnapshot' has no instance method '[]'.
flutter: Receiver: Instance of 'DataSnapshot'
I guess it's index related. No idea how to correctly map it within a list.
import 'package:cached_network_image/cached_network_image.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(16.0),
child: new FutureBuilder(
future: FirebaseDatabase.instance
.reference()
.child('messages')
.child('1551276762582')
.orderByChild('messagetype')
.equalTo('1')
.once(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data != null) {
return new Column(
children: <Widget>[
new Expanded(
child: new GridView.builder(
// itemCount: item.length,
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
itemBuilder: (context, index) {
return GridTile(
child: CachedNetworkImage(
imageUrl: snapshot.data[index]['imageUrl']
.toString()));
},
),
)
],
);
} else {
return new CircularProgressIndicator();
}
} else {
return new CircularProgressIndicator();
}
}));
}
}
I could solve it with the following code. Again, I have to say that the Firebase documentation really lacks, which is quite disappointing, since Firebase is a great tool. Moreover, I do not understand, that there is no documentation on 'How to use Firebase with Flutter' (we are talking about both Google products.) Notwithstanding, here is the working code for anyone, who likes to use Streambuilder with Gridview.builder with the Realtime Database in Flutter:
StreamBuilder(
stream: FirebaseDatabase.instance
.reference()
.child('messages')
.child(groupId)
.orderByChild('messagetype')
.equalTo(1)
.onValue,
builder: (BuildContext context, AsyncSnapshot<Event> snapshot) {
if (snapshot.hasData) {
if (snapshot.data.snapshot.value != null) {
Map<dynamic, dynamic> map = snapshot.data.snapshot.value;
List<dynamic> list = map.values.toList()
..sort(
(a, b) => b['timestamp'].compareTo(a['timestamp']));
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemCount: list.length,
padding: EdgeInsets.all(2.0),
itemBuilder: (BuildContext context, int index) {
return Container(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(
imageUrl: list[index]["imageUrl"])),
);
},
child: CachedNetworkImage(
imageUrl: list[index]["imageUrl"],
fit: BoxFit.cover,
),
),
padding: EdgeInsets.all(2.0),
);
},
);
} else {
return Container(
child: Center(
child: Text(
'Es wurden noch keine Fotos im Chat gepostet.',
style: TextStyle(fontSize: 20.0, color: Colors.grey),
textAlign: TextAlign.center,
)));
}
} else {
return CircularProgressIndicator();
}
})),
Something that I do that helps me solve issues, is by explicitly turning snapshots into Maps the following way.
Map yourModel = Map.from(datasnapshot);
also many times when handling null data or so on I have to turn the asyncSnap value that comes from the future Builder into a Datasnapshot from firebase in the following way
Datasnapshot snap = asyncSnap.data;
then handle for snap being null
if(snap.value!=null){}
Hope this helps! if you need help send me a message

Resources