feat(contact): add contact picker functionality for selecting Indian phone numbers
This commit is contained in:
parent
574e7df447
commit
395444e8fc
90
lib/helpers/utils/contact_picker_helper.dart
Normal file
90
lib/helpers/utils/contact_picker_helper.dart
Normal file
@ -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<String?> 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<String>(
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<AddContactBottomSheet> {
|
||||
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<AddContactBottomSheet> {
|
||||
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<AddContactBottomSheet> {
|
||||
|
||||
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({
|
||||
|
Loading…
x
Reference in New Issue
Block a user