Futurebuilder snapshot has no data - firebase

CollectionReference users = FirebaseFirestore.instance.collection('Users');
FirebaseAuth auth = FirebaseAuth.instance;
String uid = FirebaseAuth.instance.currentUser!.uid.toString();
var userData;
var dbFuture;
#override
void initState() {
dbFuture = getData();
super.initState();
}
Future getData() async {
final String uid = FirebaseAuth.instance.currentUser!.uid.toString();
final DocumentSnapshot doc = await users.doc(uid).get();
users.doc(uid).get().then((DocumentSnapshot doc) {
userData = doc.data();
print(doc.data());
});
}
#override
Widget build(BuildContext context) => Scaffold(
body: FutureBuilder(
future: dbFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Container(
child: Text('waiting'),
);
}
if (!snapshot.hasData) {
return Container(
child: Text('error'),
);
}
final data = snapshot.data;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(userData['displayName']),
ElevatedButton(
onPressed: FirebaseAuth.instance.signOut,
child: Text("Log out"))
],
),
);
}),
);
I'm new in Flutter and trying to make an application for managing an academy.
I successfully saved the data at Firestore Cloud, and I can read them with
print(doc.data());
Now I want to build Profile page with those data, so I used Futurebuilder.
But snapshot always has no data.
I read documents as well, but still have no idea.

To get your data from Firebase and display them in your widgets, you have two ways, but you have to choose only one according to your needs.
With FutureBuilder()
This code will call your database and load the info you request at each build and at each setState() (responsible for updating your interface content). It could be useful for some data types, but in your case your redundant Firebase calls could cost you.
CollectionReference users = FirebaseFirestore.instance.collection('Users');
final auth = FirebaseAuth.instance;
late final uid = auth.currentUser!.uid;
#override
Widget build(BuildContext context) => Scaffold(
body: FutureBuilder(
future: users.doc(uid).get(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Container(
child: Text('waiting'),
);
}
if (!snapshot.hasData) {
return Container(
child: Text('error'),
);
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(snapshot.data!['some_data']),
Text(auth.currentUser?.displayName ?? 'user have no name'),
ElevatedButton(
onPressed: auth.signOut,
child: Text("Log out"))
],
),
);
}),
);
In the initState()
When using initState(), the code inside is called only once. To refresh the content, you will have to call getData() manually (in a setState() for example)
CollectionReference users = FirebaseFirestore.instance.collection('Users');
final auth = FirebaseAuth.instance;
late final uid = auth.currentUser!.uid;
String? someData;
#override
void initState() {
getData();
super.initState();
}
Future<void> getData() async {
users.doc(uid).get().then((doc) {
someData = doc['some_data'];
});
}
#override
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(someData ?? 'no data'),
Text(auth.currentUser?.displayName ?? 'user have no name'),
ElevatedButton(
onPressed: auth.signOut,
child: Text("Log out"))
],
),
),
);
Finally, if your users authenticate, you can use auth.currentUser?.displayName and auth.currentUser!.updateDisplayName('new name') to simply get and change your users' names.

Related

'Future is not a subtype of type 'Widget' - error only present when using Future.wait

I have a string error which I can't seem to debug I think its related to how I'm trying to retrieve data when using Future.wait I get the above error. If I only build a single future and don't use Future.wait and retrieve data using snapshot.data!.data()!["prediction"] I don't get any errors.
Code below for my future.wait
Any help appreciated!
class CorrectMood extends StatefulWidget {
const CorrectMood({Key? key}) : super(key: key);
#override
_CorrectMoodState createState() => _CorrectMoodState();
}
class _CorrectMoodState extends State<CorrectMood> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.wait([getData(), getLatestMood()]),
builder: (context, AsyncSnapshot<List> snapshot) {
snapshot.data![0]; //getLatestMood
snapshot.data![1]; //getData
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![0]["firstName"]}"
// "${snapshot.data![0].prediction}"
//"${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,
),
),
countDocuments(),
],
),
),
],
),
),
);
}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();
}
countDocuments() async {
var currentUser = FirebaseAuth.instance.currentUser;
QuerySnapshot _myDoc = await FirebaseFirestore.instance
.collection('userMoods')
.doc('useerID')
.collection(currentUser!.uid)
.get();
List<DocumentSnapshot> _myDocCount = _myDoc.docs;
return (_myDocCount.length);
}
Future<DocumentSnapshot<Map<String, dynamic>>> getLatestMood() async {
var currentUser = FirebaseAuth.instance.currentUser;
var latestMoodDoc = countDocuments();
return await FirebaseFirestore.instance
.collection('userMoods')
.doc('useerID') // make this the userID
.collection('UbcNaFtJwXWoId9J5RLuVBVPhpN2') // make this increment every time
.doc('2') //this can be system generated name don't care about it
.get();
}

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.

how to get document id Firestore

I am new to Firestore. I am stuck at getting the document ID from Firestore.
Here I create the user with name and age parameters.
When I click Submit, it submits to Cloud Firestore. Everything is Ok up to now.
class _MyHomePageState extends State<MyHomePage> {
late final TextEditingController name = TextEditingController();
late final TextEditingController age = TextEditingController();
final DBHelper _helper = DBHelper();
late final User _user=User();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: name,
decoration: const InputDecoration(hintText: "Name"),
),
TextField(
controller: age,
decoration: const InputDecoration(hintText: "Age"),
),
ElevatedButton(onPressed:(){
_user.setAge(age.text);
_user.setName(name.text);
print("Name: ${_user.name}");
_helper.addUser(_user);
Navigator.push(context, MaterialPageRoute(builder: (builder)=>MainPage( ,))); //here i need to push the document id
}, child: const Text("Submit"))
],
),
),
);
}
This is my custom User model
class User{
late final String userId;
final String name;
final String age;
User({required this.name, required this.age});
}
This is my service class for Firestore methods
class DBHelper{
// final User user = User();
final CollectionReference _reference= FirebaseFirestore.instance.collection("users"); // database path
//Adding user to Firestore
Future<void> addUser(User user){
return _reference.add({
'name':user.name,
'age':user.age,
}).then((value) => print("User added"))
.catchError((onError)=> print("Failed to add the user: $onError"));
}
}
Here is what I want: When I click on the submit button in the previous page. It will navigate to the following page. In this page, I want to show the "name" and "age" information. The only thing that is missing is the documentId
return FutureBuilder<DocumentSnapshot>(
future: _ref.doc(documentId).get(), //which document doesn't know
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text(
"Bir hata oluştu.",
textScaleFactor: 3,
));
} else if (snapshot.hasData && !snapshot.data!.exists) {
return const Center(
child: Text(
"Döküman yok",
textScaleFactor: 3,
));
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Name: ${data['name']}",
textScaleFactor: 3,
),
Text(
"Age:${data['age']}",
textScaleFactor: 3,
),
],
)),
);
} else {
return const Center(child: CircularProgressIndicator());
}
});
you can get the id on the metodh you used to submit infos
instead of return void,return a String(the id) then go to the next screen
like that:
Future<String> addUser(User user)async {
return await _reference.add({
'name':user.name,
'age':user.age,
}).then((value) => var documentId=value.id)
and the button function should be:
ElevatedButton(onPressed:(){
_user.setAge(age.text);
_user.setName(name.text);
print("Name: ${_user.name}");
var id=await _helper.addUser(_user);
Navigator.push(Get.context!, MaterialPageRoute(builder:(builder)=>MainPage(documentI: id)));
}, child: const Text("Submit"))
],
),

flutter : not able to display image from firebase firestore

I am trying to get array of images to be displayed on container widget which is stored in firebase firestore. The url is stored with index in firestore and cloud storage. Here is the complete code below. I followed the code provided online, I am not sure what I am missing or where I went wrong. pls help me out.Thanks in advance.
class NewImage extends StatefulWidget{
List<String> urls;
int currentIndex;
VoidCallback onImageTap;
NewImage({Key key,#required this.urls,#required this.currentIndex,#required this.onImageTap})
:super(key:key);
#override
_NewImage createState() => _NewImage();
}
class _NewImage extends State<NewImage>
with SingleTickerProviderStateMixin{
List<String> images = [];
final auth = FirebaseAuth.instance;
final FirebaseFirestore fb = FirebaseFirestore.instance;
#override
void initState() {
super.initState();
getImages();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SafeArea(
child: Column(
children: [
Container(
height: 160.0,
padding: EdgeInsets.symmetric(vertical: 15.0,horizontal: 15.0),
child: FutureBuilder(
future: getImages(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(8.0),
leading: Image.network(
snapshot.data.docs[index].data()["images"],
fit: BoxFit.fill),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
),
],
),
),
),
);
}
Future<QuerySnapshot> getImages() async {
return FirebaseFirestore.instance.collection("users").doc(
auth.currentUser.uid).collection("images").get();
}
}
you are passing array to networkImage instead of image
change it like this
Image.network(
snapshot.data.docs[index].data()["images"][index],// [index] added
fit: BoxFit.fill),

Flutter/Firebase - Error in fetching currently logged in user data

I want to fetch currently logged in user data. There is a field in fire_store 'useremail'. When a user logs in, I get his ID and using 'where class' I fetch the animal's data against his ID shown below:
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: _firebaseUser.email.toString()).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Although it is working but it shows error on as well. I need some help to fix this issue or an alternate suggestion for this (Any suggestion or kind help would be highly appreciated):
════════ Exception caught by widgets library
══════════════════════
NoSuchMethodError was thrown building UserBookingHistoryModel(dirty,
state: _UserBookingHistoryModelState#2d8c2):
The getter 'email' was called on null.
Receiver: null
Tried calling: email
Probably the problem is caused by this snippet in Firebase Auth:
void initState() {
super.initState();
widget.auth.getCurrentUser().then((firebaseUserId) {
setState(() {
authStatus = firebaseUserId == null
? AuthStatus.notSignedIn
: AuthStatus.signedIn;
});
});
}
The full code of bookings.dart is here:
class _UserBookingHistoryModelState extends State<UserBookingHistoryModel> {
FirebaseAuth _auth;
FirebaseUser _firebaseUser;
#override
void initState() {
super.initState();
_auth = FirebaseAuth.instance;
_getCurrentUser();
}
_getCurrentUser () async {
_firebaseUser = await FirebaseAuth.instance.currentUser();
setState(() {
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: _firebaseUser.email.toString()).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 5.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
return Padding(
key: ValueKey(record.animal),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
),
child: new ListTile(
title: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(
"${record.animal} Slaughtering",
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.black),
),
],
),
)
),
);
}
}
class Record {
final String animal;
final String user;
final DocumentReference reference;
Record.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['animal'] != null),
assert(map['user'] != null),
animal = map['animal'],
user = map['user'];
Record.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Record<$animal:$user>]";
}
You need to do the following:
Stream<QuerySnapshot> getData() async*{
FirebaseUser firebaseUser = await FirebaseAuth.instance.currentUser();
yield* Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: firebaseUser.email.toString()).snapshots();
}
Then inside the StreamBuilder use getData():
return StreamBuilder<QuerySnapshot>(
stream: getData(),
builder: (context, snapshot) {
//....
The getData() method is asynchronous, since you are using a StreamBuilder then you need to return a Stream therefore you use the async* keyword, and you emit the result using yield*

Resources