Bug Description
When updating a contact's name fields (first, middle, last) on Android, the changes do not persist if the contact has multiple raw contacts (e.g. Google account + WhatsApp).
Root Cause
The Dart Name model does not expose a metadata field (unlike Phone, Email, etc.). As a result, when Dart serializes the contact for update, the name's rawContactId is not included in the JSON payload sent to Kotlin.
On the Kotlin side, getPrimaryRawContactIdForUpdate calls getRawContactIdWithMostProperties, which counts properties per raw contact using their metadata. Because contact.name?.metadata is always null (never round-tripped from Dart), the name's rawContactId contributes zero to the count. The "winner" is determined solely by phone/email/etc counts — which often picks the WhatsApp raw contact.
Name.toUpdateOperations(rawContactId) then:
- Deletes StructuredName from the WhatsApp raw contact (which had none)
- Inserts new StructuredName into the WhatsApp raw contact
Meanwhile, the Google raw contact's StructuredName is untouched. Android's aggregation prefers the Google account for names, so the old name persists.
Reproduction
- Have a contact with both a Google account raw contact and a WhatsApp raw contact
- Call
FlutterContacts.get(id, properties: ContactProperties.all) — fetch succeeds, name is correct
- Modify
contact.name (e.g. clear middle name)
- Call
FlutterContacts.update(contact) — returns successfully
- Re-fetch the contact — name is unchanged
Logcat evidence (name.middle being cleared, verify immediately after update):
[SAVE] fresh name: first=Jegadesan S. middle=K. last=Auditor
[SAVE] updated name: first=Jegadesan S. middle= last=Auditor
[SAVE] update complete for id=76954
[SAVE] verify name: first=Jegadesan S. middle=K. last=Auditor ← unchanged
[SAVE] rawContact: id=518 account=rajiv.m1991@gmail.com type=com.google
[SAVE] rawContact: id=76866 account=WhatsApp type=com.whatsapp
Expected Fix
Add metadata field to the Dart Name class (same as Phone, Email, Address, etc. already have). This allows the name's rawContactId to round-trip through the method channel, so getRawContactIdWithMostProperties correctly counts the name's raw contact and picks the right one for the update.
// Name class should include:
final PropertyMetadata? metadata;
And in copyWith:
Name copyWith({
...
PropertyMetadata? metadata,
}) => Name(
...
metadata: metadata ?? this.metadata,
);
Environment
- flutter_contacts: 2.2.0
- Android (tested on device with Google account + WhatsApp contacts)
- Contacts with 2 raw contacts:
com.google + com.whatsapp
Bug Description
When updating a contact's name fields (first, middle, last) on Android, the changes do not persist if the contact has multiple raw contacts (e.g. Google account + WhatsApp).
Root Cause
The Dart
Namemodel does not expose ametadatafield (unlikePhone,Email, etc.). As a result, when Dart serializes the contact for update, the name'srawContactIdis not included in the JSON payload sent to Kotlin.On the Kotlin side,
getPrimaryRawContactIdForUpdatecallsgetRawContactIdWithMostProperties, which counts properties per raw contact using their metadata. Becausecontact.name?.metadatais alwaysnull(never round-tripped from Dart), the name's rawContactId contributes zero to the count. The "winner" is determined solely by phone/email/etc counts — which often picks the WhatsApp raw contact.Name.toUpdateOperations(rawContactId)then:Meanwhile, the Google raw contact's StructuredName is untouched. Android's aggregation prefers the Google account for names, so the old name persists.
Reproduction
FlutterContacts.get(id, properties: ContactProperties.all)— fetch succeeds, name is correctcontact.name(e.g. clear middle name)FlutterContacts.update(contact)— returns successfullyLogcat evidence (name.middle being cleared, verify immediately after update):
Expected Fix
Add
metadatafield to the DartNameclass (same asPhone,Email,Address, etc. already have). This allows the name'srawContactIdto round-trip through the method channel, sogetRawContactIdWithMostPropertiescorrectly counts the name's raw contact and picks the right one for the update.And in
copyWith:Environment
com.google+com.whatsapp