How do I display text from firestore data in flutter? - firebase

I am new to firebase/firestore and wanted to add firestore to my app. My app currently has a login and adds data to the database with the UID set as the document name. Console Image
I want to display the name in my apps profile page. How would I achieve this?
Called it with this
Center(child:building(context),),
Widget building(BuildContext context) {
return new StreamBuilder(
stream: Firestore.instance
.collection('UserData')
.document(getUID())
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new Text("Loading");
} else {
return new Text(snapshot.data.toString());
}
});
}
Current Error
Error Image
Previous Error
Error Message
Thanks in advance!

Try this
Widget building(BuildContext context) {
return new StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection('UserData')
.document('TjMJDFd940UtLORgdYND771GYwG2')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return new Text("Loading");
} else {
Map<String, dynamic> documentFields = snapshot.data.data;
return Text(documentFields["First Name"] +
" " +
documentFields["Last Name"]);
}
});
}
Note that TjMJDFd940UtLORgdYND771GYwG2 refer to documentID.

The docs for the Flutter Firebase API are stashed away and are admittedly hard to find. Here's the documentation on the QuerySnapshot class.
You are trying to look at the .document property of a QuerySnapshot object, so it's throwing an error because that property does not exist. Try using snapshot.documents to get a list of documents to iterate over (or just use snapshot.documents[0] if there will always only be one), and then read the data from that, i.e.: snapshot.documents[0].data[documentId]['First Name']. I removed quotes from documentId reference, since we want to index the variable value and not just the string "documentId".

Related

Flutter Firestore: FirestoreBuilder with initial data

I'm making my first Flutter app and I encounter a problem and doesn't found any solution for it.
I have a view where I render a Firestore document, and there is two ways of getting there:
From a list where I already loaded my documents
From Dynamic Links with uid attached as arguments (args)
So in order to listen document changes and loading the data when arriving from the link I used FirestoreBuilder like this:
return FirestoreBuilder<EventDocumentSnapshot>(
ref: eventsRef.doc(args.uid),
builder: (context, AsyncSnapshot<EventDocumentSnapshot> snapshot, Widget? child) {
if (!snapshot.hasData) {
return Container();
}
Event? event = snapshot.requireData.data;
return Scafold(); //Rest of my rendering code
}
);
How I could avoid first call to Firebase when I already have the data but still listen to changes? The main problem is that my hero animation doesn't work because of this.
I tried with a StreamBuilder and initialDataparam but since it's expecting stream I didn't know how to cast my data.
Okay, so I found the solution myself after many tries, so I added my Model object that can be null as initialData, but the thing that makes me struggle with is how you get the data in the builder. You have to call different methods depending on where the data is coming from.
return StreamBuilder(
initialData: args.event
ref: eventsRef.doc(args.uid),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
// Here is the trick, when data is coming from initialData you only
// need to call requireData to get your Model
Event event = snapshot.requireData is EventDocumentSnapshot ? snapshot.requireData.data : snapshot.requireData;
return Scafold(); //Rest of my rendering code
}
);
Reading through cloud_firestore's documentation you can see that a Stream from a Query can be obtained via snapshots()
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('books').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['title']),
subtitle: new Text(document['author']),
);
}).toList(),
);
},
);
This won't help you, but with GetX it's simple to implement like this: You don't need StreamBuilder anymore.
//GetXcontroller
class pageController extends GetXcontroller {
...
RxList<EventModel> events = RxList<EventModel>([]);
Stream<List<EventModel>> eventStream(User? firebaseUser) =>
FirebaseFirestore.instance
.collection('events')
.snapshots()
.map((query) =>
query.docs.map((item) => UserModel.fromMap(item)).toList());
#override
void onReady() async {
super.onReady();
events.bindStream(
eventStream(controller.firebaseUser)); // subscribe any change of events collection
}
#override
onClose() {
super.onClose();
events.close(); //close rxObject to stop stream
}
...
}
You can use document snapshots on StreamBuilder.stream. You might want to abstract the call to firebase and map it to an entity you defined.
MyEntity fromSnapshot(DocumentSnapshot<Map<String, dynamic>> snap) {
final data = snap.data()!;
return MyEntity (
id: snap.id,
name: data['name'],
);
}
Stream<MyEntity> streamEntity(String uid) {
return firebaseCollection
.doc(uid)
.snapshots()
.map((snapshot) => fromSnapshot(snapshot));
}
return StreamBuilder<MyEntity>(
// you can use firebaseCollection.doc(uid).snapshots() directly
stream: streamEntity(args.uid),
builder: (context, snapshot) {
if (snapshot.hasData) {
// do something with snapshot.data
return Scaffold(...);
} else {
// e.g. return progress indicator if there is no data
return Center(child: CircularProgressIndicator());
}
},
);
For more complex data models you might want to look at simple state management or patterns such as BLoC.

How to get specific document data from firebase and return as a string

I am trying to get a data in firestore like this:
am trying to get FCMToken from a user document
And be able to immediately use it as a string inside a streambuilder
FirebaseFirestore.instance.collection("usersToken")
.doc(snapshot.data.docs[index].data()['userId'].toString())
.get();
//I don't know what to do from here on to be able to get it as a string
How do I do it?
It works for me
Widget build(BuildContext context) {
return new StreamBuilder(
stream: Firestore.instance.collection('COLLECTION_NAME').document('TEST').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
var userDocument = snapshot.data;
return new Text(userDocument["property_name"]);
}
);
}

Error getting data from subcollection of flutter firebase

In a flutter messaging app, chatrooms are created and on the conversation screen, I can access the subcollection of the messages. But when the same subcollection I am trying to access on the main page
(where existing chats are shown) I cannot access them.
I have a collection of ChatRooms, in which users as an array are stored. Then, Messages named subcollection stores the messages.
See, the document is named lexicographically attached with and in between. Further, it has Messages collection.
And messages collection is also not empty.
On the main page, the existing chats are shown in listTile. I want to show the last message in its subtitle.
So, here is the last message stateful widget.
class _LastMessageState extends State<LastMessage> {
#override
Widget build(BuildContext context) {
return FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection("ChatRooms")
.doc(widget.chatRoomID)
.collection("Messages")
.orderBy("Time")
.snapshots()
.last,
builder: (context, documentSnapshot) {
return Text(documentSnapshot.data.docs.last.get("Message"));
});
}
}
Always the bad state error is coming up.
I would be glad if you could figure out the problem.
Edit :
This is my firestore rules.
You should use the limit() method, in order to get a QuerySnapshot with only one document. Then you can do as follows, knowing the first (and unique) element of the docs list is the document you are looking for:
return FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance.
.collection("ChatRooms")
.doc(widget.chatRoomID)
.collection("Messages")
.orderBy("Time", descending: true)
.limit(1)
.get(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text("...");
}
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data.size > 0) {
return Text(snapshot.data.docs[0].get("Message"));
} else {
return Text("No document");
}
}
return Text("Loading");
},
);

Firebase in flutter getCurrentUser never unwraps the Future and I can't get at the Data

So I have a Flutter app with Firebase authentication. I have a dart file for sign in and registration and then a separate dart file with different Widgets for a list of data. I want to get the current user so I can get that users personalized data from the database. But for some reason I can never get that user. Here is the code:
Future getUser() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user.getIdToken();
}
return FutureBuilder(
future: getUser(),
builder: (context, snapshot) {
if (snapshot.hasData) return Text(snapshot.data);
else if (snapshot.hasError) return Text("data error (see futurebuilder)");
return Text("Await for data");
},
);
But on my debugging device the Text output just says data error (see future builder) which is the error message I wrote. However I can't figure out what I'm doing wrong. I don't see why this isn't just giving me the user ID token. Thanks.
Try changing your code from snapshot.hasData to snapshot.connectionState == ConnectionState.done and see if this helps you. I have the exact same setup in my app and this works for me.
something like this should work
FutureBuilder(
future: getUser(),
builder: (context, snapshot) {
if(snapshot.connectionState == ConnectionState.done){
return
//Execute code after future is returned
} else {
return CircularProgressIndicator();
}
},
);

Flutter Future:A build function returned null

I want to retrieve data from firebase to future builder.I use this function for that.
Future getAllData() async {
ReseviorDataModel resModel;
DatabaseReference ref = FirebaseDatabase.instance.reference();
DataSnapshot childed =await
ref.child("Reservoir/${widget.placeName}").once();
Map<dynamic, dynamic> values;
values = childed.value;
resModel = ReseviorDataModel.customConstrcutor(values["sensor1"],
values["sensor2"], values["sensor3"]);
return resModel;
}
I invoke this function inside my future builder.
child: FutureBuilder(
future: getAllData(),
builder: (context, snapshot) {
Center(child: Text(snapshot.data));
}
but it keeps throwing this "A build function returned null." error .i cant figure out what is the problem here
To clear things up here, your builder always has to return a widget to display. You can never return null, as the error message describes.
The problem in this case was that the OP didn't return anything from the builder, so just adding a return worked out fine.
Some things to keep in mind when using FutureBuilder. Always check the snapshot.hasData property and return a UI accordingly. This will prevent cases where the snapshot.data is null causing a new error to be thrown in your Widget taking in the null.
Example:
child: FutureBuilder(
future: getAllData(),
builder: (context, snapshot) {
if(!snapshot.hasData) {
// show loading while waiting for real data
return CircularProgressIndicator();
}
return Center(child: Text(snapshot.data));
}

Resources