0

I have a simple table from which I'm fetching a list of records. Once I get the records, then I have to get information online for each of the records. The code to do this is as follows:

class UserStationList {
  List<UserStationListItem> _userStations = [];
  final StreamController<HomeViewState> stateController;

  UserStationList({@required this.stateController});

  Future fetchUserStations() async {
    stateController.add(HomeViewState.Busy);

    //Fetch stations from table.
    List<Map<String, dynamic>> stations =
        await UserStationDatabase.instance.queryAllRows();

    //If there are no stations, return and tell the screen to display the no data message.
    if (stations.length == 0) {
      stateController.add(HomeViewState.NoData);
      return;
    }

    //Loop through each of the stations in the list and build the collection.
    stations.forEach((station) async {
      UserStationListItem newItem =
          await _getPurpleAirSiteData(station['_id'], station['stationid']);
      _userStations.add(newItem);
    });

    //When done, let the screen know.
    stateController.add(HomeViewState.DataRetrieved);
  }

  Future<UserStationListItem> _getPurpleAirSiteData(
      int id, int stationId) async {
    var response = await http.get('$kURL$stationId');
    var data = json.decode(response.body);

    return UserStationListItem(
        id: id, stationId: stationId, stationName: data['results'][0]['Label']);
  }
}

The problem that I am running into involves the futures. I am processing the loop in a forEach and calling into the _getPurpleAirSiteData function for each. Within that function I have to await on the http.get to bring in the data. The stateController.add(HomeViewState.DataRetrieved) function is being called and the function exits long before the loop is completed. This is resulting in the data not being available when the StreamBuilder that I have receiving the data is run.

How can I set this up so that the loop runs completely before calling stateController.add?

1 Answer 1

1

I would change this part of code to a list of Futures and await-ing on it.

//Loop through each of the stations in the list and build the collection.
stations.forEach((station) async {
   UserStationListItem newItem =
    await _getPurpleAirSiteData(station['_id'], station['stationid']);
  _userStations.add(newItem);
});

To:

List<Future<UserStationListItem>> listOfFutures = [];
stations.forEach((station)  {
    listOfFutures.add(_getPurpleAirSiteData(station['_id'], station['stationid']));
});

var stationItems = await Future.wait(listOfFutures);

stationItems.forEach((userStationListItem) {
  _userStations.add(userStationListItem);
});

What I am essentially doing creating a list of Futures with your server request. Then await on it which returns a list of item result Maintaining index, which in turn ensures that requests are completed before you hit statecontroller.add. You also gain a performance gain since all request are not going one by one and instead asynchronously. Then you just iterate through the future result and add it to your item list.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks. I understand what you're getting at with this. I am getting this error: The argument type 'List' can't be assigned to the parameter type 'Iterable<Future>'. It's happening on this line of code: var stationItems = await Future.wait(listOfFutures);
Now I'm getting this: The argument type 'List<Future>' can't be assigned to the parameter type 'Iterable<Future<UserStationListItem>>'.dart(argument_type_not_assignable) I can make the error go away with this line: List<Future<UserStationListItem>> listOfFutures = [];
Yeah that should work. I am not away of the types you are using so didn't check it. But you should get an idea of what I am trying to do.
Thank you. You got me past something that I've been trying to work out for many days now.
Welcome, glad to help :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.