Contacts List
Loading preview
Starting the Flutter app
A Flutter CRM contacts list with search, avatars, and alphabetical sections.
import 'package:flutter/material.dart';
class _Contact {
final String name;
final String company;
const _Contact(this.name, this.company);
}
class ContactsList extends StatelessWidget {
const ContactsList({super.key});
static const _contacts = [
_Contact('Amara Okafor', 'Northwind Ltd'),
_Contact('Ben Carter', 'Acme Corp'),
_Contact('Bianca Rossi', 'Vertex Labs'),
_Contact('Carlos Diaz', 'Sunrise Media'),
_Contact('Dana Ford', 'Bluewave'),
_Contact('Elena Petrova', 'Orbit Studio'),
_Contact('Farah Nasser', 'Cedar & Co'),
];
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
// Group by first letter.
final grouped = <String, List<_Contact>>{};
for (final c in _contacts) {
grouped.putIfAbsent(c.name[0], () => []).add(c);
}
final letters = grouped.keys.toList()..sort();
return Scaffold(
appBar: AppBar(
title: const Text('Contacts'),
actions: [IconButton(tooltip: 'Add contact', onPressed: () => _showDemo(context, 'Add contact (demo)'), icon: const Icon(Icons.add))],
),
body: ListView(
children: [
Padding(
padding: const EdgeInsets.all(12),
child: TextField(
decoration: InputDecoration(
hintText: 'Search contacts',
prefixIcon: const Icon(Icons.search),
filled: true,
isDense: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24),
borderSide: BorderSide.none,
),
),
),
),
for (final letter in letters) ...[
Padding(
padding: const EdgeInsets.fromLTRB(20, 12, 20, 4),
child: Text(letter,
style: TextStyle(
fontWeight: FontWeight.bold, color: scheme.primary)),
),
for (final c in grouped[letter]!)
ListTile(
leading: CircleAvatar(
backgroundColor: scheme.primaryContainer,
child: Text(c.name[0],
style: TextStyle(color: scheme.onPrimaryContainer)),
),
title: Text(c.name),
subtitle: Text(c.company),
trailing: IconButton(
tooltip: 'Call',
onPressed: () => _showDemo(context, 'Call ${c.name} (demo)'),
icon: const Icon(Icons.call_outlined)),
),
],
],
),
);
}
}
void _showDemo(BuildContext context, String message) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
}
Add it to your project
Copies the Dart source into your app, so you own it. Flutter SDK only, no extra packages.
First time? Run dart pub global activate fluttertemplates_dev, or read the CLI guide.
How it works
build groups the const _contacts list into a map keyed by c.name[0], sorts the keys into letters, and emits a header plus ListTiles per letter. Grouping reruns on each rebuild, and an empty name would throw. Keys are case-sensitive, so a lowercase name would get its own section. Search isn’t wired up, and add and the call IconButton show a demo SnackBar.
Adapting it
- Group once when data arrives, outside
build, and uppercase the key. - Add a phone field to
_Contactand launchtel:from theIcons.call_outlinedbutton with url_launcher. - The outer
ListViewbuilds every row eagerly, so switch toListView.builderor slivers for large address books.