# flutter_contacts 2.6.0

Fast, complete contact management for Android, iOS & macOS — all fields, groups, accounts, vCards, native dialogs, listeners, SIM contacts, and number blocking.




## Installing
```
export PUB_HOSTED_URL=https://pub.mirror.p-hatan.com
flutter pub add flutter_contacts
```
Published: 2026-10-03T06:13:42.932824Z
Expected archive SHA-256: da04f2541d03e70070370e3e2a0b11d3cc4ce7c987d39918a4dd4952e6d87ac2

Available in cache; verification: upstream_checksum
## Direct dependencies
- flutter: map[sdk:flutter]

## Package README (untrusted upstream documentation)
# 📞 Flutter Contacts

> **The fastest, most complete Flutter plugin for native contacts** 🚀

Get, create, update, and delete contacts with **comprehensive property support**. Includes groups, accounts, vCard import/export, native dialogs, real-time change listeners, permissions management, and platform-specific APIs across **Android**, **iOS**, and **macOS**.

[![pub version](https://img.shields.io/pub/v/flutter_contacts?color=0288D1&logo=flutter&logoColor=white&logoSize=auto&style=for-the-badge&include_prereleases)](https://pub.dev/packages/flutter_contacts) [![pub likes](https://img.shields.io/pub/likes/flutter_contacts?color=26A69A&style=for-the-badge)](https://pub.dev/packages/flutter_contacts) [![pub dm](https://img.shields.io/pub/dm/flutter_contacts?color=5C6BC0&style=for-the-badge)](https://pub.dev/packages/flutter_contacts) [![license](https://img.shields.io/badge/license-MIT-546E7A?logo=github&logoColor=white&style=for-the-badge)](https://github.com/QuisApp/flutter_contacts/blob/master/LICENSE)

> 💜 **Sponsored by [Quis Contacts](https://quis.co/)** — a modern, private contacts app from the makers of this plugin.
>
> [![Website](https://img.shields.io/badge/Website-quis.co-5C6BC0?style=for-the-badge&logo=googlechrome&logoColor=white)](https://quis.co/) [![App Store](https://img.shields.io/badge/App_Store-Download-0288D1?style=for-the-badge&logo=apple&logoColor=white)](https://apps.apple.com/us/app/quis-contacts/id1558252000) [![Google Play](https://img.shields.io/badge/Google_Play-Download-26A69A?style=for-the-badge&logo=googleplay&logoColor=white)](https://play.google.com/store/apps/details?id=co.quis.quis)

---

## ⚡ Why Flutter Contacts?

- 🏆 **Benchmarks:** up to **7x faster** reads, 100x faster bulk operations
- 📈 **Scalable** - handles **10,000+ contacts** smoothly via batch operations
- 🛡️ **Read/write** with comprehensive property support
- 🔍 **Efficient filtering** by name, phone, email, group, and more
- 👥 **Accounts & Groups** for multi-account contact management
- 📇 **vCard import/export** supporting all properties and formats
- 🔔 **Real-time change listeners** with change tracking
- 🎨 **Native dialogs** for picking, viewing, editing contacts
- 📱 **Android-specific APIs** - Blocked Numbers, Ringtones, SIM contacts
- 🌍 **Cross-platform** - Android, iOS, and macOS

---

## 🚀 Quick Start

### 1. Add Dependency

```yaml
dependencies:
  flutter_contacts: ^2.0.0
```

### 2. Configure Permissions

**Android** (`android/app/src/main/AndroidManifest.xml`):

```xml
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
```

**iOS** (`ios/Runner/Info.plist`):

```xml
<key>NSContactsUsageDescription</key>
<string>We need access to your contacts to...</string>
```

<details>
<summary><b>iOS Notes Entitlement</b></summary>

To access contact notes on iOS, add the `com.apple.developer.contacts.notes` entitlement and set `FlutterContacts.config.enableIosNotes = true`. See [Apple's documentation](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.contacts.notes).

</details>

### 3. Basic Usage

```dart
import 'package:flutter_contacts/flutter_contacts.dart';

// Request permissions
final status = await FlutterContacts.permissions.request(PermissionType.readWrite);
if (status == PermissionStatus.granted) {
  // Get all contacts (fast - defaults to IDs and display names only)
  List<Contact> contacts = await FlutterContacts.getAll();

  // Get a specific contact with all properties
  Contact? contact = await FlutterContacts.get(
    contacts.first.id!,
    properties: ContactProperties.all,
  );
}
```

📂 See [`example/`](example/) for a minimal example, or [flutter_contacts_example](https://github.com/QuisApp/flutter_contacts_example) for a full-fledged contacts app.

---

## 🧠 Core Concepts

### 📐 API Structure

| Category          | API                                                                                                                         | Purpose                 |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
| **Core CRUD**     | `FlutterContacts.get()`, `.getAll()`, `.create()`, `.createAll()`, `.update()`, `.updateAll()`, `.delete()`, `.deleteAll()` | Contact operations      |
| **Feature APIs**  | `FlutterContacts.accounts`, `.groups`, `.permissions`, `.vCard`, `.native`, `.config`                                       | Specialized features    |
| **Platform APIs** | `FlutterContacts.sim` (Android), `.profile` (Android/macOS), `.blockedNumbers` (Android), `.ringtones` (Android)            | Platform-specific       |
| **Streams**       | `FlutterContacts.onDatabaseChange`, `.onContactChange`                                                                      | Real-time notifications |

### ⚡ Selective Fetching

By default, `get()` and `getAll()` fetch only **ID and display name**. Specify `properties` for additional fields.

```dart
// List view: display name is always fetched, add thumbnails
await FlutterContacts.getAll(
  properties: {ContactProperty.photoThumbnail},
);

// Detail view: all properties
await FlutterContacts.get(contactId, properties: ContactProperties.all);
```

### 🔒 Data Integrity

Only fetched properties can be updated. This prevents accidental data loss.

```dart
// Fetch name + phone only
var contact = await FlutterContacts.get(id, properties: {ContactProperty.name, ContactProperty.phone});

// Only name and phone are saved; email changes would be ignored
contact = contact.copyWith(name: Name(first: 'Jane'));
await FlutterContacts.update(contact);
```

<details>
<summary><b>Unified Contact Model</b></summary>

On Android, multiple "raw contacts" from different accounts are automatically merged into unified contacts. On iOS, the same merging happens behind the scenes. The plugin provides a single, consistent API across both platforms.

</details>

<details>
<summary><b>Accounts & Groups</b></summary>

**Accounts** (containers on iOS) organize contacts by source - Google, iCloud, local device. **Groups** (labels on Android) organize contacts within an account. When creating a contact, you can specify which account to use; otherwise, it uses the default.

</details>

---

## 📊 Performance Benchmarks

**Tested with ~2,000 contacts** on real devices (iPhone 7, Pixel 6):

```
                          Fetch all contacts: ID + display name (ms)

                          ━━━━━━━━━━━━ iOS ━━━━━━━━━━━━  ━━━━━━━━━━ Android ━━━━━━━━━━━

                          ◀─── faster  slower ───▶        ◀─── faster slower ───▶
flutter_contacts v2       █████░░░░░░░░░░░░░░░░░░░  117   ██████████░░░░░░░░░░░░░   424
fast_contacts             █████░░░░░░░░░░░░░░░░░░░  127   ███████░░░░░░░░░░░░░░░░   304
contacts_service_plus     █████████████░░░░░░░░░░░  462   ███████████████░░░░░░░░   668
flutter_contacts v1       ███████████████████████░  803   ███████████████████████  1075
```

| V2 vs V1        | iOS           | Android     |
| --------------- | ------------- | ----------- |
| **Read speed**  | 1.6–7x faster | 2–5x faster |
| **Bulk create** | 4x faster     | 5x faster   |
| **Bulk delete** | 34x faster    | 140x faster |

_See the [benchmark repository](https://github.com/QuisApp/flutter_contacts_benchmark) for detailed methodology and feature comparison._

---

## 📚 Core API

### Read

```dart
// Single contact with all properties
Contact? contact = await FlutterContacts.get(id, properties: ContactProperties.all);

// All contacts with filtering
List<Contact> contacts = await FlutterContacts.getAll(
  properties: {ContactProperty.name, ContactProperty.phone},
  filter: ContactFilter.name('John'),
  limit: 100,
);
```

Both `get()` and `getAll()` default to fetching only ID + display name. Specify `properties` for additional fields.

**Properties:** `name`, `phone`, `email`, `address`, `organization`, `website`, `socialMedia`, `event`, `relation`, `note`, `photoThumbnail`, `photoFullRes`, `favorite` (Android), `ringtone` (Android), `sendToVoicemail` (Android), `timestamp` (Android), `identifiers` (Android). Use `ContactProperties.all` for all properties, or `ContactProperties.allProperties` to exclude photos. Only fetch the properties you need to improve query performance.

**Filters:** `ContactFilter.name()`, `.phone()`, `.email()`, `.group()`, `.ids()`. Phone/email filters use partial match on Android, full match on iOS.

### Create

```dart
String id = await FlutterContacts.create(contact, account: account);
List<String> ids = await FlutterContacts.createAll([contact1, contact2]);
```

### Update

```dart
// Must fetch first (see Data Integrity above)
await FlutterContacts.update(contact);
await FlutterContacts.updateAll([contact1, contact2]);
```

### Delete

```dart
await FlutterContacts.delete(id);
await FlutterContacts.deleteAll([id1, id2, id3]);
```

---

## 🔧 Feature APIs

<details>
<summary><b>🔐 Permissions</b></summary>

```dart
bool has = await FlutterContacts.permissions.has(PermissionType.read);
PermissionStatus status = await FlutterContacts.permissions.check(PermissionType.read);
PermissionStatus result = await FlutterContacts.permissions.request(PermissionType.readWrite);
await FlutterContacts.permissions.openSettings();
```

**Types:** `PermissionType.read`, `.write`, `.readWrite` (on iOS, all are equivalent).

</details>

<details>
<summary><b>👤 Accounts</b></summary>

```dart
List<Account> accounts = await FlutterContacts.accounts.getAll();
Account? defaultAccount = await FlutterContacts.accounts.getDefault();
await FlutterContacts.accounts.showDefaultPicker(); // Android only
```

</details>

<details>
<summary><b>👥 Groups</b></summary>

```dart
Group? group = await FlutterContacts.groups.get(groupId, withContactCount: true);
List<Group> groups = await FlutterContacts.groups.getAll(accounts: [account]);
Group created = await FlutterContacts.groups.create('Family', account: account);
await FlutterContacts.groups.update(group);
await FlutterContacts.groups.delete(groupId);
await FlutterContacts.groups.addContacts(groupId: groupId, contactIds: [id1, id2]);
await FlutterContacts.groups.removeContacts(groupId: groupId, contactIds: [id1]);
List<Group> contactGroups = await FlutterContacts.groups.getOf(contactId);
```

</details>

<details>
<summary><b>📇 vCard</b></summary>

```dart
String vCard = FlutterContacts.vCard.export(contact, version: VCardVersion.v3);
String vCards = FlutterContacts.vCard.exportAll([contact1, contact2]);
List<Contact> contacts = FlutterContacts.vCard.import(vCardString);
```

Supports vCard 2.1, 3.0, and 4.0.

</details>

<details>
<summary><b>🎨 Native Dialogs</b> (Android & iOS)</summary>

System UI for picking, viewing, editing, and creating contacts.

```dart
Contact? picked = await FlutterContacts.native.showPicker(
  properties: {ContactProperty.phone},
);
await FlutterContacts.native.showViewer(contactId);
String? editedId = await FlutterContacts.native.showEditor(contactId);
String? createdId = await FlutterContacts.native.showCreator(contact: prefill);
```

Permissions:

- `showPicker()` and `showCreator()` need no contacts permission on either platform.
- `showPicker(properties: …)` is permissionless on iOS. On Android it requires `READ_CONTACTS` and throws `PlatformException` if missing.
- `showViewer()` and `showEditor()` are permissionless on Android. On iOS they require `NSContactsUsageDescription` in `Info.plist` and a granted read permission.

</details>

<details>
<summary><b>🔔 Change Listeners</b></summary>

```dart
FlutterContacts.onDatabaseChange.listen((_) => refreshUI());

FlutterContacts.onContactChange.listen((changes) {
  for (final change in changes) {
    print('${change.type}: ${change.contactId}');
  }
});
```

</details>

<details>
<summary><b>📱 SIM Contacts</b> (Android only)</summary>

```dart
List<Contact> simContacts = await FlutterContacts.sim.get();
```

SIM contacts are read-only and typically contain only name and phone.

</details>

<details>
<summary><b>👤 Profile / "Me" Card</b> (Android & macOS)</summary>

```dart
Contact? me = await FlutterContacts.profile.get(properties: ContactProperties.all);
```

Returns the device owner's profile (Android) or "Me" card (macOS). Not available on iOS.

</details>

<details>
<summary><b>🚫 Blocked Numbers</b> (Android only)</summary>

Requires app to be default phone app. See [example manifest](https://github.com/QuisApp/flutter_contacts_example/blob/master/android/app/src/main/AndroidManifest.xml).

```dart
if (await FlutterContacts.blockedNumbers.isAvailable()) {
  bool isBlocked = await FlutterContacts.blockedNumbers.isBlocked('+1234567890');
  List<Phone> blocked = await FlutterContacts.blockedNumbers.getAll();
  await FlutterContacts.blockedNumbers.block('+1234567890');
  await FlutterContacts.blockedNumbers.blockAll([number1, number2]);
  await FlutterContacts.blockedNumbers.unblock('+1234567890');
  await FlutterContacts.blockedNumbers.unblockAll([number1, number2]);
  await FlutterContacts.blockedNumbers.openDefaultAppSettings();
}
```

</details>

<details>
<summary><b>🔔 Ringtones</b> (Android only)</summary>

```dart
String? uri = await FlutterContacts.ringtones.pick(type: RingtoneType.ringtone);
Ringtone? ringtone = await FlutterContacts.ringtones.get(uri, withMetadata: true);
List<Ringtone> all = await FlutterContacts.ringtones.getAll(type: RingtoneType.ringtone);
String? defaultUri = await FlutterContacts.ringtones.getDefaultUri(RingtoneType.ringtone);
await FlutterContacts.ringtones.setDefaultUri(RingtoneType.ringtone, uri);
await FlutterContacts.ringtones.play(uri);
await FlutterContacts.ringtones.stop();
```

</details>

<details>
<summary><b>⚙️ Configuration</b></summary>

```dart
FlutterContacts.config.enableIosNotes = true; // Requires iOS entitlement
```

</details>

---

## 📋 Data Model

> **See [lib/models/](https://github.com/QuisApp/flutter_contacts/tree/master/lib/models) for complete data model documentation.**

A `Contact` contains an `id`, an auto-generated `displayName`, and lists of properties (`phones`, `emails`, `addresses`, etc.). Many properties support native labels (home, work, mobile, etc.) and custom labels.

<details>
<summary><b>Contact Structure</b></summary>

```dart
class Contact {
  final String? id;                         // Stable identifier
  final String? displayName;                // Read-only, auto-generated
  final Photo? photo;
  final Name? name;
  final List<Phone> phones;
  final List<Email> emails;
  final List<Address> addresses;
  final List<Organization> organizations;
  final List<Website> websites;
  final List<SocialMedia> socialMedias;
  final List<Event> events;                 // Birthdays, anniversaries
  final List<Relation> relations;
  final List<Note> notes;

  // Android-specific fields: isFavorite, customRingtone,
  // sendToVoicemail, timestamp, identifiers
  final AndroidData? android;
}
```

</details>

<details>
<summary><b>Property Types</b></summary>

```dart
// Name
Name(
  first: 'John',
  last: 'Smith',
  middle: 'Q',
  prefix: 'Dr.',
  suffix: 'Jr.',
)

// Phone, Email, Address
Phone(number: '555-1234')
Phone(number: '555-1234', label: Label(PhoneLabel.mobile))
Email('john@example.com', label: Label(EmailLabel.work))
Address(
  street: '123 Main St',
  city: 'New York',
  state: 'NY',
  postalCode: '10001',
  label: Label(AddressLabel.home),
)

// Organization
Organization(
  organizationName: 'FlutterCorp',
  jobTitle: 'Software Engineer',
  departmentName: 'Engineering',
)

// Photo
Photo(fullSize: imageBytes)
```

> **Address Usage:** Prefer component fields (`street`, `city`, `state`, etc.) when creating/editing. The `formatted` field is available when reading and is guaranteed to be present.

> **Photo Usage:** When creating/updating, set `Photo(fullSize: imageBytes)` - the system automatically generates the thumbnail. When reading, fetch `ContactProperty.photoThumbnail` (fast) for contact lists, `ContactProperty.photoFullRes` (slower) for detail views.

> **Note:** The examples above show common fields. See [lib/models/](https://github.com/QuisApp/flutter_contacts/tree/master/lib/models) for the complete list of all available fields and properties.

</details>

<details>
<summary><b>Labels</b></summary>

Most properties support **labels** (home, work, mobile, etc.) and **custom labels**:

```dart
// Standard label
Phone(number: '555-1234', label: Label(PhoneLabel.mobile))

// Custom label
Phone(number: '555-1234', label: Label(PhoneLabel.custom, 'Emergency'))
```

**Platform Support:** Some labels are platform-specific (e.g., `PhoneLabel.appleWatch` is iOS-only, `PhoneLabel.workMobile` is Android-only). Unsupported labels are automatically converted to custom labels with the original name preserved. On iOS/macOS, Apple's built-in labels that have no enum value (mostly relation labels) are read as custom labels in Apple's raw form, e.g. `_$!<ElderBrother>!$_`, so saving the contact keeps them intact. Strip the `_$!<` and `>!$_` markers to display them, and keep `customLabel` unchanged when saving.

</details>

---

## ⚠️ Platform Limitations

<details>
<summary><b>iOS & macOS</b></summary>

**Not Available:**

- APIs: `ringtones`, `blockedNumbers`, `sim`, `profile` (iOS), `native` (macOS)
- Properties: `favorite`, `ringtone`, `sendToVoicemail`, `timestamp`, `identifiers`, `debugData` (all nested in `android` field)
- Fields: `Phone.isPrimary`, `Phone.normalizedNumber`, `Email.isPrimary`, `Address.poBox`, `Address.neighborhood`, `Organization.jobDescription`, `Organization.symbol`, `Organization.officeLocation`
- Some Android-specific labels (auto-converted to custom)

**Limits:** One organization, one note, one birthday per contact.

**Filtering:** Phone and email filters only support full match (not partial).

**iOS Notes:** Requires the `com.apple.developer.contacts.notes` entitlement and `FlutterContacts.config.enableIosNotes = true`. See [Apple's documentation](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.contacts.notes).

</details>

<details>
<summary><b>Android</b></summary>

**Not Available:**

- Fields: `Name.previousFamilyName`, `Address.isoCountryCode`, `Address.subAdministrativeArea`, `Address.subLocality`
- Some iOS-specific labels (auto-converted to custom)

**Special Features:**

- **SIM Contacts:** Read-only, typically name + phone only
- **Blocked Numbers:** Requires app to be default phone app

</details>

---

## 🔄 Migrating from v1

If you're using [flutter_contacts v1](https://pub.dev/packages/flutter_contacts/versions/1.1.9+2):

| v1                                     | v2                                                              |
| -------------------------------------- | --------------------------------------------------------------- |
| `FlutterContacts.getContacts()`        | `FlutterContacts.getAll()`                                      |
| `FlutterContacts.getContact(id)`       | `FlutterContacts.get(id)`                                       |
| `contact.insert()`                     | `FlutterContacts.create(contact)`                               |
| `FlutterContacts.requestPermission()`  | `FlutterContacts.permissions.request(PermissionType.readWrite)` |
| `FlutterContacts.addListener()`        | `FlutterContacts.onDatabaseChange.listen()`                     |
| `contact.toVCard()`                    | `FlutterContacts.vCard.export(contact)`                         |
| `PhoneLabel.mobile`                    | `Label(PhoneLabel.mobile)`                                      |
| `FlutterContacts.openExternalView(id)` | `FlutterContacts.native.showViewer(id)`                         |
| `FlutterContacts.openExternalPick()`   | `FlutterContacts.native.showPicker()`                           |
| `FlutterContacts.openExternalEdit(id)` | `FlutterContacts.native.showEditor(id)`                         |
| `FlutterContacts.openExternalInsert()` | `FlutterContacts.native.showCreator()`                          |

---

## 🤝 Contributing

We'd love your help making this plugin even better. Found a bug? Have a feature idea? Want to improve the docs?

- 🐛 [Report an issue](https://github.com/QuisApp/flutter_contacts/issues)
- 💡 [Suggest a feature](https://github.com/QuisApp/flutter_contacts/issues)
- 🔧 [Submit a pull request](https://github.com/QuisApp/flutter_contacts/pulls)
- 📖 Improve the documentation

Every contribution, big or small, makes a difference. Thank you for helping make Flutter Contacts better for everyone.

---

## 📄 License

MIT License - see the [LICENSE](LICENSE) file for details.

---

## 💝 Support

Flutter Contacts is maintained in my free time. If this plugin has been helpful for your project, consider supporting its development. Your support helps me continue maintaining and improving it.

[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/G2G11OWJA4)

---

## 🙏 Acknowledgments

Special thanks to all contributors and users who have helped shape this plugin.

---

<div align="center">

**Made with ❤️ for the Flutter community**

[⭐ Star us on GitHub](https://github.com/QuisApp/flutter_contacts) | [📖 Documentation](https://pub.dev/documentation/flutter_contacts/latest/) | [🐛 Report Issues](https://github.com/QuisApp/flutter_contacts/issues)

</div>

## Changelog
## 2.6.0

- Fix Android's contacts provider crashing (and photos being lost) when many
  photos are written at once, e.g. `createAll()` with photos. Bulk photo writes
  are also several times faster, and photos are visible when the call returns.
- **Behavior change:** `get()` returns `null` for a missing contact instead of
  throwing.
- **Behavior change:** iOS/macOS labels without a mapping read back as
  `Label(XLabel.custom, 'text')`, as on Android.
- Fix `ContactFilter.ids([])` throwing on iOS/macOS.
- Fix `groups.get()` returning deleted groups on Android.
- Blocked-number calls refused by Android now throw `security_error`, as
  documented.
- Fix `showCreator()` prefill on Android for phonetic names, nicknames and
  addresses.

## 2.5.0

- `update()` and `updateAll()` now throw `read_only_contact` instead of silently
  dropping properties owned by a sync adapter, which Android's provider filters
  out of the write — thanks @yassinsameh for the request (#95, #260).

## 2.4.1

- Reject a second `showCreator()` call on iOS while one is already presented,
  which previously left the first call's future unresolved.
- Document that `showEditor()` also returns null when the contact is deleted
  from the system editor, not only when the user cancels.

## 2.4.0

- `showEditor()` on iOS now waits for the user to finish instead of resolving to
  `null` immediately; it returns the contact identifier on save.
- Fix Android dropping events whose date uses ISO 8601 basic format
  (`19960415`, `--0415`), which vCard 4.0 mandates and Android's vCard importer
  stores verbatim — thanks @Ruman87 for the report and diagnosis (#248).
- Fix `groups.addContacts()` failing with `Group has no account` on Android 12
  and below, where the platform exposes no default account and `groups.create()`
  produces a local group — thanks @Eunno-An for the report (#247).
- Fix the iOS native editor staying on screen when the contact is deleted from
  it (iOS 26+) — thanks @liliana-tagonsoft for the report and the fix (#250).

## 2.3.1

- Fix `showCreator()` name prefill not displaying in some Android contact
  editors (e.g. Samsung) by passing `Insert.NAME` instead of a `StructuredName`
  row — thanks @CrashKiki for the fix (#241) and @Eunno-An for the report
  (#242).

## 2.3.0

- Android now returns the same contacts the system Contacts app displays,
  instead of filtering on `IN_VISIBLE_GROUP = 1`: contacts from sync adapters
  that don't mark them visible (e.g. MS365/Exchange) are included in `getAll()`,
  operations by ID work regardless of visibility, and filters matching no
  contacts return none instead of all (#240).
- Fix `showCreator()` dropping the pre-filled photo on Android: the system
  editor ignores photos passed through the insert intent, so the photo is now
  applied to the contact after creation when the app holds `WRITE_CONTACTS`
  (#242).

## 2.2.2

- Update README.

## 2.2.1

- Fix name updates writing to the wrong raw contact on Android contacts with
  multiple raw contacts (e.g. Google + WhatsApp): the name is now updated in
  place on the raw contact that already holds it, instead of the raw contact
  with the most properties (#236).

## 2.2.0

- Round-trip unknown vCard properties via `contact.extras` (#21).
- Migrate Android plugin to Built-in Kotlin; requires Flutter 3.44+ (#234).

## 2.1.0

- `FlutterContacts.native.showPicker()` now returns `Contact?` (with `id` and
  `displayName` always populated) and accepts a `properties:` set for additional
  fields. The picker itself remains permissionless on both platforms; on Android,
  requesting `properties` requires `READ_CONTACTS` and throws otherwise.
  **Breaking:** replace `await …showPicker()` with `(await …showPicker())?.id` (#232).
- Dedupe phone numbers and emails returned by aggregated contacts on Android,
  keyed on normalized value + label (#229).

## 2.0.2

- Fix Flutter SDK resolution for FVM and IDE sync compatibility (#227)

## 2.0.1

- Fix Android build failure from hardcoded Flutter SDK path (#223)

## 2.0.0

- Stable 2.0 release. Includes all changes from 2.0.0-beta.1 through 2.0.0-beta.5.

## 2.0.0-beta.5

- Relax Dart/Flutter version requirements
- Fix for older Android versions without Settings.getDefaultAccount()

## 2.0.0-beta.4

- Migrate to Swift Package Manager
- Migrate to UISceneDelegate

## 2.0.0-beta.3

- Fix for #214
- Disable Swift Package Manager for example app

## 2.0.0-beta.2

- Expose Android identifiers and timestamp
- Add `androidLookup` parameter to `get()`

## 2.0.0-beta.1

- Complete rewrite with improved architecture and performance
- Full support for Android, iOS, and macOS platforms
- Added support for all contact fields including groups, accounts, and metadata
- vCard import/export support (v2.1, v3.0, v4.0)
- Native dialogs for contact creation, editing, viewing, and picking
- Contact change listeners for real-time updates
- SIM contacts support (Android)
- Number blocking support (Android)
- Ringtone management (Android)
- Profile/Me card support
- Bulk operations for creating, updating, and deleting contacts
- Account-aware contact management

## 1.1.9+2

- Fix wrong namespace on android (https://github.com/QuisApp/flutter_contacts/issues/171) - thanks mattmapadmi

## 1.1.9+1

- Fix openExternalInsert returning null on android (https://github.com/QuisApp/flutter_contacts/issues/100, https://github.com/QuisApp/flutter_contacts/issues/161)

## 1.1.9

- Fix kotlin/android incompability issues with java 1.8 and java 17 (https://github.com/QuisApp/flutter_contacts/issues/142, https://github.com/QuisApp/flutter_contacts/issues/147, https://github.com/QuisApp/flutter_contacts/issues/158, https://github.com/QuisApp/flutter_contacts/issues/169, https://github.com/QuisApp/flutter_contacts/pull/170) - thanks to Valent1, Hirohumi, himsarevenus, PcolBP, and other contributors
- Fix openExternalInsert() crash - thanks rahulraj-idt

## 1.1.8

- Upgrade gradle, add null-safety in example_full.

## 1.1.7+1

- Fix for AGP <4.2 (https://github.com/QuisApp/flutter_contacts/issues/127) - thanks trfiladelfo

## 1.1.7

- Add feature to pre-populate fields in openExternalInsert() - thanks sakchhams
- Fix openExternalView and openExternalEdit on iOS (https://github.com/QuisApp/flutter_contacts/issues/91, https://github.com/QuisApp/flutter_contacts/issues/70)
- Support Gradle 8 (https://github.com/QuisApp/flutter_contacts/issues/123)
- Fix fetching notes on iOS - thanks starshipcoder and yassinsameh
- Fix bug with address label default on iOS - thanks MohamedAl-Kainai and yassinsameh

## 1.1.6

- Update kotlin/gradle versions (https://github.com/QuisApp/flutter_contacts/issues/69)

## 1.1.5+1

- Fix null pointer error (https://github.com/QuisApp/flutter_contacts/pull/71 - thanks anggrayudi)
- Update README

## 1.1.5

- Edit groups (iOS) / labels (Android) and group/label membership

## 1.1.4

- Fix gradle compile error (https://github.com/QuisApp/flutter_contacts/issues/49)

## 1.1.3

- Fix social media custom label bug (https://github.com/QuisApp/flutter_contacts/issues/42)

## 1.1.2

- Read/write starred contacts on Android (https://github.com/QuisApp/flutter_contacts/issues/37)
- Fix vCard photo encoding (https://github.com/QuisApp/flutter_contacts/issues/34)

## 1.1.1+2

- Update comments

## 1.1.1+1

- Update README

## 1.1.1

- Fetch groups (iOS) / labels (Android) and containers (iOS) / accounts (Android) (https://github.com/QuisApp/flutter_contacts/issues/29)
- Ability to request read-only permissions (https://github.com/QuisApp/flutter_contacts/issues/25)

## 1.1.0+4

- Load rawContact instead of contactId after update, since Android sometimes changes the contactId

## 1.1.0+3

- Fix withThumbnail on iOS

## 1.1.0+2

- Fix hashCode and ==

## 1.1.0+1

- Fix a diacritics bug

## 1.1.0

- Add ability to open external contact app to view, edit, pick or insert contacts (https://github.com/QuisApp/flutter_contacts/issues/16)

## 1.0.0+1

- Fix for permission handler on Android (https://github.com/QuisApp/flutter_contacts/pull/17) - thanks @scroollocker
- Fix type cast error on iOS 14.5 (https://github.com/QuisApp/flutter_contacts/issues/19) - thanks @jadasi

## 1.0.0

- Stable release 🎉
- Follow-up fix for https://github.com/QuisApp/flutter_contacts/issues/9
- Fix https://github.com/QuisApp/flutter_contacts/issues/14

## 0.3.3+1

- Fix https://github.com/QuisApp/flutter_contacts/issues/9

## 0.3.3

- Fix lint warning

## 0.3.2

- Remove unused dependency

## 0.3.1

- Change Dart SDK version requirements

## 0.3.0

- Migrated to null-safety

## 0.2.2

- Support for requesting permissions
- Option to return non-visible contacts on Android and raw contacts on Android/iOS
  (https://github.com/QuisApp/flutter_contacts/issues/5)

## 0.2.1

- Format

## 0.2.0

- Not backward compatible!
- Photo and thumbnail are now distinct fields
- `Event.date` is now `Event.year`, `Event.month`, `Event.day`
- Convenience methods `contact.insert()`, `contact.update()`, `contact.delete()`
- Much more advanced vCard parsing and exporting (to version 3.0 and 4.0)
- We can now add multiple listeners
- Removed dependencies to `build_runner` and `json_serializable`
- Implemented `toString()`, `hashCode` and `operator==` for contact and properties
- Support for notes on iOS13+ by setting
  `FlutterContacts.config.includeNotesOnIos13AndAbove = true`
- Added tests

## 0.1.3

- Remove extra print statements

## 0.1.2

- Properly delete events on iOS (https://github.com/QuisApp/flutter_contacts/issues/2)

## 0.1.1

- Fix date serialization on Android (https://github.com/QuisApp/flutter_contacts/issues/2)

## 0.1.0

- Support for vCard parsing and exporting
- Add `pedantic` static analyzer
- Make default values non-const so they can be mutated
- Add tests
- Update dependencies
- Deduplicate events
- Rename `socialMedia.dart` -> `social_media.dart`

## 0.0.7

- Bug fix with normalized phone number

## 0.0.6

- Added normalized phone numbers (android only)
- Deduplicate phone numbers and emails by default

## 0.0.5

- Improved README and dartdoc.
- Added option to `getFullContacts()` with high-res photos.
- Fixed bug on iOS where low-res photos were fetched instead of high-res and vice-versa.
- Better Kotlin null safety.

## 0.0.4

- `newContact()` now returns the full contact instead of ID / raw ID.

## 0.0.3

- Fix README typos.

## 0.0.2

- Fix support for iOS.

## 0.0.1

- Initial release.







 
