Benson OO
Benson OO

Reputation: 517

how to use async/await in Listview builder

I have a table in my sqflite database containing the call history of the respective users. Now on my Call history page in flutter, I am showing the complete history data, fetched from sqflite up till now its working fine. But now I want to check whether the numbers are in my history list exist in contact. If yes, then I want to show their contact name and avatar in the list. Otherwise I just want to show the number. Here's my code:

List<Map<String, dynamic>> ok =
await DatabaseHelper.instance.getAllLogs(argv);
setState(() {
  queryRows = ok;
});

var historyRecords = List<HistoryRecord>.from(queryRows.map((row) => HistoryRecord.fromJson(row)));

FutureBuilder<List<HistoryRecord>>(
            future: _checkContact(historyRecords),
            builder: (context, snapshot) {
              return ListView.builder(
                itemCount: historyRecords.length,
                itemBuilder: (context, index) {
                  print(historyRecords[index]);

                },
              );
            },
          )

   Future<List<HistoryRecord>> _checkContact(List<HistoryRecord> rec)async
   {
   for(int i=0;i<rec.length;i++) {
   var conhere=await 
   ContactsService.getContactsForPhone(rec[i].callHistoryNumber);
   
   //how should i map iterable contact list to Historyrecord
   }
  }

Upvotes: 6

Views: 9431

Answers (2)

Bach
Bach

Reputation: 3326

To call an asynchronous call in UI, you can use FutureBuilder. You can run a check for each and every items in the list like this:

FutureBuilder<bool>(
  initialData: false, // You can set initial data or check snapshot.hasData in the builder
  future: _checkRecordInContact(queryRow), // Run check for a single queryRow
  builder: (context, snapshot) {
    if (snapshot.data) { // snapshot.data is what being return from the above async function
      // True: Return your UI element with Name and Avatar here for number in Contacts
    } else {
      // False: Return UI element withouut Name and Avatar
    }
  },
);

However I don't recommended this method since there would be too many async calls that will slow down the app. What I recommend is to run a check for all items in the queryRows first, then send it to UI.

First of all you should use an Object to represent your history records instead of Map<String, dynamic> to avoid bugs when handling data. Let's say we have a list of HistoryRecord objects, parse from queryRows. Let's call this list historyRecords

var historyRecords = List<HistoryRecord>.from(queryRows.map((row) => HistoryRecord.fromJson(row)));

Each object should have a Boolean property fromContact to check if it's in the Contacts or not. We can then do this:

Widget buildListView(historyRecords) {
  return FutureBuilder<List<HistoryRecord>>(
    future: _checkContact(historyRecords), // Here you run the check for all queryRows items and assign the fromContact property of each item
    builder: (context, snapshot) {
      ListView.builder(
        itemCount: historyRecords.length,
        itemBuilder: (context, index) {
          if (historyRecords[index].fromContact) { // Check if the record is in Contacts
            // True: Return your UI element with Name and Avatar here
          } else {
            // False: Return UI element without Name and Avatar
          }
        },
      );
    },
  );
}

You can then check the contacts with the following property of HistoryRecord and function:

class HistoryRecord {
  bool fromContact;
  Uint8List avatar;
  String name;
  
  //... other properties

  HistoryRecord({this.fromContact, this.avatar, this.name});
}

Future<List<HistoryRecord>> _checkContact(List<HistoryRecord> rec) async {
      for (int i = 0; i < rec.length; i++) {
        Iterable<Contact> conhere =
            await ContactsService.getContactsForPhone(rec[i].callHistoryNumber);
        if (conhere != null) {
          rec[i]
            ..name = conhere.first.displayName
            ..avatar = conhere.first.avatar
            ..fromContact = true;
        }
      }
      return rec;
    }

Upvotes: 7

Silo&#233; Bezerra Bispo
Silo&#233; Bezerra Bispo

Reputation: 2254

You can use FutureBuilder to check each number like:

    ListView.builder(
        itemCount: history.length,
        itemBuilder: (context, index) {
          FutureBuilder(
             future: checkContactExists(history[0]),
             builder: (context, snap){
                if(snap.hasData){
                  if(snap.data = true){
                     return PersonContact();
                  }else{
                     return JustNumber();
                  }
                }
                return Loading();
             }
          )
        },
      );

Upvotes: 1

Related Questions