Compare commits
No commits in common. "f3c092d5b8c1150488c6879b4cca7792a85c4d70" and "3f6ea2e332831481f967d28e842af40af7fe3182" have entirely different histories.
f3c092d5b8
...
3f6ea2e332
@ -1,8 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
|
||||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
the Flutter tool needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
|
||||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
|
||||||
<application
|
<application
|
||||||
android:label="com.example.dialer"
|
android:label="com.example.dialer"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
|
||||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<!-- The INTERNET permission is required for development. Specifically,
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
import '../../widgets/contact_service.dart';
|
|
||||||
|
|
||||||
class CompositionPage extends StatefulWidget {
|
class CompositionPage extends StatefulWidget {
|
||||||
const CompositionPage({super.key});
|
const CompositionPage({super.key});
|
||||||
@ -13,7 +12,6 @@ class _CompositionPageState extends State<CompositionPage> {
|
|||||||
String dialedNumber = "";
|
String dialedNumber = "";
|
||||||
List<Contact> _allContacts = [];
|
List<Contact> _allContacts = [];
|
||||||
List<Contact> _filteredContacts = [];
|
List<Contact> _filteredContacts = [];
|
||||||
final ContactService _contactService = ContactService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -23,7 +21,7 @@ class _CompositionPageState extends State<CompositionPage> {
|
|||||||
|
|
||||||
Future<void> _fetchContacts() async {
|
Future<void> _fetchContacts() async {
|
||||||
if (await FlutterContacts.requestPermission()) {
|
if (await FlutterContacts.requestPermission()) {
|
||||||
_allContacts = await _contactService.fetchContacts();
|
_allContacts = await FlutterContacts.getContacts(withProperties: true);
|
||||||
_filteredContacts = _allContacts;
|
_filteredContacts = _allContacts;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import 'package:flutter_contacts/flutter_contacts.dart';
|
|||||||
class ContactService {
|
class ContactService {
|
||||||
Future<List<Contact>> fetchContacts() async {
|
Future<List<Contact>> fetchContacts() async {
|
||||||
if (await FlutterContacts.requestPermission()) {
|
if (await FlutterContacts.requestPermission()) {
|
||||||
return await FlutterContacts.getContacts(withProperties: true, withThumbnail: true, withAccounts: true, withGroups: true, withPhoto: true);
|
return await FlutterContacts.getContacts(withProperties: true, withThumbnail: true, withAccounts: true, withGroups: true);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
import '../../widgets/contact_service.dart';
|
import 'contact_service.dart';
|
||||||
|
|
||||||
class ContactState extends StatefulWidget {
|
class ContactState extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
@ -3,17 +3,14 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
import '../../../widgets/color_darkener.dart';
|
import '../../../widgets/color_darkener.dart';
|
||||||
import '../contact_state.dart';
|
import '../contact_state.dart';
|
||||||
import '../../../widgets/contact_service.dart';
|
|
||||||
import 'add_contact_button.dart';
|
import 'add_contact_button.dart';
|
||||||
import 'contact_modal.dart';
|
|
||||||
import 'share_own_qr.dart';
|
import 'share_own_qr.dart';
|
||||||
|
|
||||||
class AlphabetScrollPage extends StatefulWidget {
|
class AlphabetScrollPage extends StatefulWidget {
|
||||||
final List<Contact> contacts;
|
final List<Contact> contacts;
|
||||||
final double scrollOffset;
|
final double scrollOffset;
|
||||||
|
|
||||||
const AlphabetScrollPage(
|
const AlphabetScrollPage({super.key, required this.contacts, required this.scrollOffset});
|
||||||
{super.key, required this.contacts, required this.scrollOffset});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_AlphabetScrollPageState createState() => _AlphabetScrollPageState();
|
_AlphabetScrollPageState createState() => _AlphabetScrollPageState();
|
||||||
@ -21,15 +18,11 @@ class AlphabetScrollPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
||||||
late ScrollController _scrollController;
|
late ScrollController _scrollController;
|
||||||
List<Contact> _contacts = []; // Local copy of contacts for updating
|
|
||||||
final ContactService _contactService = ContactService();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_contacts = widget.contacts; // Initialize with the provided contacts
|
_scrollController = ScrollController(initialScrollOffset: widget.scrollOffset);
|
||||||
_scrollController =
|
|
||||||
ScrollController(initialScrollOffset: widget.scrollOffset);
|
|
||||||
_scrollController.addListener(_onScroll);
|
_scrollController.addListener(_onScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,59 +31,11 @@ class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
|||||||
contactState.setScrollOffset(_scrollController.offset);
|
contactState.setScrollOffset(_scrollController.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshContacts() async {
|
|
||||||
try {
|
|
||||||
// Use the fetchContacts method from ContactService
|
|
||||||
final updatedContacts = await _contactService.fetchContacts();
|
|
||||||
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
_contacts = updatedContacts;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print('Error refreshing contacts: $e');
|
|
||||||
// Optionally show a user-friendly error message
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
.showSnackBar(SnackBar(content: Text('Failed to refresh contacts')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _toggleFavorite(Contact contact) async {
|
|
||||||
try {
|
|
||||||
// Request permission first
|
|
||||||
if (await FlutterContacts.requestPermission()) {
|
|
||||||
// Fetch the full contact details with all available properties
|
|
||||||
Contact? fullContact = await FlutterContacts.getContact(contact.id,
|
|
||||||
withProperties: true,
|
|
||||||
withAccounts: true,
|
|
||||||
withPhoto: true,
|
|
||||||
withThumbnail: true);
|
|
||||||
|
|
||||||
if (fullContact != null) {
|
|
||||||
// Update the contact
|
|
||||||
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");
|
|
||||||
// Optional: Show a user-friendly error message
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text('Failed to update contact favorite status')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Map<String, List<Contact>> alphabetizedContacts = {};
|
Map<String, List<Contact>> alphabetizedContacts = {};
|
||||||
for (var contact in _contacts) {
|
for (var contact in widget.contacts) {
|
||||||
String firstLetter = contact.displayName.isNotEmpty
|
String firstLetter = contact.displayName.isNotEmpty ? contact.displayName[0].toUpperCase() : '#';
|
||||||
? contact.displayName[0].toUpperCase()
|
|
||||||
: '#';
|
|
||||||
if (!alphabetizedContacts.containsKey(firstLetter)) {
|
if (!alphabetizedContacts.containsKey(firstLetter)) {
|
||||||
alphabetizedContacts[firstLetter] = [];
|
alphabetizedContacts[firstLetter] = [];
|
||||||
}
|
}
|
||||||
@ -102,19 +47,15 @@ class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [ // Top buttons row
|
||||||
// Top buttons row
|
|
||||||
Container(
|
Container(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||||
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
AddContactButton(),
|
AddContactButton(),
|
||||||
QRCodeButton(
|
QRCodeButton(contacts: widget.contacts, selfContact: ContactState.of(context).selfContact),
|
||||||
contacts: _contacts,
|
|
||||||
selfContact: ContactState.of(context).selfContact),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -131,8 +72,7 @@ class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
|||||||
children: [
|
children: [
|
||||||
// Alphabet Letter Header
|
// Alphabet Letter Header
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
|
||||||
vertical: 8.0, horizontal: 16.0),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
letter,
|
letter,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -144,75 +84,24 @@ class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
|||||||
),
|
),
|
||||||
// Contact Entries
|
// Contact Entries
|
||||||
...contacts.map((contact) {
|
...contacts.map((contact) {
|
||||||
String phoneNumber = contact.phones.isNotEmpty
|
String phoneNumber = contact.phones.isNotEmpty ? contact.phones.first.number : 'No phone number';
|
||||||
? contact.phones.first.number
|
Color avatarColor = generateColorFromName(contact.displayName);
|
||||||
: 'No phone number';
|
|
||||||
Color avatarColor =
|
|
||||||
generateColorFromName(contact.displayName);
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: (contact.thumbnail != null &&
|
leading: (contact.thumbnail != null && contact.thumbnail!.isNotEmpty)
|
||||||
contact.thumbnail!.isNotEmpty)
|
|
||||||
? CircleAvatar(
|
? CircleAvatar(
|
||||||
backgroundImage:
|
backgroundImage: MemoryImage(contact.thumbnail!),
|
||||||
MemoryImage(contact.thumbnail!),
|
)
|
||||||
)
|
|
||||||
: CircleAvatar(
|
: CircleAvatar(
|
||||||
backgroundColor: avatarColor,
|
backgroundColor: avatarColor,
|
||||||
child: Text(
|
child: Text(
|
||||||
contact.displayName.isNotEmpty
|
contact.displayName.isNotEmpty ? contact.displayName[0].toUpperCase() : '?',
|
||||||
? contact.displayName[0].toUpperCase()
|
style: TextStyle(color: darken(avatarColor, 0.4)),
|
||||||
: '?',
|
),
|
||||||
style: TextStyle(
|
),
|
||||||
color: darken(avatarColor, 0.4)),
|
title: Text(contact.displayName, style: TextStyle(color: Colors.white)),
|
||||||
),
|
subtitle: Text(phoneNumber, style: TextStyle(color: Colors.white70)),
|
||||||
),
|
|
||||||
title: Text(contact.displayName,
|
|
||||||
style: TextStyle(color: Colors.white)),
|
|
||||||
subtitle: Text(phoneNumber,
|
|
||||||
style: TextStyle(color: Colors.white70)),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet(
|
// Handle contact tap
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
builder: (context) {
|
|
||||||
return ContactModal(
|
|
||||||
contact: contact,
|
|
||||||
onEdit: () async {
|
|
||||||
// Trigger edit logic and refresh contacts
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@ -1,180 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:dialer/widgets/username_color_generator.dart';
|
|
||||||
|
|
||||||
class ContactModal extends StatelessWidget {
|
|
||||||
final Contact contact;
|
|
||||||
final Function onEdit;
|
|
||||||
final Function onToggleFavorite;
|
|
||||||
final bool isFavorite;
|
|
||||||
|
|
||||||
const ContactModal({
|
|
||||||
super.key,
|
|
||||||
required this.contact,
|
|
||||||
required this.onEdit,
|
|
||||||
required this.onToggleFavorite,
|
|
||||||
required this.isFavorite,
|
|
||||||
});
|
|
||||||
|
|
||||||
void _launchPhoneDialer(String phoneNumber) async {
|
|
||||||
final uri = Uri(scheme: 'tel', path: phoneNumber);
|
|
||||||
if (await canLaunchUrl(uri)) {
|
|
||||||
await launchUrl(uri);
|
|
||||||
} else {
|
|
||||||
debugPrint('Could not launch $phoneNumber');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _launchSms(String phoneNumber) async {
|
|
||||||
final uri = Uri(scheme: 'sms', path: phoneNumber);
|
|
||||||
if (await canLaunchUrl(uri)) {
|
|
||||||
await launchUrl(uri);
|
|
||||||
} else {
|
|
||||||
debugPrint('Could not launch SMS to $phoneNumber');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _launchEmail(String email) async {
|
|
||||||
final uri = Uri(scheme: 'mailto', path: email);
|
|
||||||
if (await canLaunchUrl(uri)) {
|
|
||||||
await launchUrl(uri);
|
|
||||||
} else {
|
|
||||||
debugPrint('Could not launch email to $email');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
String phoneNumber = contact.phones.isNotEmpty
|
|
||||||
? contact.phones.first.number
|
|
||||||
: 'No phone number';
|
|
||||||
String email =
|
|
||||||
contact.emails.isNotEmpty ? contact.emails.first.address : 'No email';
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => Navigator.of(context).pop(),
|
|
||||||
child: Container(
|
|
||||||
color: Colors.black.withOpacity(0.5),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {},
|
|
||||||
child: FractionallySizedBox(
|
|
||||||
heightFactor: 0.7,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey[900],
|
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
// Modal Handle
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
||||||
child: Container(
|
|
||||||
width: 50,
|
|
||||||
height: 5,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey.shade300,
|
|
||||||
borderRadius: BorderRadius.circular(5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Contact Profile
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
CircleAvatar(
|
|
||||||
radius: 50,
|
|
||||||
backgroundImage: (contact.thumbnail != null &&
|
|
||||||
contact.thumbnail!.isNotEmpty)
|
|
||||||
? MemoryImage(contact.thumbnail!)
|
|
||||||
: null,
|
|
||||||
backgroundColor:
|
|
||||||
generateColorFromName(contact.displayName),
|
|
||||||
child: (contact.thumbnail == null ||
|
|
||||||
contact.thumbnail!.isEmpty)
|
|
||||||
? Text(
|
|
||||||
contact.displayName.isNotEmpty
|
|
||||||
? contact.displayName[0].toUpperCase()
|
|
||||||
: '?',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 40, color: Colors.white),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
SizedBox(height: 10),
|
|
||||||
Text(
|
|
||||||
contact.displayName,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Contact Actions
|
|
||||||
Divider(),
|
|
||||||
ListTile(
|
|
||||||
leading: Icon(Icons.phone, color: Colors.green),
|
|
||||||
title: Text(phoneNumber),
|
|
||||||
onTap: () {
|
|
||||||
if (contact.phones.isNotEmpty) {
|
|
||||||
_launchPhoneDialer(phoneNumber);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: Icon(Icons.message, color: Colors.blue),
|
|
||||||
title: Text(phoneNumber),
|
|
||||||
onTap: () {
|
|
||||||
if (contact.phones.isNotEmpty) {
|
|
||||||
_launchSms(phoneNumber);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: Icon(Icons.email, color: Colors.orange),
|
|
||||||
title: Text(email),
|
|
||||||
onTap: () {
|
|
||||||
if (contact.emails.isNotEmpty) {
|
|
||||||
_launchEmail(email);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Divider(),
|
|
||||||
// Favorite and Edit Buttons
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
onToggleFavorite();
|
|
||||||
},
|
|
||||||
icon: Icon(contact.isStarred
|
|
||||||
? Icons.star
|
|
||||||
: Icons.star_border),
|
|
||||||
label: Text(
|
|
||||||
contact.isStarred ? 'Unfavorite' : 'Favorite'),
|
|
||||||
),
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: () => onEdit(),
|
|
||||||
icon: Icon(Icons.edit),
|
|
||||||
label: Text('Edit Contact'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 16),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,183 +1,3 @@
|
|||||||
// import 'package:flutter/material.dart';
|
|
||||||
// import 'package:flutter_contacts/flutter_contacts.dart';
|
|
||||||
// import '../../../widgets/color_darkener.dart';
|
|
||||||
// import '../contacts/contact_state.dart';
|
|
||||||
// import '../contacts/widgets/add_contact_button.dart';
|
|
||||||
// import '../contacts/widgets/contact_modal.dart';
|
|
||||||
// import '../contacts/widgets/share_own_qr.dart';
|
|
||||||
// import 'package:dialer/widgets/username_color_generator.dart';
|
|
||||||
|
|
||||||
// class FavoritePage extends StatefulWidget {
|
|
||||||
// final List<Contact> contacts;
|
|
||||||
// final double scrollOffset;
|
|
||||||
|
|
||||||
// const FavoritePage({
|
|
||||||
// super.key,
|
|
||||||
// required this.contacts,
|
|
||||||
// required this.scrollOffset,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// _FavoritePageState createState() => _FavoritePageState();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class _FavoritePageState extends State<FavoritePage> {
|
|
||||||
// late ScrollController _scrollController;
|
|
||||||
// List<Contact> _favoriteContacts = []; // Local list of favorite contacts
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// void initState() {
|
|
||||||
// super.initState();
|
|
||||||
// _favoriteContacts = widget.contacts.where((contact) => contact.isStarred).toList(); // Filter only favorites
|
|
||||||
// _scrollController = ScrollController(initialScrollOffset: widget.scrollOffset);
|
|
||||||
// _scrollController.addListener(_onScroll);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void _onScroll() {
|
|
||||||
// final contactState = ContactState.of(context);
|
|
||||||
// contactState.setScrollOffset(_scrollController.offset);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> _refreshContacts() async {
|
|
||||||
// if (await FlutterContacts.requestPermission()) {
|
|
||||||
// final updatedContacts = await FlutterContacts.getContacts(
|
|
||||||
// withProperties: true,
|
|
||||||
// withThumbnail: true,
|
|
||||||
// );
|
|
||||||
// setState(() {
|
|
||||||
// _favoriteContacts = updatedContacts.where((contact) => contact.isStarred).toList();
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void _toggleFavorite(Contact contact) async {
|
|
||||||
// if (await FlutterContacts.requestPermission()) {
|
|
||||||
// try {
|
|
||||||
// // Fetch all contacts (this can be slow if there are many contacts)
|
|
||||||
// List<Contact> allContacts = await FlutterContacts.getContacts(
|
|
||||||
// withProperties: true,
|
|
||||||
// withThumbnail: true,
|
|
||||||
// withAccounts: true,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Find the specific contact by matching contact.id
|
|
||||||
// Contact? contactToUpdate = allContacts.firstWhere(
|
|
||||||
// (c) => c.id == contact.id,
|
|
||||||
// orElse: () => throw Exception("Contact not found"),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (contactToUpdate != null) {
|
|
||||||
// contactToUpdate.isStarred = !contactToUpdate.isStarred;
|
|
||||||
|
|
||||||
// // Update the contact
|
|
||||||
// await FlutterContacts.updateContact(contactToUpdate);
|
|
||||||
|
|
||||||
// // Refresh the favorite contacts list
|
|
||||||
// setState(() {
|
|
||||||
// _favoriteContacts = allContacts.where((c) => c.isStarred).toList();
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// print("Error updating favorite status: $e");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// Widget build(BuildContext context) {
|
|
||||||
// return Scaffold(
|
|
||||||
// backgroundColor: Colors.black,
|
|
||||||
// appBar: AppBar(
|
|
||||||
// title: const Text('Favorites'),
|
|
||||||
// backgroundColor: Colors.black,
|
|
||||||
// actions: [
|
|
||||||
// IconButton(
|
|
||||||
// icon: const Icon(Icons.refresh),
|
|
||||||
// onPressed: _refreshContacts,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// body: _favoriteContacts.isEmpty
|
|
||||||
// ? Center(
|
|
||||||
// child: Text(
|
|
||||||
// 'No favorite contacts yet!',
|
|
||||||
// style: TextStyle(color: Colors.white),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// : ListView.builder(
|
|
||||||
// controller: _scrollController,
|
|
||||||
// itemCount: _favoriteContacts.length,
|
|
||||||
// itemBuilder: (context, index) {
|
|
||||||
// Contact contact = _favoriteContacts[index];
|
|
||||||
// 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 {
|
|
||||||
// // Trigger edit logic and refresh contacts
|
|
||||||
// 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();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class FavoritePage extends StatefulWidget {
|
class FavoritePage extends StatefulWidget {
|
||||||
|
@ -5,15 +5,12 @@ import 'package:dialer/features/history/history_page.dart';
|
|||||||
import 'package:dialer/features/composition/composition.dart';
|
import 'package:dialer/features/composition/composition.dart';
|
||||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
import 'package:dialer/features/settings/settings.dart';
|
import 'package:dialer/features/settings/settings.dart';
|
||||||
import '../../widgets/contact_service.dart';
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage>
|
class _MyHomePageState extends State<MyHomePage>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late TabController _tabController;
|
late TabController _tabController;
|
||||||
List<Contact> _allContacts = [];
|
List<Contact> _allContacts = [];
|
||||||
List<Contact> _contactSuggestions = [];
|
List<Contact> _contactSuggestions = [];
|
||||||
final ContactService _contactService = ContactService();
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -24,8 +21,10 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _fetchContacts() async {
|
void _fetchContacts() async {
|
||||||
_allContacts = await _contactService.fetchContacts();
|
if (await FlutterContacts.requestPermission()) {
|
||||||
setState(() {});
|
_allContacts = await FlutterContacts.getContacts(withProperties: true);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSearchChanged(String query) {
|
void _onSearchChanged(String query) {
|
||||||
|
@ -34,7 +34,6 @@ dependencies:
|
|||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
url_launcher: ^6.1.9 # To manage system dialer. Call, message...
|
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
flutter_contacts: ^1.1.9+2
|
flutter_contacts: ^1.1.9+2
|
||||||
permission_handler: ^11.3.1 # For handling permissions
|
permission_handler: ^11.3.1 # For handling permissions
|
||||||
|
Loading…
Reference in New Issue
Block a user