toVCard method

String toVCard({
  1. bool withPhoto = true,
  2. String? productId,
  3. bool includeDate = false,
})

Exports to vCard format.

By default we use vCard format v3 (https://tools.ietf.org/html/rfc2426) which is the most widely used, but it's possible to use the more recent vCard format v4 (https://tools.ietf.org/html/rfc6350) using:

FlutterContacts.config.vCardVersion = VCardVersion.v4;

The optional productId specifies a product identifier, such as "-//Apple Inc.//Mac OS X 10.15.7//EN"

Implementation

String toVCard({
  bool withPhoto = true,
  String? productId,
  bool includeDate = false,
}) {
  // BEGIN (V3): https://tools.ietf.org/html/rfc2426#section-2.1.1
  // VERSION (V3): https://tools.ietf.org/html/rfc2426#section-3.6.9
  // PRODID (V3): https://tools.ietf.org/html/rfc2426#section-3.6.3
  // REV (V3): https://tools.ietf.org/html/rfc2426#section-3.6.4
  // FN (V3): https://tools.ietf.org/html/rfc2426#section-3.1.1
  // PHOTO (V3): https://tools.ietf.org/html/rfc2426#section-3.1.4
  // END (V3): https://tools.ietf.org/html/rfc2426#section-2.1.1
  // BEGIN (V4): https://tools.ietf.org/html/rfc6350#section-6.1.1
  // VERSION (V4): https://tools.ietf.org/html/rfc6350#section-6.7.9
  // PRODID (V4): https://tools.ietf.org/html/rfc6350#section-6.7.3
  // REV (V4): https://tools.ietf.org/html/rfc6350#section-6.7.4
  // FN (V4): https://tools.ietf.org/html/rfc6350#section-6.2.1
  // PHOTO (V4): https://tools.ietf.org/html/rfc6350#section-6.2.4
  // END (V4): https://tools.ietf.org/html/rfc6350#section-6.1.2
  final v4 = FlutterContacts.config.vCardVersion == VCardVersion.v4;
  var lines = [
    'BEGIN:VCARD',
    v4 ? 'VERSION:4.0' : 'VERSION:3.0',
  ];
  if (productId != null) {
    lines.add('PRODID:$productId');
  }
  if (includeDate) {
    lines.add('REV:${DateTime.now().toIso8601String()}');
  }
  if (displayName.isNotEmpty) {
    lines.add('FN:${vCardEncode(displayName)}');
  }
  if (withPhoto && photoOrThumbnail != null) {
    final encoding = vCardEncode(base64.encode(photoOrThumbnail!));
    final prefix =
        v4 ? 'PHOTO:data:image/jpeg;base64,' : 'PHOTO;ENCODING=b;TYPE=JPEG:';
    // 63 chars on each line.
    final prefixLength = prefix.length;
    lines.add(prefix +
        encoding.substring(0, min(63 - prefixLength, encoding.length)));
    // Subsequent lines have a leading space, and 62 chars each.
    var index = prefixLength;
    while (index < encoding.length) {
      lines.add(
          ' ' + encoding.substring(index, min(index + 62, encoding.length)));
      index += 62;
    }
  }
  lines.addAll([
    name.toVCard(),
    phones.map((x) => x.toVCard()).expand((x) => x),
    emails.map((x) => x.toVCard()).expand((x) => x),
    addresses.map((x) => x.toVCard()).expand((x) => x),
    organizations.map((x) => x.toVCard()).expand((x) => x),
    websites.map((x) => x.toVCard()).expand((x) => x),
    socialMedias.map((x) => x.toVCard()).expand((x) => x),
    events.map((x) => x.toVCard()).expand((x) => x),
    notes.map((x) => x.toVCard()).expand((x) => x),
  ].expand((x) => x));
  lines.add('END:VCARD');
  return lines.join('\n');
}