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

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'];
}

Related

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;
});

I need to process a DocumentSnapShot single document

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();

Wait for stream inside a Future: Flutter

I want to check if the Firebase DB is connected or not, so I have to use a Future to return a boolean
Have a check at my code..
#override
Future<bool> isAvailable() async {
bool ret = false;
await firebaseInstance.reference().child('.info/connected').onValue.listen((event) {
ret = event.snapshot.value;
});
return ret;
}
the firebaseInstace.reference is a StreamSubscription type and does not wait for the future to return me a result.
please help.
If you only need to know the current value, use once().then instead of onValue.listen
#override
Future<bool> isAvailable() async {
var snapshot = await firebaseInstance.reference().child('.info/connected').once();
return snapshot.value;
}
Instead of awaiting the end of the stream subscription (it never ends), just take the first value:
#override
Future<bool> isAvailable() => firebaseInstance.reference().child('.info/connected').onValue.first;
You can put the StreamSubcription in a variable
StreamSubscription subscription = someDOMElement.onSubmit.listen((data) {
// you code here
if (someCondition == true) {
subscription.cancel();
}
});
More can be found here is there any way to cancel a dart Future?
You can do the following:
#override
Future<bool> isAvailable() async {
bool ret = false;
Stream<Event> events =
FirebaseDatabase.instance.reference().child('.info/connected').onValue;
await for (var value in events) {
ret = value.snapshot.value;
}
return ret;
}
onValue returns a Stream<Event> and then you can use await for to iterate inside the Stream and get the data, and then it will return.

Getting output as "Instance of 'Future<dynamic>'" in flutter

I am trying get sum of a column in sqlite table using Bloc pattern.
debt_bloc.dart
getTotalAmount() async {
return await _debtRepository.getTotalAmt();
}
debt_dao.dart
Future<int> getTotalAmount() async {
final db = await dbProvider.database;
var result = await db.rawQuery("SELECT SUM(amount) FROM $debtDetailsTable");
int value = result[0]["SUM(amount)"];
return value;
}
debt_repositary.dart
Future getTotalAmount() => debtDao.getTotalAmount();
When i try to print like below
var total;
#override
void initState () {
super.initState();
_asyncMethod();
}
_asyncMethod() async {
var t = await debtBloc.getTotalAmount();
setState(() {
total = t;
});
}
print(total);
Output not updating when add new data. But if go back to home screen and come to respective screen value is updating.
Please guide me in right way. Thanks in advance
Await on your method first before printing it.
var total = await debtBloc.getTotalAmount(); // await here
print(total); // should now print some int value

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.

Resources