Flutter | Multiple contacts selection using two plugins | A custom code

Waleed Arshad
CodeChai
Published in
5 min readAug 22, 2018

--

Hi Flutterers!

Note: Please follow the steps very carefully in order to succeed in fetching and populating contacts, otherwise you won’t be able to build iOS, and Android will crash immediately after launching.

I won’t be showing how to build exactly the same thing as in the picture, to get the complete aura of the code please follow the link below:

We will be getting the complete contact list from both Android and iOS and populate it in a custom beautiful list which will also have the feature of selecting multiple contacts.

Thanks to Clovis Nicolas for contacts_service plugin and Ethras for simple_permissions plugin. We will be using these two plugins wisely in order to achieve our goal.

We are going to build something similar to this:

Setting up

First of all, you need to create a new Flutter project with an additional command line argument in order to build the iOS successfully. Create a new flutter project like this:

$ flutter create -i swift your_app_name

contacts_service plugin doesn’t work if you don’t create your Flutter project like this as quoted by the Author of this plugin. There are some iOS conflicts with Swift versions which I am unaware about.

Insert the following lines in your pubspec.yaml:

contacts_service: ^0.0.8
simple_permissions: ^0.1.6

Note: simple_permissions is just used to get permissions for Android only. You can use iOS with or without this. The new android now asks the user to allow it between the application, just like in iOS, in order to get permission, even if you have added permissions in Manifest you need to use this otherwise Android crashes as soon as it opens up.

Let’s add permissions, insert the following in Android manifest:

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

And this in iOS info.plist:

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

An important tweak

Build your app once on iOS and it will fail, then you need to change the second line of your Podfile from this:

# platform :ios, '8.0'

to this:

platform :ios, '10.0'

simple_permissions plugin works on iOS ≥ 10.0, so before you freak out why your app is failing to build, make sure you do this step!

Building code

Import these two lines in your code first:

import 'package:contacts_service/contacts_service.dart';
import 'package:simple_permissions/simple_permissions.dart';

Now let us create some lists and variables that we will use to restate the UI, add this to your state class:

List<Contact> _contacts = new List<Contact>();
List<CustomContact> _uiCustomContacts = List<CustomContact>();
List<CustomContact> _allContacts = List<CustomContact>();
bool _isLoading = false;
bool _isSelectedContactsView = false;
String floatingButtonLabel;
Color floatingButtonColor;
IconData icon;

Create a CustomContact class as well, in order to keep the track of selected contacts:

class CustomContact {
final Contact contact;
bool isChecked;

CustomContact({
this.contact,
this.isChecked = false,
});
}

Now add an initState function which will get contacts permission and populate the contacts list.

@override
void initState() {
// TODO: implement initState
super.initState();
getContactsPermission();
refreshContacts();
}
void getContactsPermission() {
SimplePermissions.requestPermission(Permission.ReadContacts);
}
refreshContacts() async {
setState(() {
_isLoading = true;
});
var contacts = await ContactsService.getContacts();
_populateContacts(contacts);
}
void _populateContacts(Iterable<Contact> contacts) {
_contacts = contacts.where((item) => item.displayName != null).toList();
_contacts.sort((a, b) => a.displayName.compareTo(b.displayName));
_allContacts =
_contacts.map((contact) => CustomContact(contact: contact)).toList();
setState(() {
_uiCustomContacts = _allContacts;
_isLoading = false;
});
}

I am sorting the list alphabetically and converting the fetched list into my CustomContact list in order to be able to select the contacts from my populated list.

I have used _isLoading variable in order to show a loader, you can skip that if you want.

Follow the github link anytime you are struggling to understand things.

So we have populated our lists. Let’s build the UI now; add the following to the body tag of your build method:

Container(
child: ListView.builder(
itemCount: _uiCustomContacts?.length,
itemBuilder: (BuildContext context, int index) {
CustomContact _contact = _uiCustomContacts[index];
var _phonesList = _contact.contact.phones.toList();

return _buildListTile(_contact, _phonesList);
},
),
)

Add this function too somewhere below the build method:

ListTile _buildListTile(CustomContact c, List<Item> list) {
return ListTile(
leading: (c.contact.avatar != null)
? CircleAvatar(backgroundImage: MemoryImage(c.contact.avatar))
: CircleAvatar(
child: Text(
(c.contact.displayName[0] +
c.contact.displayName[1].toUpperCase()),
style: TextStyle(color: Colors.white)),
),
title: Text(c.contact.displayName ?? ""),
subtitle: list.length >= 1 && list[0]?.value != null
? Text(list[0].value)
: Text(''),
trailing: Checkbox(
activeColor: Colors.green,
value: c.isChecked,
onChanged: (bool value) {
setState(() {
c.isChecked = value;
});
}),
);
}

I am using ListTile to show name and number of a contact and a checkbox in the trailing tag of the Tile. I am also using an avatar to show the first two characters of the names that I retrieve. You can style the ListTile the way you want.

This code will hopefully bring you the contacts.

Let’s add the FloatingActionButton and see what it should do when we press it.

floatingActionButton: new FloatingActionButton.extended(
backgroundColor: floatingButtonColor,
onPressed: _onSubmit,
icon: Icon(icon),
label: Text(floatingButtonLabel),
),

Add _onSubmit function:

void _onSubmit() {
setState(() {
if (!_isSelectedContactsView) {
_uiCustomContacts =
_allContacts.where((contact) => contact.isChecked == true).toList();
_isSelectedContactsView = true;
_restateFloatingButton(
widget.reloadLabel,
Icons.refresh,
Colors.green,
);
} else {
_uiCustomContacts = _allContacts;
_isSelectedContactsView = false;
_restateFloatingButton(
widget.fireLabel,
Icons.filter_center_focus,
Colors.red,
);
}
});
}

The above code checks which view is selected and restate the contacts list and the floating button accordingly.

Add _restateFloatingButton function:

void _restateFloatingButton(String label, IconData icon, Color color) {
floatingButtonLabel = label;
icon = icon;
floatingButtonColor = color;
}

This will update the UI with the selected contacts, and will also update the FAB text, label and color accordingly.

You can go through the complete code here.

If you click deny on Android while asking for contacts permission, the application crashes. I haven’t been able to figure it out. If you want to contribute for this or for any other suggestion, feel free to contact me. If I figure it out, I will update the code and the article too. The current code is tested on 500 contacts, it may take time to fetch depending on the number of contacts in the phone.

Feel free to contact me for suggestions, updates and contributions!

Cheers! :)

The Flutter Pub is a medium publication to bring you the latest and amazing resources such as articles, videos, codes, podcasts etc. about this great technology to teach you how to build beautiful apps with it. You can find us on Facebook, Twitter, and Medium or learn more about us here. We’d love to connect! And if you are a writer interested in writing for us, then you can do so through these guidelines.

--

--

Waleed Arshad
CodeChai

Writer || Tech philosophy || Senior Flutter Engineer || Google Developer Expert