I am saving data of different users in their uid node and in uid node i have generated different keys in which i have saved data. I my trying to retrieve email,username from keys node.
I have tried to fetch email,username using this code:-
`
#override
void initState() {
super.initState();
getCurrentUser();
rootRef.child('Manager').child(loggedInUser.uid).child(accountKey);
rootRef.once().then((DataSnapshot snap) {
var value= snap.value;
print(value['username']);
}
);
}
`
but i am getting a null value.
How could i retrieve email, username and display it to Text widget.
You need to use a FutureBuilder() since the uid will be null in the above code, therefore create a method that will return a Future<DataSnapshot>:
Future<DataSnapshot> getData() async{
var user = await FirebaseAuth.instance.currentUser();
final dbRef = FirebaseDatabase.instance.reference().child('Manager').child(user.uid).child(accountKey);
return await dbRef.once();
}
Then use it inside FutureBuilder:
FutureBuilder(
future: getData(),
builder: (context, AsyncSnapshot<DataSnapshot> snapshot) {
if (snapshot.hasData) {
Related
I followed some tutos and I add user on firestore successly but I can't print this user on the profile page when the current user is logging because doc. is underlined in red in my backend, they say : The getter 'doc' isn't defined for the type 'DocumentSnapshot<Object?>'.
This is my entire backend
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
Future<void> userSetup(String displayName) async {
CollectionReference users = FirebaseFirestore.instance.collection('Users');
FirebaseAuth auth = FirebaseAuth.instance;
String uid = auth.currentUser!.uid.toString();
users.add({'displayName': displayName, 'uid' : uid });
final result = await users.doc(uid).get();
return result.doc.data()['displayName'];
}
Plus I'm having a error too in my front end but I think it's because the backend wrond initialized or just a code wrote.
So this is my front end code:
(userId is red underlined because it's undefined and my [] operators too),
hope you help me !
FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.doc('uid')
.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text(snapshot.data['displayName'],
);
} else {
return Text("Loading...");
I edited
FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get(),
builder: (context, snapshot) {
if (snapshot.hasData)
return Text("Loading...");
if (snapshot.data == null) {
print('Document does not exist on the database');
}else{
return Text("Researching data...");
}
if (snapshot.connectionState == ConnectionState.done) {
var name = snapshot.data as DocumentSnapshot;
return Text(name['displayName'],
);
} else {
return Text("Loading..");
}
},
)
You are not specifying the document ID when creating user, so then, is created unique ID for your user document, and you can't access the document by user ID. And you should also await the creating process. The solution is easy, just specify the document ID when creating user:
Future<void> userSetup(String displayName) async {
CollectionReference users = FirebaseFirestore.instance.collection('Users');
FirebaseAuth auth = FirebaseAuth.instance;
String uid = auth.currentUser!.uid.toString();
await users.doc(uid).set({'displayName': displayName, 'uid': uid });
final result = await users.doc(uid).get();
final data = result.data() as Map<String, dynamic>;
return data['displayName'];
}
Hope it works!
I am fairly new to Flutter and following some tutorials I've managed to create a little app. Users can make an account and post announcements about their pets. All these announcement go to the home page, which is built using a Stream. Each announcement has a field that saves the UID of the user who posted it. What I am trying to do now is make the home page display all the announcements, except for the ones the current user posts. I want to do so by comparing the .userId field in the announcements to the UID of the current user.
StreamBuilder(
stream: getUsersPets(context),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Text(
"Loading...",
);
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder:
(BuildContext context, int index) =>
buildPetsList(context,
snapshot.data.documents[index]),
);
})
This is how the buildPetsList function looks like:
Widget buildPetsList(BuildContext context, DocumentSnapshot document) {
final pet = Pet.fromSnapshot(document);
Size size = MediaQuery.of(context).size;
if (pet.userId == 'tLH3ZZvxOEMQkdSL63jncQgbqN32') {
return Container(
height: 0,
);
} else { ...
This works fine with the hardcoded UID, but I want it to be stored in a variable and comparing it that way, obviously.
I have a method inside my Auth class but I don't know how to use it to get what I want.
class AuthService {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
Stream<String> get onAuthStateChanged => _firebaseAuth.onAuthStateChanged.map(
(FirebaseUser user) => user?.uid,
);
// GET UID
Future<String> getCurrentUID() async {
return (await _firebaseAuth.currentUser()).uid;
}
// GET CURRENT USER
Future getCurrentUser() async {
return await _firebaseAuth.currentUser();
}
}
I'm attempting to do the following:
Listen to a Firestore stream so when a new document is added, the StreamBuilder will receive it, modify it, and then present it.
The "modification" takes the Stream data, which includes a Firestore UID, gets the data from Firestore with that UID, and then the StreamBuilder is populated with that data.
So the flow is: New document added -> Stream gets document -> Function gets UID from that document -> Function uses that UID to get more data from Firestore -> Function returns to populate StreamBuilder with that new data.
My current set-up is as follows -- which works, but the FutureBuilder is obviously making the Firestore call each time the widget is rebuilt, and nobody wants that.
Stream<QuerySnapshot> upperStream;
void initState() {
super.initState();
upperStream = aStream();
}
Stream<QuerySnapshot> aStream() {
return Firestore.instance
.collection('FirstLevel')
.document(/*ownUID (not related to stream)*/)
.collection('SecondLevel')
.snapshots();
}
Future<List> processStream(List streamData) async {
List futureData = List();
for (var doc in streamData) {
Map<String, dynamic> dataToReturn = Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection('FirstLevel')
.document(/*OTHER USER'S UID FROM STREAM*/)
.get();
dataToReturn['i'] = userDoc['i'];
futureData.add(dataToReturn);
}
return futureData;
}
...
...
//The actual widget
Expanded(
child: StreamBuilder(
stream: upperStream,
builder: (context, snapshot) {
// Error/null handling
return FutureBuilder(
future: processStream(snapshot.data.documents),
builder: (context, futureSnap) {
// Error/null handling
return ListView.builder(
shrinkWrap: true,
itemCount: futureSnap.data.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
//Continuing with populating
});
});
}),
),
What's the best way to handle a flow like this? Creating a method where the data from the Firestore stream is modified and then returned without needing ListView.builder at all?
Edit: I tried creating my own stream like this:
Stream<Map<String, dynamic>> aStream2() async* {
QuerySnapshot snap = await Firestore.instance
.collection(FirstLevel)
.document(/*OWN UID*/)
.collection(SecondLevel)
.getDocuments();
for (var doc in snap.documents) {
Map<String, dynamic> data = Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection(FirstLevel)
.document(/*OTHER USER'S UID RECEIVED FROM STREAM*/)
.get();
data['i'] = userDoc['i'];
yield data;
}
}
However, the Stream is not triggered/updated when a new Document is added to the SecondLevel collection.
Alright I think I found the path to the solution. I get the data from the stream, modify it, and then yield it to the StreamBuilder within one method and no longer need the FutureBuilder. The key to this, as Christopher Moore mentioned in the comment, is await for. The stream method looks like this:
Stream<List> aStream() async* {
List dataToReturn = List();
Stream<QuerySnapshot> stream = Firestore.instance
.collection(LevelOne)
.document(OWN UID)
.collection(LevelTwo)
.snapshots();
await for (QuerySnapshot q in stream){
for (var doc in q.documents) {
Map<String, dynamic> dataMap= Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection('UserData')
.document(doc['other user data var'])
.get();
dataMap['i'] = userDoc['i'];
//...//
dataToReturn.add(dataMap);
}
yield dataToReturn;
}
}
And then the StreamBuilder is populated with the modified data as I desired.
I found myself using this to implement a chat system using the Dash Chat package in my app. I think using the map function on a stream may be a little cleaner here is a sample:
Stream<List<ChatMessage>> getMessagesForConnection(
String connectionId) {
return _db
.collection('connections')
.doc(connectionId)
.collection('messages')
.snapshots()
.map<List<ChatMessage>>((event) {
List<ChatMessage> messages = [];
for (var doc in event.docs) {
try {
messages.add(ChatMessage.fromJson(doc.data()));
} catch (e, stacktrace) {
// do something with the error
}
}
return messages;
});}
I am trying to make my Flutter app update when a change is made to the usersCollection.document(user.uid) firebase document.
When the user document is updated I want to retrieve the data from this document but also from another firebase document, facilitiesCollection.document(...).
My current code
Future<Map> _getCheckedInFacilityData() async {
Map<String, dynamic> result = {};
try {
DocumentSnapshot userDoc =
await _db.usersCollection.document(user.uid).get();
if (userDoc.data['checkedIn']) {
// User is checked in
DocumentSnapshot facDoc = await _db.facilitiesCollection
.document(userDoc.data['activeFacilityID'].toString())
.get();
result['facilityID'] = userDoc.data['activeFacilityID'];
result['sessionID'] = userDoc.data['activeSessionID'];
result['facilityActiveUsers'] = facDoc.data['activeUsers'].length;
result['facilityName'] = facDoc.data['name'];
return result;
}
} catch (er) {
debugPrint(er.toString());
}
return null;
}
FutureBuilder<Map>(
future: _getCheckedInFacilityData(),
builder: (context, map) {
switch (map.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
...
This is currently working but the page is not updated when a change is made to the user document.
I haven't been using Flutter/Dart for long so any ideas are welcome.
Is it possible to return a custom object/map which is comprised of 2 separate documents from a StreamBuilder, or is there another method that will work in my situation.
Surely you can do it with Streams asyncMap() and then listen in StreamBuilder
Basic algoritm
Get stream of you first data type and then asyncMap to wait second data type and return them both
stream.asyncMap(
(v1) async {
final v2 = await Future.delayed(Duration(seconds: 1), () => 4);
return v1 * v2;
},
);
Closer to your code
Stream<Map<String, dynamic>> _getCheckedInFacilityData() {
return _db.usersCollection.document(user.uid).snapshots()
.asyncMap(
(userDoc) async {
final DocumentSnapshot facDoc =
await _db.facilitiesCollection
.document(userDoc.data['activeFacilityID'].toString())
.get();
final Map<String, dynamic> userMap = userDoc.data;
final Map<String, dynamic> facMap = facDoc.data;
return userMap..addAll(facMap);
},
);
}
In this function I merge two maps - be carefull if both maps have identical keys map will keep only last was added key in our case from addAll(facMap)
Last step is to show you streamed data on screen - use StreamBuilder
StreamBuilder<Map>(
stream: _getCheckedInFacilityData(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('${snapshot.error}');
} else if (snapshot.connectionState == ConnectionState.waiting) {
return LinearProgressIndicator();
}
return /* some widget that shows your data*/;
},
),
I'm new to flutter and firebase so bear with me. I'm using email sign up with firestore and flutter on my app, on registration some additional fields are saved to firestore. I want to retrieve those fields to display on the user profile.
The key identifier for the fields saved to the users collection is the auto generated user id upon sign up.
I have in my widget build context
child: new FutureBuilder<FirebaseUser>(
future: _firebaseAuth.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
String userID = snapshot.data.uid;
_userDetails(userID);
return new Text(firstName);
}
else {
return new Text('Loading...');
}
},
),
And my get associated data method is:
Future<void> getData(userID) async {
// return await Firestore.instance.collection('users').document(userID).get();
DocumentSnapshot result = await Firestore.instance.collection('users').document(userID).get();
return result;
}
To retrieve the user details
void _userDetails(userID) async {
final userDetails = getData(userID);
setState(() {
firstName = userDetails.toString();
new Text(firstName);
});
}
I have tried adding a .then() to the set state in _userdetails but its saying userDetails is a type of void and cannot be assigned to string.
The current code block here returns instance of 'Future' instead of the user Details.
Your method is marked as async so you have to await for the result :
Future<void> _userDetails(userID) async {
final userDetails = await getData(userID);
setState(() {
firstName = userDetails.toString();
new Text(firstName);
});
}