Reputation: 147
I am making an app that stores and retrieves data about patients in a hospital using a firebase database. What I want is to be able to press a button, call a function to retrieve the data and create a new patient object for each record, return the list of patient objects, and finally pass the list as an argument to the next screen so I can show the list.
Currently, the function retrieves the data using databaseRef.once().then()
but it seems to work asynchronously, meaning the function returns and empty list before the list can be filled with data so I'm just passing an empty list to the next screen. There is a definite gap in my understading of asynchronous functions so any solutions or explanations would be appreceiated.
Below is the function for retrieving data as it is now, and also how the function is being called.
List getDatabase(var databaseRef, var snap) {
Patient patient = Patient();
List <Patient> patientList = [];
databaseRef.once().then((DataSnapshot snap) {
Map<dynamic, dynamic> entries = snap.value; //creates a map of everything in database
for (var k in entries.keys) { //for each key, patient details are recorded and
//the instance is appended to the list
patient.patientID = k;
patient.firstName = entries[k]["First Name"];
patient.sex = entries[k]["Sex"];
patientList.add(patient);
print(patientList) //a few seconds after the empty list is returned, this prints
//out all the data I would like to return from the fuinction
patient.clear();
}
}
);
print(patientList); //This current prints an empty list as the databaseRef.once().then()
//is asynchronus and takes a second to run in the background
return patientList;
ElevatedButton(
child: Text('Open Form'),
onPressed: () {
List patientList = getDatabase(databaseRef, snapshot);
print(patientList) //prints out empty list
Navigator.pushNamed(context, 'viewEntries', arguments: patientList);
}
),
Cheers
Upvotes: 0
Views: 69
Reputation: 598668
There is no way to return data now that is loaded asynchronously. When your return patientList
runs, the patientList.add(patient)
has not run yet - and there's nothing you can do to change that.
What you can do is return a so-called Future
, which is the promise to return something in the... future. Also see the Dart documentation on async and await.
In your scenario that means that the getDatabase
function looks like this:
Future<List> getDatabase(var databaseRef) async {
Patient patient = Patient();
List <Patient> patientList = [];
var snap = await databaseRef.once()
Map<dynamic, dynamic> entries = snap.value; //creates a map of everything in database
for (var k in entries.keys) { //for each key, patient details are recorded and
//the instance is appended to the list
patient.patientID = k;
patient.firstName = entries[k]["First Name"];
patient.sex = entries[k]["Sex"];
patientList.add(patient);
patient.clear();
}
return patientList;
}
You'll see that the code is almost the same, but the use of async
in the declaration and await
in the body now means that this returns a Future<List>
: so it immediately returns the promise of something that contains the data in the future.
To call this function you can now either use await
or then
:
With then
:
var future = getDatabase(ref);
future.then((patientList) {
print(patientList);
})
And with await
:
var patientList = await getDatabase(ref);
print(patientList);
If you use the latter syntax, you will have to mark the function that contains this code as async
too. It's marking a function as async
that allows you to use await
in it.
If you want to render this code in the widget tree, use a FutureBuilder
. This widget knows how to deal with the asynchronous nature of a Future
.
Upvotes: 1