如何在 Listview 构建器中使用 async/await

Ben*_* OO 5 flutter

我的 sqflite 数据库中有一个表,其中包含各个用户的通话历史记录。现在,在 flutter 中的“通话历史记录”页面上,我显示了从 sqflite 获取的完整历史数据,到目前为止它工作正常。但现在我想检查这些号码是否在我的历史记录列表中存在。如果是,那么我想在列表中显示他们的联系人姓名和头像。否则我只想显示数字。这是我的代码:

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
   }
  }
Run Code Online (Sandbox Code Playgroud)

Bac*_*ach 7

要在 UI 中调用异步调用,可以使用FutureBuilder。您可以对列表中的每个项目进行检查,如下所示:

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
    }
  },
);
Run Code Online (Sandbox Code Playgroud)

但是我不推荐这种方法,因为会有太多的异步调用,从而降低应用程序的速度。我的建议是对第一个中的所有项目进行检查queryRows,然后将其发送到 UI。

首先,您应该使用 Object 来表示历史记录,而不是 Map<String,dynamic> 以避免处理数据时出现错误。假设我们有一个HistoryRecord对象列表,从 解析queryRows。我们称这个列表为historyRecords

var historyRecords = List<HistoryRecord>.from(queryRows.map((row) => HistoryRecord.fromJson(row)));
Run Code Online (Sandbox Code Playgroud)

每个对象都应该有一个布尔属性fromContact来检查它是否在联系人中。然后我们可以这样做:

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
          }
        },
      );
    },
  );
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用 HistoryRecord 的以下属性和函数检查联系人:

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;
    }
Run Code Online (Sandbox Code Playgroud)