diff --git a/lib/helpers/utils/contact_picker_helper.dart b/lib/helpers/utils/contact_picker_helper.dart new file mode 100644 index 0000000..73c2840 --- /dev/null +++ b/lib/helpers/utils/contact_picker_helper.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_contacts/flutter_contacts.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:marco/helpers/widgets/my_snackbar.dart'; +import 'package:marco/helpers/services/app_logger.dart'; + +class ContactPickerHelper { + static Future pickIndianPhoneNumber(BuildContext context) async { + final status = await Permission.contacts.request(); + + if (!status.isGranted) { + if (status.isPermanentlyDenied) { + await openAppSettings(); + } + + showAppSnackbar( + title: "Permission Required", + message: + "Please allow Contacts permission from settings to pick a contact.", + type: SnackbarType.warning, + ); + return null; + } + + try { + final picked = await FlutterContacts.openExternalPick(); + if (picked == null) return null; + + final contact = + await FlutterContacts.getContact(picked.id, withProperties: true); + if (contact == null || contact.phones.isEmpty) { + showAppSnackbar( + title: "No Phone Number", + message: "Selected contact has no phone number.", + type: SnackbarType.warning, + ); + return null; + } + + final indiaPhones = contact.phones.where((p) { + final normalized = p.number.replaceAll(RegExp(r'[^0-9+]'), ''); + return normalized.startsWith('+91') || RegExp(r'^\d{10}$').hasMatch(normalized); + }).toList(); + + if (indiaPhones.isEmpty) { + showAppSnackbar( + title: "No Indian Number", + message: "Selected contact has no Indian (+91) phone number.", + type: SnackbarType.warning, + ); + return null; + } + + if (indiaPhones.length == 1) { + return _normalizeNumber(indiaPhones.first.number); + } + + return await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text("Choose a number"), + content: Column( + mainAxisSize: MainAxisSize.min, + children: indiaPhones + .map((p) => ListTile( + title: Text(p.number), + onTap: () => Navigator.of(ctx).pop(_normalizeNumber(p.number)), + )) + .toList(), + ), + ), + ); + } catch (e, st) { + logSafe("Error picking contact", level: LogLevel.error, error: e, stackTrace: st); + showAppSnackbar( + title: "Error", + message: "Failed to fetch contact.", + type: SnackbarType.error, + ); + return null; + } + } + + static String _normalizeNumber(String raw) { + final normalized = raw.replaceAll(RegExp(r'[^0-9]'), ''); + return normalized.length > 10 + ? normalized.substring(normalized.length - 10) + : normalized; + } +} diff --git a/lib/model/directory/add_contact_bottom_sheet.dart b/lib/model/directory/add_contact_bottom_sheet.dart index 77f9bce..22b561b 100644 --- a/lib/model/directory/add_contact_bottom_sheet.dart +++ b/lib/model/directory/add_contact_bottom_sheet.dart @@ -7,6 +7,7 @@ import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_text_style.dart'; import 'package:marco/model/directory/contact_model.dart'; +import 'package:marco/helpers/utils/contact_picker_helper.dart'; class AddContactBottomSheet extends StatefulWidget { final ContactModel? existingContact; @@ -184,8 +185,23 @@ class _AddContactBottomSheetState extends State { inputFormatters: inputType == TextInputType.phone ? [FilteringTextInputFormatter.digitsOnly] : [], - decoration: _inputDecoration("Enter $inputLabel") - .copyWith(counterText: ""), + decoration: _inputDecoration("Enter $inputLabel").copyWith( + counterText: "", + suffixIcon: inputType == TextInputType.phone + ? IconButton( + icon: const Icon(Icons.contact_phone, + color: Colors.blue), + onPressed: () async { + final selectedPhone = + await ContactPickerHelper.pickIndianPhoneNumber( + context); + if (selectedPhone != null) { + controller.text = selectedPhone; + } + }, + ) + : null, + ), validator: (value) { if (value == null || value.trim().isEmpty) return "$inputLabel is required"; @@ -195,7 +211,6 @@ class _AddContactBottomSheetState extends State { return "Enter valid phone number"; } } - if (inputType == TextInputType.emailAddress && !RegExp(r'^[\w.-]+@([\w-]+\.)+[\w-]{2,4}$') .hasMatch(trimmed)) { @@ -243,24 +258,24 @@ class _AddContactBottomSheetState extends State { Widget _buildPhoneList() => Column( children: List.generate(phoneControllers.length, (index) { - return Padding( - padding: const EdgeInsets.only(bottom: 12), - child: _buildLabeledRow( - "Phone Label", - phoneLabels[index], - ["Work", "Mobile", "Other"], - "Phone", - phoneControllers[index], - TextInputType.phone, - onRemove: phoneControllers.length > 1 - ? () { - phoneControllers.removeAt(index); - phoneLabels.removeAt(index); - } - : null, - ), - ); - }), + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: _buildLabeledRow( + "Phone Label", + phoneLabels[index], + ["Work", "Mobile", "Other"], + "Phone", + phoneControllers[index], + TextInputType.phone, + onRemove: phoneControllers.length > 1 + ? () { + phoneControllers.removeAt(index); + phoneLabels.removeAt(index); + } + : null, + ), + ); + }), ); Widget _popupSelector({