All checks were successful
/ mirror (push) Successful in 4s
Feat: contact-modal et refonte du contact-state pour la page de favori et possibilité de update les contacts. Aussi mis la composition page avec le service de contact, on évite de fetch dans la page directement Reviewed-on: icing/G-EIP-700-TLS-7-1-eip-stephane.corbiere#10 Co-authored-by: Florian Griffon <florian.griffon@epitech.eu> Co-committed-by: Florian Griffon <florian.griffon@epitech.eu>
219 lines
8.2 KiB
Dart
219 lines
8.2 KiB
Dart
import 'package:dialer/widgets/username_color_generator.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
|
import '../contact_state.dart';
|
|
import '../../../widgets/color_darkener.dart';
|
|
import 'add_contact_button.dart';
|
|
import 'contact_modal.dart';
|
|
import 'share_own_qr.dart';
|
|
|
|
class AlphabetScrollPage extends StatefulWidget {
|
|
final double scrollOffset;
|
|
|
|
const AlphabetScrollPage({super.key, required this.scrollOffset});
|
|
|
|
@override
|
|
_AlphabetScrollPageState createState() => _AlphabetScrollPageState();
|
|
}
|
|
|
|
class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
|
late ScrollController _scrollController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_scrollController =
|
|
ScrollController(initialScrollOffset: widget.scrollOffset);
|
|
_scrollController.addListener(_onScroll);
|
|
}
|
|
|
|
void _onScroll() {
|
|
final contactState = ContactState.of(context);
|
|
contactState.setScrollOffset(_scrollController.offset);
|
|
}
|
|
|
|
Future<void> _refreshContacts() async {
|
|
final contactState = ContactState.of(context);
|
|
try {
|
|
await contactState.fetchContacts();
|
|
} catch (e) {
|
|
print('Error refreshing contacts: $e');
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Failed to refresh contacts')),
|
|
);
|
|
}
|
|
}
|
|
|
|
void _toggleFavorite(Contact contact) async {
|
|
try {
|
|
if (await FlutterContacts.requestPermission()) {
|
|
Contact? fullContact = await FlutterContacts.getContact(contact.id,
|
|
withProperties: true,
|
|
withAccounts: true,
|
|
withPhoto: true,
|
|
withThumbnail: true);
|
|
|
|
if (fullContact != null) {
|
|
fullContact.isStarred = !fullContact.isStarred;
|
|
await FlutterContacts.updateContact(fullContact);
|
|
}
|
|
await _refreshContacts();
|
|
} else {
|
|
print("Could not fetch contact details");
|
|
}
|
|
} catch (e) {
|
|
print("Error updating favorite status: $e");
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Failed to update contact favorite status')),
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final contactState = ContactState.of(context);
|
|
final contacts = contactState.contacts;
|
|
final selfContact = contactState.selfContact;
|
|
|
|
Map<String, List<Contact>> alphabetizedContacts = {};
|
|
for (var contact in contacts) {
|
|
String firstLetter = contact.displayName.isNotEmpty
|
|
? contact.displayName[0].toUpperCase()
|
|
: '#';
|
|
if (!alphabetizedContacts.containsKey(firstLetter)) {
|
|
alphabetizedContacts[firstLetter] = [];
|
|
}
|
|
alphabetizedContacts[firstLetter]!.add(contact);
|
|
}
|
|
|
|
List<String> alphabetKeys = alphabetizedContacts.keys.toList()..sort();
|
|
|
|
return Scaffold(
|
|
backgroundColor: Colors.black,
|
|
body: Column(
|
|
children: [
|
|
// Top buttons row
|
|
Container(
|
|
color: Colors.black,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
AddContactButton(),
|
|
QRCodeButton(contacts: contacts, selfContact: selfContact),
|
|
],
|
|
),
|
|
),
|
|
// Contact List
|
|
Expanded(
|
|
child: ListView.builder(
|
|
controller: _scrollController,
|
|
itemCount: alphabetKeys.length,
|
|
itemBuilder: (context, index) {
|
|
String letter = alphabetKeys[index];
|
|
List<Contact> contactsForLetter = alphabetizedContacts[letter]!;
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Alphabet Letter Header
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 8.0, horizontal: 16.0),
|
|
child: Text(
|
|
letter,
|
|
style: TextStyle(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
// Contact Entries
|
|
...contactsForLetter.map((contact) {
|
|
String phoneNumber = contact.phones.isNotEmpty
|
|
? contact.phones.first.number
|
|
: 'No phone number';
|
|
Color avatarColor =
|
|
generateColorFromName(contact.displayName);
|
|
return ListTile(
|
|
leading: (contact.thumbnail != null &&
|
|
contact.thumbnail!.isNotEmpty)
|
|
? CircleAvatar(
|
|
backgroundImage:
|
|
MemoryImage(contact.thumbnail!),
|
|
)
|
|
: CircleAvatar(
|
|
backgroundColor: avatarColor,
|
|
child: Text(
|
|
contact.displayName.isNotEmpty
|
|
? contact.displayName[0].toUpperCase()
|
|
: '?',
|
|
style: TextStyle(
|
|
color: darken(avatarColor, 0.4)),
|
|
),
|
|
),
|
|
title: Text(contact.displayName,
|
|
style: TextStyle(color: Colors.white)),
|
|
subtitle: Text(phoneNumber,
|
|
style: TextStyle(color: Colors.white70)),
|
|
onTap: () {
|
|
showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
backgroundColor: Colors.transparent,
|
|
builder: (context) {
|
|
return ContactModal(
|
|
contact: contact,
|
|
onEdit: () async {
|
|
if (await FlutterContacts.requestPermission()) {
|
|
final updatedContact =
|
|
await FlutterContacts.openExternalEdit(
|
|
contact.id);
|
|
if (updatedContact != null) {
|
|
await _refreshContacts();
|
|
Navigator.of(context).pop();
|
|
ScaffoldMessenger.of(context)
|
|
.showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
'${contact.displayName} updated successfully!'),
|
|
),
|
|
);
|
|
} else {
|
|
ScaffoldMessenger.of(context)
|
|
.showSnackBar(
|
|
SnackBar(
|
|
content:
|
|
Text('Edit canceled or failed.'),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
},
|
|
onToggleFavorite: () {
|
|
_toggleFavorite(contact);
|
|
},
|
|
isFavorite: contact.isStarred,
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_scrollController.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|