I need to process a DocumentSnapShot single document - firebase

I am new to flutter and am writing a new app. In the app I want to retrieve the user data from a firebase document and populate TextFields on the user screen. I can pull the data fine using DocumentSnapShot. I want to use TextEditingController and populate it with the data but I am not able to do it. I know the data is there since I can see it in debug mode. Below are snippets of my code.
Here is where I get the data and populate the TextEditingControllers:
final agentsRef = FirebaseFirestore.instance.collection(('agents'));
getCurrentAgent() async {
final DocumentSnapshot currentAgent =
await agentsRef.doc(globals.currentUid).get();
if (currentAgent == null) {
emailController.text = "";
new Future.delayed(Duration.zero, () {
final agentProvider =
Provider.of<AgentProvider>(context, listen: false);
agentProvider.loadValues(Agents());
});
} else {
// existing record
// Updates Controllers
**emailController.text = currentAgent.data[index].toString(); // THIS LINE IS WHERE I AM HAVING TROUBLE**
// Updates State
new Future.delayed(Duration.zero, () {
final agentProvider =
Provider.of<AgentProvider>(context, listen: false);
agentProvider.loadValues(widget.agents);
});
}
#override
void initState() {
getCurrentAgent();
super.initState();
}
How do I access each data element in the DocumentSnapShot?

You'll typically get the fields by their name and not by their index. So:
**emailController.text = currentAgent.data()["email"].toString();

Related

How to sequentially call the Getx controllers

My Task is :
I have an list of orders in firebase in users collection ,
I want to get information of those orders which is in orders collection,
And moment a list of order is updated in the firebase users collection.
The orders list should be automatically updated to get the latest information from the orders collection.
But for that i have to wait until list of orders is fetched from the users collection and only then i can query from orders collection about those orders..
I am stuck here,
And i want to actually understand Getx's bindStream , ever(), and observable variables,and Obx() is used in widget , But what if it is normal variable and i want to listen to it's changes ,how to do that, because Obx() can be only used while you use Widget
So far my code:
controllers.dart
UtilityController utilityController = UtilityController.instance;
CartController cartController = CartController.instance;
OrderController orderController = OrderController.instance;
UserModel.dart
class UserModel {
String? uid;
String? email;
String? name;
bool? isAdmin;
String? password;
List<CartItemModel>? cart;
String? token;
List<String>? orders;
UserModel({this.uid, this.email, this.name, this.isAdmin, this.password, this.cart, this.token, this.orders});
UserModel.fromSnapshot(DocumentSnapshot snapshot) {
uid = snapshot.id;
name = snapshot['name'];
token = snapshot['token'];
cart = _convertCartItems(snapshot['cart'] ?? []);
orders = new List<String>.from(snapshot['orders']);
}
List<CartItemModel> _convertCartItems(List cartFomDb) {
List<CartItemModel> _result = [];
if (cartFomDb.length > 0) {
cartFomDb.forEach((element) {
_result.add(CartItemModel.fromMap(element));
});
}
return _result;
}
}
UtilityController.dart
class UtilityController extends GetxController {
static UtilityController instance = Get.find();
Rx<UserModel> userModel = UserModel().obs;
#override
void onReady() {
super.onReady();
getUserType();
userModel.bindStream(listenToUser());
}
Stream<UserModel> listenToUser() {
return FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser?.uid)
.snapshots()
.map((snapshot) => UserModel.fromSnapshot(snapshot));
}
OrderController.dart
class OrderController extends GetxController {
static OrderController instance = Get.find();
RxList<OrderModel> orders = RxList<OrderModel>([]);
#override
void onReady() {
super.onReady();
orders.bindStream(getAllOrders());
ever(utilityController.userModel, function); --> I am using this , but i know this is not the correct way
}
function(UserModel userModel) {
getAllOrders();
}
Stream<List<OrderModel>> getAllOrders() {
return FirebaseFirestore.instance
.collection("orders")
.where(FieldPath.documentId, whereIn: utilityController.userModel.value.orders)
.snapshots()
.map((query) => query.docs.map((item) => OrderModel.fromMap(item.data(), item.id)).toList());
}
}
The utilityController.userModel.value.orders is null !!! it's not yet loaded, so all the orders are fetched :(
And even if the orders are changed... But new orders are not fetched from the orders collection
How to get over this?
Consider using like this.
class OrderController extends GetxController {
static OrderController instance = Get.find();
final isLoading = true.obs;
final isDone = false.obs;
var orders = OrderModel([]).obs;
#override
void onInit() async {
await listenForOrders();
super.onInit();
}
Future listenForOrders() async {
isLoading.value = true;
isDone.value = false;
Stream<QuerySnapshot> _userOrders;
_userOrders = getAllOrders();
_userOrders.listen((QuerySnapshot query) {
if (query.docs.isNotEmpty) {
query.docs.forEach((element) {
orders.addIf(!orders.contains(element), OrderModel.fromDocumentSnapshot(element));
});
} else {
isDone.value = true;
}
isLoading.value = false;
});
}
Stream<QuerySnapshot> getAllOrders() {
return FirebaseFirestore.instance
.collection("orders")
.where(FieldPath.documentId, whereIn: utilityController.userModel.value.orders)
.snapshots();
}
}
The best approach is to use the worker functions provided by getx controller like:
ever - is called every time the Rx variable emits a new value.
everAll - Much like ever , but it takes a List of Rx values Called every time its variable is changed. That's it.
once - is called only the first time the variable has been changed.

Can't assign a data value to a string - returns null - flutter

In my code, am trying a assign a string value to an empty string and display on the page but it keeps showing null but when I print it out, it shows the value.
String fName = '';
#override
void initState() {
super.initState();
getData();
}
getData() async {
FirebaseAuth _auth = FirebaseAuth.instance;
User _firebaseUser = _auth.currentUser;
print("============ MyHome ================");
print(_firebaseUser.uid);
_currentUser = await Database().getUserData(_firebaseUser.uid);
if (_currentUser != null) {
fName = _currentUser.firstName;
print(_currentUser.firstName);
}
}
database
Future<UserData> getUserData(String uid) async {
UserData returnValue = UserData();
try {
DocumentSnapshot _docSnapshot =
await _firestore.collection("users").doc(uid).get();
returnValue.uid = uid;
returnValue.firstName = _docSnapshot.data()["firstName"];
returnValue.lastName = _docSnapshot.data()["lastName"];
returnValue.userMail = _docSnapshot.data()["userMail"];
returnValue.userType = _docSnapshot.data()["userType"];
print("====================== on getData =============");
print(returnValue.firstName);
} catch (e) {
print(e);
}
return returnValue;
}
And whenever I try displaying the data it gives me null
Text("Hello, $fName"),
Please how do I do this or am I missing something
use setState to rebuild the widget tree with the value:
setState(() {
fName = _currentUser.firstName;
});
Since the getData function is async, flutter has already built the widget tree before getData finished. You'll now have to update the state using setstate.
setState(() {
fName = _currentUser.firstName;
});
You need to set the new state since we have made changes to the previous state (since your getData function is async.
setState(() {
fName = _currentUser.firstName;
});

How to get all documents

void initState() {
// TODO: implement initState
super.initState();
getusers().then((dataf){
snapshots=dataf.documents[1];// In here
});
}
final db=Firestore.instance;
DocumentSnapshot snapshots;
Future<QuerySnapshot> getusers(){
return db.collection("Users").document(widget.userid).collection("UsersJoined").getDocuments();
}
I want to get all the documents and fields inside documents without referring to their index. Is it possible by a loop or something to loop through the whole documents and get all their fields
To loop over the documents in the QuerySnapshot:
getusers().then((dataf){
for (var userDoc in dataf.documents) {
print(userDoc["name"] // get the value of one field
var data = userDoc.data // get all data as a Map<String, Object>
})
});

How do I write tests for a transformed stream in Flutter?

I have a Provider which has a method which takes data from Firebase as a stream, transforms it to a list and returns a Stream<List<Model>> . I'm trying to write a test where I want to check if the items in the List are the same as I expect them to be. How can I do that?
My Current Code:
test('getContacts returns a empty list when there is no contact',() async{
when(sharedPreferencesMock.get(any)).thenReturn('uid'); //mock the sharedprefs
documentSnapshot = DocumentSnapshotMock(); //mock documentsnapshot
when(documentSnapshot.exists).thenReturn(true); // this is done to pass the getUidByUsername method
documentReference = DocumentReferenceMock(documentSnapshotMock: documentSnapshot);
documentReference.setData({
'uid':'uid',
'contacts':[] // setting the usename in the data already so that duplicate contact exception is thrown
});
userDataProvider.getContacts().asBroadcastStream().listen((data){
expect(data.length,0);
});
});
And the provider method
#override
Stream<List<Contact>> getContacts() {
CollectionReference userRef = fireStoreDb.collection(Paths.usersPath);
DocumentReference ref =
userRef.document(SharedObjects.prefs.get(Constants.sessionUid));
return ref.snapshots().transform(StreamTransformer<DocumentSnapshot, List<Contact>>.fromHandlers(handleData: (documentSnapshot, sink) async{
List<String> contacts;
if (documentSnapshot.data['contacts'] == null) {
ref.updateData({'contacts': []});
contacts = List();
} else {
contacts = List.from(documentSnapshot.data['contacts']);
}
List<Contact> contactList = List();
for (String username in contacts) {
print(username);
String uid = await getUidByUsername(username);
DocumentSnapshot contactSnapshot = await userRef.document(uid).get();
contactList.add(Contact.fromFirestore(contactSnapshot));
}
sink.add(contactList);
}));
}
Update:
StreamController streamController = StreamController<List<Contact>>();
StreamSink<List<Contact>> sink = streamController.sink;
Stream<List<Contact>> stream = streamController.stream;
stream.listen((List<Contact> list){
expect(list.length,1);
});
userDataProvider.mapDocumentToContact(userCollection, userRef, documentSnapshot, sink);
streamController.close();
Make the lambda function that you currently pass to the StreamTansformer a separate function and test that.
If you want to test the full function there is a Firebase mock package on pub.

Flutter Getting a specific Firestore document data and store it into a String variable

I am trying to get data from my Firestore and store the data into a String variable named time. Then put the variable into _event.
However, when I run the code, I got null, and I don't know why.
StateFulWidget...
String event;
Map<DateTime, List> _events;
#override
void initState() {
super.initState();
Firestore.instance.collection('events').document('2019-07-
30').get().then((
DocumentSnapshot ds) {
event = ds['time'];
});
_events = {
DateTime.parse("2019-08-01"): [event]
};
}
This is my Firestore document
I am new to Flutter and I have struggled for this question for a long time. Hope someone can help me
Thank you so much
UPDATE
I change my code.
However, I am still getting errors: The method [](Intance of 'DateTime') was called on null.
String event;
Map<DateTime, List> _events;
Future<String> getData()async{
final ds = await
Firestore.instance.collection('events').document('2019-07-30').get();
return ds['time'];
}
#override
void initState() {
super.initState();
getData().then((s) {
event = s;
//when I print(s), it showed the data correctly
});
_events = {
DateTime.parse("2019-08-01"): [event]
};
Can someone shows me where I got wrong?
Thank you!
You are not waiting for the result, ie _events = .. will run before .then() clause and that is why event variable is still null;
.get() is a Future so you need to await the result.
final ds = await Firestore.instance.collection('events').document('2019-07-
30').get();
event = ds['time'];
_events = {
DateTime.parse("2019-08-01"): [event]
};
This works for me :
Firestore().instance.collection('Collection Name')
.document('Document Name').get().then((data) async {
var dataReceive = data['property Name'];
}

Resources