I am trying to develop a flutter app. This is a simple chat app with some webview. After create all backend and in firebase news as well as new users show up. in the app, messages won't show up in chat. i have that error when i try to go to chat room.
NoSuchMethodError (NoSuchMethodError: The getter 'docs' was called on null.
Receiver: null
Tried calling: docs)
this is my code
class _ConversationScreenState extends State<ConversationScreen> {
DatabaseMethods databaseMethods = new DatabaseMethods();
TextEditingController messageController = new TextEditingController();
Stream chatMessageStream;
Widget ChatMessageList() {
return StreamBuilder(
stream: chatMessageStream,
builder: (
context,
snapshot,
) {
return ListView.builder(
itemCount: snapshot.data.docs.lenght, //ERROR
itemBuilder: (context, index) {
return MessageTile(snapshot.data.docs[index].data["message"]); //ERROR
});
},
);
}
There is a chance that the snapshot being returned might be null if the stream hasn't yet emitted something. Use an if checker to check if the snapshot is null.
if(snapshot != null && snapshot.hasData){
return ListView.builder(
itemCount: snapshot.data.docs.length, //ERROR
itemBuilder: (context, index) {
return MessageTile(snapshot.data.docs[index].data["message"]); //ERROR
});
}else {
return Container();
}
Related
I am implementing the project by referring to the sample code before firestore 2.0.
I'm going to bring the last document of the Chat collector to StreamBuilder.
Next is my StreamBuilder code.
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection(COL_USERS)
.doc(_myPerson!.userKey)
.collection(COL_CHATROOMS)
.doc(personKey)
.collection(COL_CHATS)
.snapshots(includeMetadataChanges: true),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
// error check
if (snapshot.hasError) {
return SizedBox();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return SizedBox();
}
if (snapshot.data == null) {
return SizedBox();
}
// chat document list
List<QueryDocumentSnapshot> listChat = snapshot.data!.docs;
// last chat document // ! error
QueryDocumentSnapshot lastChat = listChat
// edit .data()[] => .get()
.where((element) => element.get(DOC_LASTMSG_TIME) == lastMsgTime)
.toList()[0];
NewChatModel lastDataChat =
NewChatModel.fromJson(lastChat.data() as Map<String, dynamic>);
return Text("123");
);
However, the following error is printed from the code below:
QueryDocumentSnapshot lastChat = listChat
.where((element) => element.get(DOC_LASTMSG_TIME) == lastMsgTime)
.toList()[0];
NewChatModel lastDataChat =
NewChatModel.fromJson(lastChat.data() as Map<String, dynamic>);
Error
════════ Exception caught by widgets library ═══════════════════════════════════
The following StateError was thrown building StreamBuilder<QuerySnapshot<Object?>>(dirty, state: _StreamBuilderBaseState<QuerySnapshot<Object?>, AsyncSnapshot<QuerySnapshot<Object?>>>#cc2a2):
Bad state: field does not exist within the DocumentSnapshotPlatform
The relevant error-causing widget was
StreamBuilder<QuerySnapshot<Object?>>
When the exception was thrown, this was the stack
Can I know how to solve this problem?
I want to get a string from my DB in Firebase, I'm very confused and I don't know how to do that!
I made a big search in the few past days about this idea but unf I don't get any useful result
what do I want? I want to make a Method that returns the 'Question' string.
DB:Collection / History/question
thank you for your time
the incorrect code :
Future loadData() async {
await Firebase.initializeApp();
if (snapshot.hasError) {
return Scaffold(
body: Center(
child: Text("Error: ${snapshot.error}"),
),
);
}
// Collection Data ready to display
if (snapshot.connectionState == ConnectionState.done) {
// Display the data inside a list view
return snapshot.data.docs.map(
(document) {
return method(
document.data()['question'].toString().toString(),
); //Center(
},
);
}
}
Here is the official documentation from Flutter Fire - https://firebase.flutter.dev/docs/firestore/usage/
Read data from Cloud firestore
Cloud Firestore gives you the ability to read the value of a collection or a document. This can be a one-time read or provided by real-time updates when the data within a query changes.
One-time Read
To read a collection or document once, call the Query.get or DocumentReference.get methods. In the below example a FutureBuilder is used to help manage the state of the request:
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data.data();
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}
To learn more about reading data whilst offline, view the Access Data Offline documentation.
Realtime changes
FlutterFire provides support for dealing with real-time changes to collections and documents. A new event is provided on the initial request, and any subsequent changes to collection/document whenever a change occurs (modification, deleted, or added).
Both the CollectionReference & DocumentReference provide a snapshots() method which returns a Stream:
Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
Stream documentStream = FirebaseFirestore.instance.collection('users').doc('ABC123').snapshots();
Once returned, you can subscribe to updates via the listen() method. The below example uses a StreamBuilder which helps automatically manage the streams state and disposal of the stream when it's no longer used within your app:
class UserInformation extends StatefulWidget {
#override
_UserInformationState createState() => _UserInformationState();
}
class _UserInformationState extends State<UserInformation> {
final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance.collection('users').snapshots();
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document.data()['full_name']),
subtitle: new Text(document.data()['company']),
);
}).toList(),
);
},
);
}
}
By default, listeners do not update if there is a change that only affects the metadata. If you want to receive events when the document or query metadata changes, you can pass includeMetadataChanges to the snapshots method:
FirebaseFirestore.instance
.collection('users')
.snapshots(includeMetadataChanges: true)
When I want to get data from Firebase using Streambuilder and I want the image and some text from firebase firestone
#override
Widget build(BuildContext context) {
return StreamBuilder (
stream: FirebaseFirestore.instance.collection("products").snapshots(),
builder: (_, snapshot){
if(!snapshot.hasData){
return Center(
child:Text("Loading"),);
}else{
return GridView.builder(
itemCount: snapshot.data.docs.length,
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext contex, int index) {
DocumentSnapshot data = snapshot.data.docs[index];
return
Single_prod (
product_name: data['name'],
prod_pictures: data['imageurl'],
prod_oldprice: data['old_price'],
prod_price: data["price"],
);
},
);
}
}
);
When it Calling Single_prod class it happened in this class image and text are shown
Single_prod class:
class Single_prod extends StatefulWidget {
final product_name;
final prod_pictures;
//final prod_oldprice;
final prod_price;
const Single_prod(
{Key key,
this.product_name,
this.prod_pictures,
this.prod_oldprice,
this.prod_price});
#override
_Single_prodState createState() => _Single_prodState();
}
I get this error:
Type 'List' is not a subtype of type 'String'
The relevant error-causing widget was
Try using product_name: data.data()['name'] , same thing for the rest of your product items. Somewhere also in your returned data, you are getting a List, and trying to assign it to a string. Which element in your document snapshot in firestore is a List object?
For pictures, try this prod_pictures: data.data()['imageurl'][0].
I am new to flutter and I am trying to integrate a firebase backend to store my data. I am trying to establish a stream using firebase but when I try to create a listview with the stream I get the following message:
The method 'collection' was called on null.
Receiver: null
Tried calling: collection("betslips")
Here is my code:
class Database {
final FirebaseFirestore firestore;
Database(this.firestore);
Stream<List<BetSlipModel>> streamBetSlip({String uid}) {
try {
print(firestore.collection("betslips"));
return firestore
.collection("betslips")
.snapshots()
.map((query) {
List<BetSlipModel> retVal;
for(final DocumentSnapshot doc in query.docs) {
retVal.add(BetSlipModel.fromDocumentSnapshot(documentSnapshot: doc));
}
return retVal;
});
} catch(e) {
rethrow;
}
}
}
I then try and access the values here:
body: Expanded(
child: StreamBuilder(
stream: Database(widget.firestore)
.streamBetSlip(uid: widget.auth.currentUser.uid),
builder: (BuildContext context,
AsyncSnapshot<List<BetSlipModel>> snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.data.isEmpty) {
return const Center(
child: Text("Empty"),
);
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return BetSlipCard(
firestore: widget.firestore,
uid: widget.auth.currentUser.uid,
betslip: snapshot.data[index],
);
},
);
} else {
return const Center(
child: Text("loading..."),
);
}
},
),
),
Any ideas? Thanks
The method 'collection' was called on null.
Receiver: null
Tried calling: collection("betslips")
means that firestore variable is not referencing anything, check the below on how to solve it:
You are creating an instance of the class here:
stream: Database(widget.firestore)
widget is an instance variable of the class State, therefore inside the State class initialize firestore:
final FirebaseFirestore firestore = FirebaseFirestore.instance;
Using similar code as flutter's firestore example, suppose there is a reference field stored in a snapshot document, called: document['userRef'].
First of all, how do I access the data of userRef? Using document['userRef'].get().data or document['userRef'].get().username I wasn't able to access the data. (NoSuchMethodError: Class 'Future<DocumentSnapshot>' has no instance getter 'data')
I also tried using document['userRef'].get().then(...) but getting the error: type 'Future<dynamic>' is not a subtype of type 'String'
Even if .then would work, wouldn't it then look up the same reference again for each message? Here the database is updated in realtime, but it's unnecessary to make the same lookup for multiple messages in the ListView.
class MessageList extends StatelessWidget {
MessageList({this.firestore});
final Firestore firestore;
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: firestore.collection('messages').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
final int messageCount = snapshot.data.documents.length;
return ListView.builder(
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document = snapshot.data.documents[index];
// document['userRef'] exists here
return ListTile(
title: Text(document['message'] ?? '<No message retrieved>'),
subtitle: Text('Message ${index + 1} of $messageCount'),
);
},
);
},
);
}
}
Edit:
I was able to fetch the nested data using FutureBuilder, though not sure how efficient it is. (Wouldn't this possibly send loads of redundant requests to Firebase?)
Creating a widget for the nested data, where document['userRef'] exists:
FutureBuilder(
future: userData(document['userRef']),
builder: (BuildContext context,
AsyncSnapshot<dynamic> uData) {
return Text(uData.data['username']);
},
);
And the userData function looks like this:
Future<dynamic> userData(DocumentReference user) async {
DocumentSnapshot userRef = await user.get();
return userRef.data;
}
Sticking to the Firebase and Flutter way, it is possible to use a Streambuilder inside a Streambuilder. That is, instead of using a FutureBuilder for the nested data, which makes you wait for each .get request.
(The code is untested, but the principle is tested.)
class MessageList extends StatelessWidget {
MessageList({this.firestore});
final Firestore firestore;
#override
Widget build(BuildContext context) {
Map UserSnapshot = Map(); // create a variable for accessing users by id
return StreamBuilder<QuerySnapshot>(
stream: firestore.collection('users').snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> UsersSnapshot) {
// process usersnapshot from list to map
UsersSnapshot.data.documents.forEach((userRecord) {
//print(optionRecord.documentID); // debug
UserSnapshot[userRecord.documentID] = userRecord;
});
// user data can be accessed as soon as there is a reference field or documentID:
// UserSnapshot[document['userRef']]['userName'}
return StreamBuilder<QuerySnapshot>(
stream: firestore.collection('messages').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> MessagesSnapshot) {
if (!MessagesSnapshot.hasData) return const Text('Loading...');
final int messageCount = MessagesSnapshot.data.documents.length;
return ListView.builder(
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document =
MessagesSnapshot.data.documents[index];
// document['userRef'] exists here
// UserSnapshot[document['userRef']]['userName'} is accessible here
return ListTile(
title:
Text(document['message'] ?? '<No message retrieved>'),
subtitle: Text('Message ${index + 1} of $messageCount'),
);
},
);
},
);
});
}
}