0

On a project i am working on i list all contacts with checkboxlist. When you tap one it adds to selectedContact list and Database, tap again and it deletes from both so far so good. My problem was when app restarts selected Contacts also zeroed so i used database. I save phone numbers and query all contacts with that phone number and keep adding it to selectedContacts. The problem is even though selectedContact items has the same values as _contacts items selectedContact.contains(c) returns false.

Any ideas?

c is the 'Contact' class object

return ListView.builder(
  itemCount: _contacts?.length ?? 0,
  itemBuilder: (BuildContext context, int index) {
    Contact c = _contacts?.elementAt(index);
    //print(snapshot.data[index]);
    //selectedContacts.forEach((element) {print(element.phones.first.value);});
    //print(selectedContacts.contains(c).toString() +" " +c.phones.first.value);

    return CheckboxListTile(
      value: selectedContacts.contains(c),
      onChanged: (bool value) {
        setState(() {
          value
              ? addDb(c)
              : deleteDb(c);
        });
      },
      title: Text(c.displayName ?? ""),
      secondary: CircleAvatar(child: Text(c.initials())),
    );
void addDb(Contact c) {
  selectedContacts.add(c);
      DatabaseHelper.addContact(c);
    }

void deleteDb(Contact c) {
  selectedContacts.remove(c);
      DatabaseHelper.deleteContact(c);
    }
var selectedContacts = List<Contact>();

void importFromDatabase()async{
  var import = await DatabaseHelper.importContacts();
  import.forEach((element)async {
    var contacts = (await ContactsService.getContactsForPhone(element,withThumbnails: false,iOSLocalizedLabels: true)).toList();
    selectedContacts.addAll(contacts);
  });
}

I have some kind of debugging print statement below which prints selectedContacts and prints if it contains the C Debug code

but even though their variables are same it returns false.

Debug output

EDIT: Contact class is from contacts_service package

// Name
String displayName, givenName, middleName, prefix, suffix, familyName;

// Company
String company, jobTitle;

// Email addresses
Iterable<Item> emails = [];

// Phone numbers
Iterable<Item> phones = [];

// Post addresses
Iterable<PostalAddress> postalAddresses = [];

// Contact avatar/thumbnail
Uint8List avatar;


W33baker
  • 61
  • 8
  • Can you share the `Contact` class? – YoBo Feb 21 '21 at 18:07
  • Updated the post. its from [contacts_service](https://pub.dev/packages/contacts_service) Package – W33baker Feb 21 '21 at 18:12
  • Your code looks fine. Can you try to `print(selectedContacts.contains(c).toString() +" " +selectedContacts.length);`? – YoBo Feb 21 '21 at 18:17
  • `I/flutter ( 6949): false 1 I/chatty ( 6949): uid=10155(com.example.database_learning) 1.ui identical 1 line I/flutter ( 6949): false 1` There's only 1 Contact selected from previous session(before i closed it)(and i can confirm it from database) – W33baker Feb 21 '21 at 18:21
  • Can you do one more? `print(selectedContacts.first.phones.first.values+" " + c.phones.first.value);` – YoBo Feb 21 '21 at 18:23
  • `I/flutter ( 6949): (556) 565-6566 <- selectedContact | c-> (556) 565-6566` `I/flutter ( 6949): (556) 565-6566 <- selectedContact | c-> (566) 565-6566` `I/flutter ( 6949): (556) 565-6566 <- selectedContact | c-> (644) 643-2311` – W33baker Feb 21 '21 at 18:26
  • @YoBo There's nothing wrong with the `Contact` class. – Abion47 Feb 21 '21 at 18:59

1 Answers1

4

List.contains checks the items in the list for strict equality. The Contact class from the referenced package defines equality as two objects having equal values in 16 different fields. If even one of them is different in the object you are checking with, contains will return false. Since you are only saving phone numbers in your database, you are probably not saving all the fields that a Contact object has, so when you recreate that object, it won't "equal" the object it originally referred to.

You need to store the whole object in your database. Relational databases aren't ideal for storing objects with a complex structure, so instead I would recommend using a NoSQL database such as hive, which comes with the added bonus of being able to operate exponentially faster than sqflite due to its use of a memory buffer.

Fortunately, the Contact class defines toMap and fromMap methods, so both storage and retrieval are easy:

// Storage
var box = Hive.box('contactsBox');
var payload = contact.toMap();
await box.put(contact.phones.first, payload);

// Retrieval
var box = Hive.box('contactsBox');
var stored = box.get(phoneNumber);
var contact = Contact.fromMap(stored);

(Note: box.put returns a future, but awaiting it isn't strictly necessary. Storage of data into the memory buffer is synchronous and the future only resolves when the data is successfully written to the file storage. If you don't care about waiting for that to happen and want to access the data immediately, you can skip the await here.)

Abion47
  • 22,211
  • 4
  • 65
  • 88