Giter VIP home page Giter VIP logo

flutter_contacts's Introduction

contacts_service

pub package Build Status Coverage Status

A Flutter plugin to access and manage the device's contacts.

Usage

To use this plugin, add contacts_service as a dependency in your pubspec.yaml file.
For example:

dependencies:  
    contacts_service: ^0.6.3

Permissions

Android

Add the following permissions to your AndroidManifest.xml:

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

iOS

Set the NSContactsUsageDescription in your Info.plist file

<key>NSContactsUsageDescription</key>  
<string>This app requires contacts access to function properly.</string>  

Note
contacts_service does not handle the process of asking and checking for permissions. To check and request user permission to access contacts, try using the following plugins: flutter_simple_permissions or permission_handler.

If you do not request user permission or have it granted, the application will fail. For testing purposes, you can manually set the permissions for your test app in Settings for your app on the device that you are using. For Android, go to "Settings" - "Apps" - select your test app - "Permissions" - then turn "on" the slider for contacts.

Example

// Import package  
import 'package:contacts_service/contacts_service.dart';  
  
// Get all contacts on device
List<Contact> contacts = await ContactsService.getContacts();  

// Get all contacts without thumbnail (faster)
List<Contact> contacts = await ContactsService.getContacts(withThumbnails: false);

// Android only: Get thumbnail for an avatar afterwards (only necessary if `withThumbnails: false` is used)
Uint8List avatar = await ContactsService.getAvatar(contact);
  
// Get contacts matching a string
List<Contact> johns = await ContactsService.getContacts(query : "john");

// Add a contact  
// The contact must have a firstName / lastName to be successfully added  
await ContactsService.addContact(newContact);  
  
// Delete a contact
// The contact must have a valid identifier
await ContactsService.deleteContact(contact);  

// Update a contact
// The contact must have a valid identifier
await ContactsService.updateContact(contact);

// Usage of the native device form for creating a Contact
// Throws a error if the Form could not be open or the Operation is canceled by the User
await ContactsService.openContactForm();

// Usage of the native device form for editing a Contact
// The contact must have a valid identifier
// Throws a error if the Form could not be open or the Operation is canceled by the User
await ContactsService.openExistingContact(contact);

Contact Model

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

// Company
String company, jobTitle;

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

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

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

// Contact avatar/thumbnail
Uint8List avatar;

Example

Contributions

Contributions are welcome! If you find a bug or want a feature, please fill an issue.

If you want to contribute code please create a pull request under the staging branch.

Credits

Heavily inspired from rt2zz's react native plugin

flutter_contacts's People

Contributors

abion47 avatar chanakaweerasinghe avatar creativepsyco avatar dakaugu avatar dgp1130 avatar eli1stark avatar engylemure avatar faiyyazauxy avatar fjbatresv avatar imanneo avatar joachimvalente avatar kmccmk9 avatar lidongze91 avatar lukasgit avatar mehmetf avatar panzhiev-timur avatar pavanbuzz avatar sgehrman avatar sperochon avatar tryneeth avatar victoruvarov avatar zaharl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

flutter_contacts's Issues

App crashes while adding multiple contacts

Hi, i want to add 300 contacts within loop.

Here is my code

ls.forEach((f) async {
       f["avatar"] = null;
      ContactsService.addContact(Contact.fromMap(f));
  });

Contacts adding successfully but app crashes while adding. Loader widget not working and Android occurs "This app is not responding" alert.

Load account identifier as part of Contact

At least on Android, each Contact is linked to an account (e.g. Google, Exchange or DAV). Currently it is neither visible e.g. as a field in Contact, much less updateable.

From previous Android experience, I know it is a pain in the ass -- it is not possible to just change the account, you'd have to delete the contact from one account and add it to the next. Still, being able to choose the account at least at creation is important, e.g. to sync the contact to the right Google/Exchange/davx account.

ContactsService.addContact to return id/displayName

Not sure if possible with Android/iOS, but I would appreciate if ContactsService.addContact(contact) would:

  • set contact.id and contact.displayName as generated upon insertion
  • or return a Future<Contact> with the newly inserted contact including id and displayName

My use case is adding a new contact and then immediately switching to the contact update page (or rather going add -> detail -> update). To call ContactsService.updateContact(contact), contact.id must be set but I do not know it a priori after adding.

My current workaround is to reload the contact with ContactsService.getContacts(query = [contact.givenName, contact.middleName, contact.familyName].where((s) => s != null).join (" ")) but this fails e.g. if no familyName is set. Loading all contacts and then searching manually would also be possible but time consuming.

In summary, getting the id/displayName from addContact would be a much cleaner solution, and I would much prefer it to my current, somewhat hacky, workaround.

Delay while loading contacts

I am having a problem loading my contact list of about 2113 contacts, It is taking the plugin example app a whole 10 min to display my contacts. I just want to know whether there is a work around for accessing my contacts directly from my phonebook.

Add Contacts Doesn't Work

Hi @lukasgit please help!

I don't know who else to go to
but add contacts doesn't work anymore

I get this error
"Exception has occurred.
FormatException (FormatException: Invalid envelope)"
in the main console window

and this in the debug output

I'm using VS Code and a modified version of your example file
the modified version uses a plugin that actually works

its the permission plugin

    
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:contacts_service/contacts_service.dart';
import 'package:not_at_home/helper.dart';
import 'package:permission/permission.dart';
import 'package:permission/permission.dart';

void main() => runApp(ContactsExampleApp());

class ContactsExampleApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ContactListPage(),
      routes: <String, WidgetBuilder>{
        '/add': (BuildContext context) => AddContactPage()
      },
    );
  }
}

class ContactListPage extends StatefulWidget {
  @override
  _ContactListPageState createState() => _ContactListPageState();
}

class _ContactListPageState extends State<ContactListPage> {
  Iterable<Contact> _contacts;

  @override
  initState() {
    super.initState();
    refreshContacts();
  }

  refreshContacts() async {
    PermissionStatus permissionStatus = await _getContactPermission();
    if (isAuthorized(permissionStatus)) {
      var contacts = await ContactsService.getContacts();
//      var contacts = await ContactsService.getContactsForPhone("8554964652");
      setState(() {
        _contacts = contacts;
      });
    } else {
      _handleInvalidPermissions(permissionStatus);
    }
  }

  updateContact() async {
    Contact ninja = _contacts.toList().firstWhere((contact) => contact.familyName.startsWith("Ninja"));
    ninja.avatar = null;
    await ContactsService.updateContact(ninja);

    refreshContacts();
  }
  
  Future<PermissionStatus> _getContactPermission() async {
    PermissionStatus permission = (await Permission.getPermissionsStatus([PermissionName.Contacts]))[0].permissionStatus;
    if (isAuthorized(permission) == false) {
      PermissionStatus permissionStatus = (await Permission.requestPermissions([PermissionName.Contacts]))[0].permissionStatus;
      return permissionStatus;
    } else {
      return permission;
    }
  }

  void _handleInvalidPermissions(PermissionStatus permissionStatus) {
    if (isAuthorized(permissionStatus) == false) {
      throw new PlatformException(
          code: "PERMISSION_DENIED",
          message: "Access to location data denied",
          details: null);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Contacts Plugin Example')),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          Navigator.of(context).pushNamed("/add").then((_) {
            refreshContacts();
          });
        },
      ),
      body: SafeArea(
        child: _contacts != null
            ? ListView.builder(
          itemCount: _contacts?.length ?? 0,
          itemBuilder: (BuildContext context, int index) {
            Contact c = _contacts?.elementAt(index);
            return ListTile(
              onTap: () {
                Navigator.of(context).push(MaterialPageRoute(
                    builder: (BuildContext context) =>
                        ContactDetailsPage(c)));
              },
              leading: (c.avatar != null && c.avatar.length > 0)
                  ? CircleAvatar(backgroundImage: MemoryImage(c.avatar))
                  : CircleAvatar(child: Text(c.initials())),
              title: Text(c.displayName ?? ""),

            );
          },
        )
            : Center(child: CircularProgressIndicator(),),
      ),
    );
  }
}

class ContactDetailsPage extends StatelessWidget {
  ContactDetailsPage(this._contact);
  final Contact _contact;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_contact.displayName ?? ""),
        actions: <Widget>[
//          IconButton(
//            icon: Icon(Icons.share),
//            onPressed: () => shareVCFCard(context, contact: _contact),
//          ),
          IconButton(
            icon: Icon(Icons.delete),
            onPressed: () => ContactsService.deleteContact(_contact),
          ),
          IconButton(
            icon: Icon(Icons.update),
            onPressed: () => Navigator.of(context).push(
              MaterialPageRoute(builder: (context) => UpdateContactsPage(contact: _contact,),),
            ),
          ),
        ],
      ),
      body: SafeArea(
        child: ListView(
          children: <Widget>[
            ListTile(
              title: Text("Name"),
              trailing: Text(_contact.givenName ?? ""),
            ),
            ListTile(
              title: Text("Middle name"),
              trailing: Text(_contact.middleName ?? ""),
            ),
            ListTile(
              title: Text("Family name"),
              trailing: Text(_contact.familyName ?? ""),
            ),
            ListTile(
              title: Text("Prefix"),
              trailing: Text(_contact.prefix ?? ""),
            ),
            ListTile(
              title: Text("Suffix"),
              trailing: Text(_contact.suffix ?? ""),
            ),
            ListTile(
              title: Text("Company"),
              trailing: Text(_contact.company ?? ""),
            ),
            ListTile(
              title: Text("Job"),
              trailing: Text(_contact.jobTitle ?? ""),
            ),
            ListTile(
              title: Text("Note"),
              trailing: Text(_contact.note ?? ""),
            ),
            AddressesTile(_contact.postalAddresses),
            ItemsTile("Phones", _contact.phones),
            ItemsTile("Emails", _contact.emails)
          ],
        ),
      ),
    );
  }
}

class AddressesTile extends StatelessWidget {
  AddressesTile(this._addresses);
  final Iterable<PostalAddress> _addresses;

  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        ListTile(title: Text("Addresses")),
        Column(
          children: _addresses.map((a) => Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: Column(
              children: <Widget>[
                ListTile(
                  title: Text("Street"),
                  trailing: Text(a.street ?? ""),
                ),
                ListTile(
                  title: Text("Postcode"),
                  trailing: Text(a.postcode ?? ""),
                ),
                ListTile(
                  title: Text("City"),
                  trailing: Text(a.city ?? ""),
                ),
                ListTile(
                  title: Text("Region"),
                  trailing: Text(a.region ?? ""),
                ),
                ListTile(
                  title: Text("Country"),
                  trailing: Text(a.country ?? ""),
                ),
              ],
            ),
          )).toList(),
        ),
      ],
    );
  }
}

class ItemsTile extends StatelessWidget {
  ItemsTile(this._title, this._items);
  final Iterable<Item> _items;
  final String _title;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        ListTile(title: Text(_title)),
        Column(
          children: _items.map((i) => Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            child: ListTile(
              title: Text(i.label ?? ""),
              trailing: Text(i.value ?? ""),
            ),),
          ).toList(),
        ),
      ],
    );
  }
}

class AddContactPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _AddContactPageState();
}

class _AddContactPageState extends State<AddContactPage> {
  Contact contact = Contact();
  PostalAddress address = PostalAddress(label: "Home");
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Add a contact"),
        actions: <Widget>[
          FlatButton(
            onPressed: () {
              _formKey.currentState.save();
              contact.postalAddresses = [address];
              ContactsService.addContact(contact);
              Navigator.of(context).pop();
            },
            child: Icon(Icons.save, color: Colors.white),
          )
        ],
      ),
      body: Container(
        padding: EdgeInsets.all(12.0),
        child: Form(
          key: _formKey,
          child: ListView(
            children: <Widget>[
              TextFormField(
                decoration: const InputDecoration(labelText: 'First name'),
                onSaved: (v) => contact.givenName = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Middle name'),
                onSaved: (v) => contact.middleName = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Last name'),
                onSaved: (v) => contact.familyName = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Prefix'),
                onSaved: (v) => contact.prefix = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Suffix'),
                onSaved: (v) => contact.suffix = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Phone'),
                onSaved: (v) => contact.phones = [Item(label: "mobile", value: v)],
                keyboardType: TextInputType.phone,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'E-mail'),
                onSaved: (v) => contact.emails = [Item(label: "work", value: v)],
                keyboardType: TextInputType.emailAddress,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Company'),
                onSaved: (v) => contact.company = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Job'),
                onSaved: (v) => contact.jobTitle = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Note'),
                onSaved: (v) => contact.note = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Street'),
                onSaved: (v) => address.street = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'City'),
                onSaved: (v) => address.city = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Region'),
                onSaved: (v) => address.region = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Postal code'),
                onSaved: (v) => address.postcode = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Country'),
                onSaved: (v) => address.country = v,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class UpdateContactsPage extends StatefulWidget {
  UpdateContactsPage({ @required this.contact });
  final Contact contact;
  @override
  _UpdateContactsPageState createState() => _UpdateContactsPageState();
}

class _UpdateContactsPageState extends State<UpdateContactsPage> {
  Contact contact;
  PostalAddress address = PostalAddress(label: "Home");
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  @override
  void initState() {
    super.initState();
    contact = widget.contact;
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Update Contact"),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.save, color: Colors.white,),
            onPressed: () async {
              _formKey.currentState.save();
              contact.postalAddresses = [address];
              await ContactsService.updateContact(contact).then((_) {
                Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => ContactListPage()));
              });
            },
          ),
        ],
      ),
      body: Container(
        padding: EdgeInsets.all(12.0),
        child: Form(
          key: _formKey,
          child: ListView(
            children: <Widget>[
              TextFormField(
                initialValue: contact.givenName ?? "",
                decoration: const InputDecoration(labelText: 'First name'),
                onSaved: (v) => contact.givenName = v,
              ),
              TextFormField(
                initialValue: contact.middleName ?? "",
                decoration: const InputDecoration(labelText: 'Middle name'),
                onSaved: (v) => contact.middleName = v,
              ),
              TextFormField(
                initialValue: contact.familyName ?? "",
                decoration: const InputDecoration(labelText: 'Last name'),
                onSaved: (v) => contact.familyName = v,
              ),
              TextFormField(
                initialValue: contact.prefix ?? "",
                decoration: const InputDecoration(labelText: 'Prefix'),
                onSaved: (v) => contact.prefix = v,
              ),
              TextFormField(
                initialValue: contact.suffix ?? "",
                decoration: const InputDecoration(labelText: 'Suffix'),
                onSaved: (v) => contact.suffix = v,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'Phone'),
                onSaved: (v) => contact.phones = [Item(label: "mobile", value: v)],
                keyboardType: TextInputType.phone,
              ),
              TextFormField(
                decoration: const InputDecoration(labelText: 'E-mail'),
                onSaved: (v) => contact.emails = [Item(label: "work", value: v)],
                keyboardType: TextInputType.emailAddress,
              ),
              TextFormField(
                initialValue: contact.company ?? "",
                decoration: const InputDecoration(labelText: 'Company'),
                onSaved: (v) => contact.company = v,
              ),
              TextFormField(
                initialValue: contact.jobTitle ?? "",
                decoration: const InputDecoration(labelText: 'Job'),
                onSaved: (v) => contact.jobTitle = v,
              ),
              TextFormField(
                initialValue: contact.note ?? "",
                decoration: const InputDecoration(labelText: 'Note'),
                onSaved: (v) => contact.note = v,
              ),
              TextFormField(
                initialValue: address.street ?? "",
                decoration: const InputDecoration(labelText: 'Street'),
                onSaved: (v) => address.street = v,
              ),
              TextFormField(
                initialValue: address.city ?? "",
                decoration: const InputDecoration(labelText: 'City'),
                onSaved: (v) => address.city = v,
              ),
              TextFormField(
                initialValue: address.region ?? "",
                decoration: const InputDecoration(labelText: 'Region'),
                onSaved: (v) => address.region = v,
              ),
              TextFormField(
                initialValue: address.postcode ?? "",
                decoration: const InputDecoration(labelText: 'Postal code'),
                onSaved: (v) => address.postcode = v,
              ),
              TextFormField(
                initialValue: address.country ?? "",
                decoration: const InputDecoration(labelText: 'Country'),
                onSaved: (v) => address.country = v,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Failed to delete Contacts

Add Contacts can be successfully launched. However, I am not able to delete any contacts.

[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: PlatformException(, Failed to delete contact, make sure it has a valid identifier, null)
#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:564:7)
#1      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
<asynchronous suspension>
#2      ContactsService.deleteContact (package:contacts_service/contacts_service.dart:46:16)
#3      ContactDetailsPage.build.<anonymous closure> (package:contacts_service_example/main.dart:135:46)
#4      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:635:14)
#5      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:711:32)
#6      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
#7      TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:365:11)
#8      TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:275:7)
#9      PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:455:9)
#10     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:75:13)
#11     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:102:11)
#12     _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:218:19)
#13     _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:198:22)
#14     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7)
#15     _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7)
#16     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7)
#17     _rootRunUnary (dart:async/zone.dart:1136:13)
#18     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
#19     _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
#20     _invoke1 (dart:ui/hooks.dart:250:10)
#21     _dispatchPointerDataPacket (dart:ui/hooks.dart:159:5)

ERROR: Cannot find header file on ios build

error: /Users/Home/SDKs/flutter/.pub-cache/hosted/pub.dartlang.org/contacts_service-0.3.3/ios/Classes/ContactsServicePlugin.h: No such file or directory

Also to note this path does not exist after Home".

The path on my pc is /Users/Home/dev/SDKs/

I changed the path of my flutter sdk and the problem cleared. But thats not ideal. All other plugins were ok apart from this one.

Birthday contact null

When you set a birthday of a contact without the birth year, the birthday DateTime property returned by API will be null.

No contact name error

@Override
public int compareTo(Contact contact) {
    return this.givenName.toLowerCase().compareTo(contact.givenName.toLowerCase());
}

google play console error

Caused by: java.lang.NullPointerException:
at net.goderbauer.flutter.contactpicker.ContactPickerPlugin.onActivityResult (ContactPickerPlugin.java:63)
at io.flutter.app.FlutterPluginRegistry.onActivityResult (FlutterPluginRegistry.java:204)
at io.flutter.app.FlutterActivityDelegate.onActivityResult (FlutterActivityDelegate.java:132)
at io.flutter.app.FlutterActivity.onActivityResult (FlutterActivity.java:142)
at android.app.Activity.dispatchActivityResult (Activity.java:7799)
at android.app.ActivityThread.deliverResults (ActivityThread.java:4610)

Can't get avatar of contact

I get contact infor successfully but have a exception. Then the avatar only show text.

Unhandled Exception: MissingPluginException(No implementation found for method getAvatar on channel github.com/clovisnicolas/flutter_contacts)

== Operator for Contact is broken

Using the code I suggested in another issues that converts the contact to json with the provided structures in your library
running equals on the result of toJson works... but not using the regualr equals

//dart
import 'dart:typed_data';

//plugin
import 'package:contacts_service/contacts_service.dart';
import 'package:not_at_home/contactPicker/contactJson.dart';

//--------------------------------------------------CONTACT EQUALS--------------------------------------------------

bool contactsEqual(Contact c1, Contact c2, {
  bool checkAvatars: true,
  //NOTE: naturally the == operator does not care for the order of
  //the phones, emails, or postalAddresses
  bool useJson: true,
}){
  //If we need everything to match we can use the default == operator
  if(checkAvatars) return useJson ? jsonEquals(c1,c2) : regEquals(c1, c2);
  else{
    //save stuff
    Uint8List avatar1 = c1.avatar;
    Uint8List avatar2 = c2.avatar;

    //clear stuff
    c1.avatar = new Uint8List(0);
    c2.avatar = new Uint8List(0);

    //compare
    bool areEqual = useJson ? jsonEquals(c1,c2) : regEquals(c1, c2);

    //restore
    c1.avatar = avatar1;
    c2.avatar = avatar2;

    //return
    return areEqual;
  }
}

jsonEquals(Contact c1, Contact c2){
  return jsonFromContact(c1) == jsonFromContact(c2);
}

regEquals(Contact c1, Contact c2){
  return (c1 == c2);
}

Empty array returned on iOS 13

The following method in iOS 13 returns an empty array.
ContactsService.getContacts()

In iOS 13, developers are unable to access the notes portion of contacts. I was thinking that this could be the cause of the problem.

Enable updating the account of a Contact

Followup from #35 which was concerned with adding account identifier to contact.

This issue is an enhancement wish, namely being able to change the account identifier in a loaded or new contact, then updating it.

Helper Functions

some helper functions that almost everyone using your plugin is going to need

import 'package:contacts_service/contacts_service.dart';
import 'dart:typed_data';
import 'dart:convert';

import 'package:not_at_home/contactPicker/helper.dart';

//by: Bryan Cancel
//https://b-cancel.github.io/
//https://github.com/b-cancel

//--------------------------------------------------JSON -> CONTACT

List<Contact> jsonToContacts(String jsonData){
  printWrapped(jsonData);
  List collection = json.decode(jsonData);
  List<Contact> contacts = collection.map((json) => Contact.fromMap(json)).toList();
  return contacts;
}

Contact jsonToContact(String jsonData){
  return Contact.fromMap(json.decode(jsonData));
}

//--------------------------------------------------JSON <- CONTACT

String jsonFromContacts(List<Contact> contacts){
  //prep
  String json = '[';

  //create json file
  for(int i = 0; i < contacts.length; i++){
    json += jsonFromContact(contacts[i]);
    //add comma between items
    if(i != (contacts.length - 1)) json += ",";
  }
  json += ']';

  //return
  return json;
}

String jsonFromContact(Contact contact){
  return json.encode(contact.toMap());
}

Sudden error on read contacts

var contacts = (await ContactsService.getContacts(withThumbnails: false));

when im calling function above , it run smoothly but when im call contacts.toString() or get getList() it will show error "Exception has occurred.ArgumentError (Invalid argument(s))"

Before this it run normally, i can get list but just today it show this errors.. i dont do nothing at this code... can u help me sir?.. need to deliver app

Crash when not name contact

contact is null the app crash

@OverRide
public int compareTo(Contact contact) {
return this.givenName.toLowerCase().compareTo(contact.givenName.toLowerCase());
}

Swift.h file not found

  1. Delete the ios folder (you might want to create a backup first)
  2. Run this command in the root of the project: flutter create -i swift .

Android permissions. Crash even if permissions is granted.

When trying to ContactsService.getContacts() my android app is crashing. The permission is granted as you see in the first line below.

I/flutter (21167): PermissionStatus.granted
E/AndroidRuntime(21167): FATAL EXCEPTION: AsyncTask #2
E/AndroidRuntime(21167): Process: com.example.phonebook, PID: 21167
E/AndroidRuntime(21167): java.lang.RuntimeException: An error occurred while executing doInBackground()
E/AndroidRuntime(21167): 	at android.os.AsyncTask$3.done(AsyncTask.java:354)
E/AndroidRuntime(21167): 	at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
E/AndroidRuntime(21167): 	at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
E/AndroidRuntime(21167): 	at java.util.concurrent.FutureTask.run(FutureTask.java:271)
E/AndroidRuntime(21167): 	at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
E/AndroidRuntime(21167): 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/AndroidRuntime(21167): 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/AndroidRuntime(21167): 	at java.lang.Thread.run(Thread.java:764)
E/AndroidRuntime(21167): Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.contacts.ContactsProvider2 uri content://com.android.contacts/data from pid=21167, uid=11432 requires android.permission.READ_CONTACTS, or grantUriPermission()
E/AndroidRuntime(21167): 	at android.os.Parcel.createException(Parcel.java:1950)
E/AndroidRuntime(21167): 	at android.os.Parcel.readException(Parcel.java:1918)
E/AndroidRuntime(21167): 	at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
E/AndroidRuntime(21167): 	at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
E/AndroidRuntime(21167): 	at android.content.ContentProviderProxy.query(ContentProviderNative.java:432)
E/AndroidRuntime(21167): 	at android.content.ContentResolver.query(ContentResolver.java:847)
E/AndroidRuntime(21167): 	at android.content.ContentResolver.query(ContentResolver.java:762)
E/AndroidRuntime(21167): 	at android.content.ContentResolver.query(ContentResolver.java:720)
E/AndroidRuntime(21167): 	at flutter.plugins.contactsservice.contactsservice.ContactsServicePlugin.getCursor(ContactsServicePlugin.java:205)
E/AndroidRuntime(21167): 	at flutter.plugins.contactsservice.contactsservice.ContactsServicePlugin.access$200(ContactsServicePlugin.java:38)
E/AndroidRuntime(21167): 	at flutter.plugins.contactsservice.contactsservice.ContactsServicePlugin$GetContactsTask.doInBackground(ContactsServicePlugin.java:154)
E/AndroidRuntime(21167): 	at flutter.plugins.contactsservice.contactsservice.ContactsServicePlugin$GetContactsTask.doInBackground(ContactsServicePlugin.java:133)
E/AndroidRuntime(21167): 	at android.os.AsyncTask$2.call(AsyncTask.java:333)
E/AndroidRuntime(21167): 	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
E/AndroidRuntime(21167): 	... 4 more
D/OSTracker(21167): OS Event: crash

this is the permission I set in the manifest file.

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

Getting contacts is very slow

Getting contacts takes a lot of time. It takes 10-12 seconds for me.
If someone wants only name and phone number it shouldn't be that slow. I've implemented it for android but don't know how to do it for iOS.

        val selection = ContactsContract.Contacts.HAS_PHONE_NUMBER
        var cursor = applicationContext.contentResolver.query(uri, arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.Contacts._ID), selection, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC")
        cursor!!.moveToFirst()
        while (cursor.isAfterLast == false) {
          val contactNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
          val contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)) 
          val phoneContactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID))
          val contactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID))
          cursor.moveToNext()
        }
        cursor.close()

call to getContacts() crashes Android emulator without stacktrace

I'm attempting to incorporate Contacts Service into my Flutter app. I have configured my manifest to include read and write permissions on contacts. I have verified the app requests and confirms permissions correctly for read/write on contacts. When attempting to call getContacts() from within my app-flow, the app immediately exits with no debug or stacktrace. Thinking I may have called something in an odd order, I removed my app-flow and copy/pasted the example widget into my app. On call, the app immediately exits with no debug or stacktrace.

Here is the output on my Android SDK run console:
I/flutter (19587): Get contacts
Lost connection to device.

Here are my manifest statements:
image

Here is my original call to getContacts():

Future<void> _getFriendList() async {
    Iterable<Contact> contacts;

    PermissionStatus permissionStatus = await _getPermission();

    if (permissionStatus == PermissionStatus.granted) {
        debugPrint("Get contacts");

        var contacts = await ContactsService.getContacts(withThumbnails: false);
        debugPrint("test");

        if (contacts != null) {
            contacts.forEach((contact) {
                debugPrint("contact");
                //addFriendToList(contact.displayName);
            });
        }
    }
    else {
        debugPrint("_getFriendList:  Could not get permission.");
    }
}

Future<PermissionStatus> _getPermission() async {

    PermissionStatus permission = await PermissionHandler().checkPermissionStatus(PermissionGroup.contacts);

    if (permission != PermissionStatus.granted && permission != PermissionStatus.disabled) {
        Map<PermissionGroup, PermissionStatus> permisionStatus = await PermissionHandler().requestPermissions([PermissionGroup.contacts]);
        debugPrint("permisionStatus " + permisionStatus.toString());
        return permisionStatus[PermissionGroup.contacts] ?? PermissionStatus.unknown;
    }
    else {
        return permission;
    }
}

Here is my Flutter Doctor output:
D:\Applications\flutter\flutter_windows_v1.5.4-hotfix.2-stable\flutter\bin\flutter.bat doctor --verbose
[√] Flutter (Channel stable, v1.12.13+hotfix.5, on Microsoft Windows [Version 10.0.18362.592], locale en-US)
• Flutter version 1.12.13+hotfix.5 at D:\Applications\flutter\flutter_windows_v1.5.4-hotfix.2-stable\flutter
• Framework revision 27321ebbad (6 weeks ago), 2019-12-10 18:15:01 -0800
• Engine revision 2994f7e1e6
• Dart version 2.7.0

[√] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
• Android SDK at C:\Users\Marc\AppData\Local\Android\Sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-29, build-tools 29.0.2
• ANDROID_HOME = C:\Users\Marc\AppData\Local\Android\Sdk
• ANDROID_SDK_ROOT = D:\Applications\Android SDK
• Java binary at: D:\Applications\Android SDK\jre\bin\java
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)
• All Android licenses accepted.

[√] Android Studio (version 3.5)
• Android Studio at D:\Applications\Android SDK
• Flutter plugin version 42.1.1
• Dart plugin version 191.8593
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)

[√] Connected device (1 available)
• Android SDK built for x86 • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)

• No issues found!
Process finished with exit code 0

Edit: I confirmed the same behavior exists on my Samsung S7 hardware.

Unable to determine Swift version

contacts_service does not specify a Swift version and none of the targets (Runner) integrating it have the SWIFT_VERSION attribute set. Please contact the author or set the SWIFT_VERSION attribute in at least one of the targets that integrate this pod.

I get this error when trying to run 'pod install'

Error, even when i use example app

Is this plugin still work on a new flutter update?

[√] Flutter (Channel stable, v1.9.1+hotfix.2, on Microsoft Windows [Version 10.0.18362.295], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 29.0.0)
[√] Android Studio (version 3.5)
[√] IntelliJ IDEA Ultimate Edition (version 2019.1)
[√] VS Code (version 1.38.0)
[√] Connected device (1 available)

Get this error after getting permission:

E/AndroidRuntime(24389): FATAL EXCEPTION: AsyncTask #2
E/AndroidRuntime(24389): Process: com.indivara.woi_app, PID: 24389
E/AndroidRuntime(24389): java.lang.RuntimeException: An error occurred while executing doInBackground()
E/AndroidRuntime(24389): at android.os.AsyncTask$3.done(AsyncTask.java:354)
E/AndroidRuntime(24389): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
E/AndroidRuntime(24389): at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
E/AndroidRuntime(24389): at java.util.concurrent.FutureTask.run(FutureTask.java:271)
E/AndroidRuntime(24389): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
E/AndroidRuntime(24389): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/AndroidRuntime(24389): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/AndroidRuntime(24389): at java.lang.Thread.run(Thread.java:764)
E/AndroidRuntime(24389): Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toLowerCase()' on a null object reference
E/AndroidRuntime(24389): at flutter.plugins.contactsservice.contactsservice.Contact.compareTo(Contact.java:94)
E/AndroidRuntime(24389): at flutter.plugins.contactsservice.contactsservice.ContactsServicePlugin$GetContactsTask$1.compare(ContactsServicePlugin.java:175)
E/AndroidRuntime(24389): at flutter.plugins.contactsservice.contactsservice.ContactsServicePlugin$GetContactsTask$1.compare(ContactsServicePlugin.java:172)
E/AndroidRuntime(24389): at java.util.TimSort.binarySort(TimSort.java:296)
E/AndroidRuntime(24389): at java.util.TimSort.sort(TimSort.java:239)
E/AndroidRuntime(24389): at java.util.Arrays.sort(Arrays.java:1498)
E/AndroidRuntime(24389): at java.util.ArrayList.sort(ArrayList.java:1470)
E/AndroidRuntime(24389): at java.util.Collections.sort(Collections.java:201)
E/AndroidRuntime(24389): at flutter.plugins.contactsservice.contactsservice.ContactsServicePlugin$GetContactsTask.doInBackground(ContactsServicePlugin.java:178)
E/AndroidRuntime(24389): at flutter.plugins.contactsservice.contactsservice.ContactsServicePlugin$GetContactsTask.doInBackground(ContactsServicePlugin.java:137)
E/AndroidRuntime(24389): at android.os.AsyncTask$2.call(AsyncTask.java:333)
E/AndroidRuntime(24389): at java.util.concurrent.FutureTask.run(FutureTask.java:266)
E/AndroidRuntime(24389): ... 4 more
I/Process (24389): Sending signal. PID: 24389 SIG: 9
Could not update files on device: HttpException: Connection closed before full header was received, uri = http://127.0.0.1:65522/EItVHkZPrCk=/
Lost connection to device.

Deleting contacts with same name

Hi,

First of all, great plugin. Makes life easy.

My problem, on a One Plus running Android 9, is that if I have a contact ("John") that was created through the phone, I then create (ContactsService.addContact) a contact through the app with the exact same name ("John"), which results in a combined contact between these two. This is not so much of an issue and is probably just how the phone works. The issue is when I want to delete (ContactsService.deleteContact) the app-created contact through the app, it also deletes the old phone-created contact.

Not sure if the case is the on iOS.

So my question is, is there any solution to this?

I assume this is related to:
http://android-contact-id-vs-raw-contact-id.blogspot.com/

Add instrumented unit test to Android part.

I tried adding an instrumented unit test to the Android part but found myself quickly in gradle dependency hell. If it is easy for you, could you add one?

This would make contributing much easier. I have started an Android only project to work on the accounts, but are unable to test the stuff on the contacts_service Android part atm.

Add a getContactById method

There needs to be a method of retrieving an individual contact again by a contact ID instead of a query. I'm trying to save a reference to a contact in my local DB and then pull up that contact again at a later date, but it doesn't look like this is possible at the moment. Looking into this it looks like instead of using the contact_id that is currently stored you should be using the lookup key, which will persist through contact sync issues that may shuffle the IDs. I'm currently working getting this to work for android, but will need some help getting the iOS code working.

Error on ContactsService.getContacts()

When i try to get the contacts, while they do arrive when the future ends, the single property of the iterable contains an exception, which includes this message.

"BadState: Too many elements".

The interesting thing is that if i don't use it in a widget it still throws that error. But if disable the uncaught and all exceptions of vscode then it manages to return the Iterable just fine.

image

mileage

This issue is similar to #12
It would be helpful to be able to use the mileage field from google contacts to pass data

Add Note field to contact

in my case I need to add a note for every contact i will be add .
I searched and see in 2 phones that there is a field for notes.
it is appreciated to add it into Contact.
Thanks.

Error when trying update contacts

When i'm trying update a contact, it doesnt save phones.
i'm using the example code. I'm thinking that the problem was in contact maping but i didn't find the error.

here is my update code
`
class UpdateContactsPage extends StatefulWidget {
UpdateContactsPage({ @required this.contact });
final Contact contact;
@OverRide
_UpdateContactsPageState createState() => _UpdateContactsPageState();
}

class _UpdateContactsPageState extends State {
Contact contact;
PostalAddress address = PostalAddress(label: "Home");
final GlobalKey _formKey = GlobalKey();
@OverRide
void initState() {
super.initState();
contact = widget.contact;
}
@OverRide
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Update Contact"),
actions: [
IconButton(
icon: Icon(Icons.save, color: Colors.white,),
onPressed: () async {
_formKey.currentState.save();
contact.postalAddresses = [address];
await ContactsService.updateContact(contact);
Navigator.of(context).pop();
},
),
],
),
body: Container(
padding: EdgeInsets.all(12.0),
child: Form(
key: _formKey,
child: ListView(
children: [
TextFormField(
initialValue: contact.givenName ?? "",
decoration: const InputDecoration(labelText: 'First name'),
onSaved: (v) => contact.givenName = v,
),
TextFormField(
initialValue: contact.middleName ?? "",
decoration: const InputDecoration(labelText: 'Middle name'),
onSaved: (v) => contact.middleName = v,
),
TextFormField(
initialValue: contact.familyName ?? "",
decoration: const InputDecoration(labelText: 'Last name'),
onSaved: (v) => contact.familyName = v,
),
TextFormField(
initialValue: contact.prefix ?? "",
decoration: const InputDecoration(labelText: 'Prefix'),
onSaved: (v) => contact.prefix = v,
),
TextFormField(
initialValue: contact.suffix ?? "",
decoration: const InputDecoration(labelText: 'Suffix'),
onSaved: (v) => contact.suffix = v,
),
TextFormField(
decoration: const InputDecoration(labelText: 'Phone'),
onSaved: (v) => contact.phones = [Item(label: "mobile", value: v)],
keyboardType: TextInputType.phone,
),
TextFormField(
decoration: const InputDecoration(labelText: 'E-mail'),
onSaved: (v) => contact.emails = [Item(label: "work", value: v)],
keyboardType: TextInputType.emailAddress,
),
TextFormField(
initialValue: contact.company ?? "",
decoration: const InputDecoration(labelText: 'Company'),
onSaved: (v) => contact.company = v,
),
TextFormField(
initialValue: contact.jobTitle ?? "",
decoration: const InputDecoration(labelText: 'Job'),
onSaved: (v) => contact.jobTitle = v,
),
TextFormField(
initialValue: contact.note ?? "",
decoration: const InputDecoration(labelText: 'Note'),
onSaved: (v) => contact.note = v,
),
TextFormField(
initialValue: address.street ?? "",
decoration: const InputDecoration(labelText: 'Street'),
onSaved: (v) => address.street = v,
),
TextFormField(
initialValue: address.city ?? "",
decoration: const InputDecoration(labelText: 'City'),
onSaved: (v) => address.city = v,
),
TextFormField(
initialValue: address.region ?? "",
decoration: const InputDecoration(labelText: 'Region'),
onSaved: (v) => address.region = v,
),
TextFormField(
initialValue: address.postcode ?? "",
decoration: const InputDecoration(labelText: 'Postal code'),
onSaved: (v) => address.postcode = v,
),
TextFormField(
initialValue: address.country ?? "",
decoration: const InputDecoration(labelText: 'Country'),
onSaved: (v) => address.country = v,
),
],
),
),
),
);
}
}
`

Cover Edge Case avatar isn't always retreived as you would expect by json.decode

Im using this instead

  if(m["avatar"] is List<dynamic>){
    List av = m["avatar"];
    if(av.length == 0) avatar = new Uint8List(0);
    else{
      List<int> ints = new List<int>();
      for(int i = 0; i < av.length; i++){
        int number = av[i];
        ints.add(number);
      }
      avatar = Uint8List.fromList(ints);
    }
  }
  else avatar = m["avatar"];

and I created my own fromMap in the meantime

Contact myFromMap(Map m) {
  //init vars
  String identifier, displayName, givenName, middleName, prefix, suffix, familyName, company, jobTitle, note;
  Iterable<Item> emails = [];
  Iterable<Item> phones = [];
  Iterable<PostalAddress> postalAddresses = [];
  Uint8List avatar;

  //set vars
  identifier = m["identifier"];
  displayName = m["displayName"];
  givenName = m["givenName"];
  middleName = m["middleName"];
  familyName = m["familyName"];
  prefix = m["prefix"];
  suffix = m["suffix"];
  company = m["company"];
  jobTitle = m["jobTitle"];
  emails = (m["emails"] as Iterable)?.map((m) => Item.fromMap(m));
  phones = (m["phones"] as Iterable)?.map((m) => Item.fromMap(m));
  postalAddresses = (m["postalAddresses"] as Iterable)
      ?.map((m) => PostalAddress.fromMap(m));

  //avatar is more complex
  if(m["avatar"] is List<dynamic>){
    List av = m["avatar"];
    if(av.length == 0) avatar = new Uint8List(0);
    else{
      List<int> ints = new List<int>();
      for(int i = 0; i < av.length; i++){
        int number = av[i];
        ints.add(number);
      }
      avatar = Uint8List.fromList(ints);
    }
  }
  else avatar = m["avatar"];
  note = m["note"];

  //return
  Contact c = new Contact(
    givenName: givenName,
    middleName: middleName,
    familyName: familyName,
    prefix: prefix,
    suffix: suffix,
    company: company,
    jobTitle: jobTitle,
    emails: emails,
    phones: phones,
    postalAddresses: postalAddresses,
    avatar: avatar,
    note: note,
  );

  c.identifier = identifier;
  c.displayName = displayName;

  return c;
}

Hard to maintain consistent code with localized labels

Hi,

I'm developing an app for internationalisation and just found out that when getting returned values from phone labels in iOS, the labels have been translated to the appropriate locale values set by the device.

This made it difficult to maintain a consistent code base to find out if the phone number was 'mobile' or not as I have to account for all the languages that I'm going to support.

By looking at the source for iOS at ios/Classes/SwiftContactsServicePlugin.swift at lines 331, 344, 356, I found that the labels are returning localized values.

Could this be resolved?

Many Thanks

Trying to display avatar get: Exception: Could not instantiate image codec.

Hi, I juts tried to pull contacts and display avatar, but get this error:

════════ Exception caught by image resource service ════════════════════════════
Exception: Could not instantiate image codec.
═══════════════════════════════════════════════════

I use following code:

CircleAvatar(child: Image.memory(contact.avatar)),

I use it on android, here is flutter doctor:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.9.1+hotfix.6, on Mac OS X 10.14.6 18G103, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.2)
[!] Android Studio (version 3.4)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[✓] VS Code (version 1.40.0)
[!] Connected device
    ! No devices available

! Doctor found issues in 2 categories.

Ability to set and retrieve contact's URL(s)

Not sure if this is supported by both iOS and Android at the same time, but it would be great if contacts_service would allow setting and retrieving contact's Website address(es). Having a possibility to set / read at least one URL would be great!

Does add_contact return a status or exception?

newContact.displayName="Jerin";
newContact.phones = []..add(Item.fromMap({'label': 'work', 'value': phoneNumber})); // Set the label of your choice
// Add a contact
// The contact must have a firstName / lastName to be successfully added
await ContactsService.addContact(newContact);

I have done this, it executes but contact is not added. Is it because I havent added the first name and last? But I have given display name. Model doesnt have definition for first and last name?

So does the function return a status?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.