contacts_kit
flutter_contacts for
DartNative: the same FlutterContacts API, models
and vCard support, with the Flutter platform channels replaced by an FFI
channel into the same Swift and Kotlin handlers.
import 'package:contacts_kit/contacts_kit.dart';
final status = await FlutterContacts.permissions.request(PermissionType.read);
final contacts = await FlutterContacts.getAll(
properties: {ContactProperty.name, ContactProperty.phone},
);
final picked = await FlutterContacts.native.showPicker(
properties: {ContactProperty.phone, ContactProperty.email},
);
The API is flutter_contacts 2.6.0's, unchanged: see its documentation for
every call. PlatformException is this package's own class with the same
fields.
Setup
dependencies:
contacts_kit: ^0.1.0
Run dn pub get; it adds ContactsKitFFIBindings.loadSymbols() to the
app's plugin registrant.
- iOS:
NSContactsUsageDescriptioninios/Runner/Info.plist. - Android:
READ_CONTACTSand/orWRITE_CONTACTSin the app manifest. The picker itself needs neither; reading the picked contact's fields does.
How the port works
| Layer | What changed |
|---|---|
| Dart | lib/api, lib/models, lib/src/vcard are the originals. Their one Flutter import now points at lib/src/channel/services.dart, a MethodChannel / EventChannel / PlatformException with the same shapes over FfiChannel (JSON strings, one dispatcher pointer, tokens). |
| iOS | ios/Classes are the original Swift handlers minus import Flutter. FlutterShim.swift defines FlutterMethodCall, FlutterResult, FlutterError, FlutterMethodNotImplemented, FlutterEventSink, FlutterStreamHandler. ContactsKit.swift holds the @_cdecl entry points, the dispatcher slot and the "domain.method" router. |
| Android | android/.../co/quis/flutter_contacts are the original Kotlin handlers. com.kluivert.contactskit.shim holds MethodCall, MethodChannel.Result, EventChannel, PluginRegistry and ActivityPluginBinding copies (the engine's own classes exist at runtime and must not be shadowed). ContactsKitBridge.kt routes calls, ContactsKitProxyActivity owns the picker/editor/creator results and the permission dialog, because the DartNative embedding has no activity-result hooks. The five call sites that started an activity for a result now go through the proxy. |
Replies travel as JSON, so a contact photo is a base64 string on the wire, exactly as flutter_contacts already encodes it.
Deviations from flutter_contacts
ViewControllerUtils.rootViewController()returns the top-most presented controller, so the picker opens above a DartNative sheet.- On Android the four activity-for-result calls and the permission request go through
ContactsKitProxyActivity; the ported handlers no longer require a host activity.
Verified
- Both platforms:
permissions.request,getAll,create→get→delete, vCard export/import (the example's autorun). - iOS:
native.showPickerfrom inside a DartNative sheet, result filled into a field (Kept). - Android:
native.showPickerround trip through the proxy activity.
Not verified yet
The viewer, editor and creator, the change listeners, groups, accounts, SIM, blocked numbers and ringtones are ported as-is and untested.
Example
example/ is a dn create app. dn run --dart-define=CONTACTS_KIT_AUTORUN=true
runs permission → getAll → create/get/delete → vCard at startup and mirrors
the results into the dn run log as [example] lines.