Commit ebb11608 authored by Sai Srinivas's avatar Sai Srinivas
Browse files

fixes

parent a5ccb211
...@@ -74,6 +74,8 @@ PODS: ...@@ -74,6 +74,8 @@ PODS:
- GoogleUtilities/UserDefaults (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0)
- nanopb (~> 3.30910.0) - nanopb (~> 3.30910.0)
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_contacts (0.0.1):
- Flutter
- flutter_downloader (0.0.1): - flutter_downloader (0.0.1):
- Flutter - Flutter
- flutter_image_compress_common (1.0.0): - flutter_image_compress_common (1.0.0):
...@@ -212,6 +214,7 @@ DEPENDENCIES: ...@@ -212,6 +214,7 @@ DEPENDENCIES:
- firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
- flutter_downloader (from `.symlinks/plugins/flutter_downloader/ios`) - flutter_downloader (from `.symlinks/plugins/flutter_downloader/ios`)
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`) - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
...@@ -277,6 +280,8 @@ EXTERNAL SOURCES: ...@@ -277,6 +280,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_messaging/ios" :path: ".symlinks/plugins/firebase_messaging/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_contacts:
:path: ".symlinks/plugins/flutter_contacts/ios"
flutter_downloader: flutter_downloader:
:path: ".symlinks/plugins/flutter_downloader/ios" :path: ".symlinks/plugins/flutter_downloader/ios"
flutter_image_compress_common: flutter_image_compress_common:
...@@ -342,6 +347,7 @@ SPEC CHECKSUMS: ...@@ -342,6 +347,7 @@ SPEC CHECKSUMS:
FirebaseInstallations: 9980995bdd06ec8081dfb6ab364162bdd64245c3 FirebaseInstallations: 9980995bdd06ec8081dfb6ab364162bdd64245c3
FirebaseMessaging: 2b9f56aa4ed286e1f0ce2ee1d413aabb8f9f5cb9 FirebaseMessaging: 2b9f56aa4ed286e1f0ce2ee1d413aabb8f9f5cb9
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
flutter_downloader: b7301ae057deadd4b1650dc7c05375f10ff12c39 flutter_downloader: b7301ae057deadd4b1650dc7c05375f10ff12c39
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
flutter_inappwebview_ios: 8d8d2c6290a3c4787cad303603662fac9a788f75 flutter_inappwebview_ios: 8d8d2c6290a3c4787cad303603662fac9a788f75
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
9AA45C142DD33732002B8BD0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9AA45C132DD33732002B8BD0 /* GoogleService-Info.plist */; }; 9AA45C142DD33732002B8BD0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9AA45C132DD33732002B8BD0 /* GoogleService-Info.plist */; };
9AE33D4D2EB6295300E40DCB /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AE33D4C2EB6295200E40DCB /* Contacts.framework */; };
9AED47FD9ED95B95B39F9EF0 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 790ED1546FAD647888E2BE7A /* Pods_RunnerTests.framework */; }; 9AED47FD9ED95B95B39F9EF0 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 790ED1546FAD647888E2BE7A /* Pods_RunnerTests.framework */; };
C1A6DB5D3568BFCBCBACC04B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03FAB6DB27A278FB15FD74E4 /* Pods_Runner.framework */; }; C1A6DB5D3568BFCBCBACC04B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03FAB6DB27A278FB15FD74E4 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
...@@ -67,6 +68,7 @@ ...@@ -67,6 +68,7 @@
9AA45C132DD33732002B8BD0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; }; 9AA45C132DD33732002B8BD0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
9AA45C152DD3487F002B8BD0 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = "<group>"; }; 9AA45C152DD3487F002B8BD0 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = "<group>"; };
9AA45C162DD3489F002B8BD0 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; }; 9AA45C162DD3489F002B8BD0 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
9AE33D4C2EB6295200E40DCB /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; };
B9F0367623E7C0B4F78A4435 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; }; B9F0367623E7C0B4F78A4435 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
C968B3C3D5D358B0AFB94F8C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; C968B3C3D5D358B0AFB94F8C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -77,6 +79,7 @@ ...@@ -77,6 +79,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
C1A6DB5D3568BFCBCBACC04B /* Pods_Runner.framework in Frameworks */, C1A6DB5D3568BFCBCBACC04B /* Pods_Runner.framework in Frameworks */,
9AE33D4D2EB6295300E40DCB /* Contacts.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -165,6 +168,7 @@ ...@@ -165,6 +168,7 @@
FE7E98E00E75972236D03914 /* Frameworks */ = { FE7E98E00E75972236D03914 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
9AE33D4C2EB6295200E40DCB /* Contacts.framework */,
03FAB6DB27A278FB15FD74E4 /* Pods_Runner.framework */, 03FAB6DB27A278FB15FD74E4 /* Pods_Runner.framework */,
790ED1546FAD647888E2BE7A /* Pods_RunnerTests.framework */, 790ED1546FAD647888E2BE7A /* Pods_RunnerTests.framework */,
); );
......
...@@ -2,10 +2,6 @@ ...@@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<!-- here is ok or not this BELOW the URL types section, NOT inside it -->
<key>NSContactsUsageDescription</key>
<string>This app needs access to your contacts to add and view contacts.</string>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
...@@ -62,6 +58,8 @@ ...@@ -62,6 +58,8 @@
<string>This app requires to access your photo library in order to upload image proofs etc.</string> <string>This app requires to access your photo library in order to upload image proofs etc.</string>
<key>NSUserNotificationsUsageDescription</key> <key>NSUserNotificationsUsageDescription</key>
<string>This app needs to access notifications.</string> <string>This app needs to access notifications.</string>
<key>NSContactsUsageDescription</key>
<string>This app needs access to your contacts to add and view contacts.</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>
......
...@@ -11,7 +11,6 @@ import 'package:permission_handler/permission_handler.dart'; ...@@ -11,7 +11,6 @@ import 'package:permission_handler/permission_handler.dart';
import '../../Utils/custom_snackbar.dart'; import '../../Utils/custom_snackbar.dart';
class ContactListScreen extends StatefulWidget { class ContactListScreen extends StatefulWidget {
const ContactListScreen({super.key}); const ContactListScreen({super.key});
...@@ -25,10 +24,12 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -25,10 +24,12 @@ class _ContactListScreenState extends State<ContactListScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final homeProv = Provider.of<HomescreenNotifier>(context, listen: false); WidgetsBinding.instance.addPostFrameCallback((_) {
Future.microtask(() { final homeProv = Provider.of<HomescreenNotifier>(context, listen: false);
final provider = Provider.of<ContactProvider>(context, listen: false); Future.microtask(() {
provider.fetchContactList(homeProv.session, homeProv.empId, 1); final provider = Provider.of<ContactProvider>(context, listen: false);
provider.fetchContactList(homeProv.session, homeProv.empId, 1);
});
}); });
} }
...@@ -47,23 +48,46 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -47,23 +48,46 @@ class _ContactListScreenState extends State<ContactListScreen> {
context: context, context: context,
message: "Opening contact form for $name...", message: "Opening contact form for $name...",
); );
// var permissionStatus = await Permission.contacts.request();
// // Check and request contact permission
// // var permissionStatus = await Permission.contacts.status;
// if (permissionStatus.isDenied) {
// final requestedStatus = await Permission.contacts.request();
// if (!requestedStatus.isGranted) {
// CustomSnackBar.hide(context);
// CustomSnackBar.showError(
// context: context,
// message: "Contact permission is required",
// );
// return;
// }
// }
// if (permissionStatus.isPermanentlyDenied) {
// CustomSnackBar.hide(context);
// _showPermissionDialog();
// return;
// }
// Check current status
var status = await Permission.contacts.status;
// Only request if not already granted
if (!await status.isGranted) {
status = await Permission.contacts.request();
}
// Check and request contact permission // Handle denied or permanently denied
var permissionStatus = await Permission.contacts.status; if (await status.isDenied) {
CustomSnackBar.hide(context);
if (permissionStatus.isDenied) { CustomSnackBar.showError(
final requestedStatus = await Permission.contacts.request(); context: context,
if (!requestedStatus.isGranted) { message: "Contact permission is required",
CustomSnackBar.hide(context); );
CustomSnackBar.showError( return;
context: context,
message: "Contact permission is required",
);
return;
}
} }
if (permissionStatus.isPermanentlyDenied) { if (await status.isPermanentlyDenied) {
CustomSnackBar.hide(context); CustomSnackBar.hide(context);
_showPermissionDialog(); _showPermissionDialog();
return; return;
...@@ -73,9 +97,10 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -73,9 +97,10 @@ class _ContactListScreenState extends State<ContactListScreen> {
final cleanPhoneNumber = _cleanPhoneNumber(phoneNumber); final cleanPhoneNumber = _cleanPhoneNumber(phoneNumber);
// ✅ Instead of inserting contact, open system form // ✅ Instead of inserting contact, open system form
final newContact = Contact() final newContact =
..name = Name(first: name) Contact()
..phones = [Phone(cleanPhoneNumber)]; ..name = Name(first: name)
..phones = [Phone(cleanPhoneNumber)];
// 🟢 This opens the phone’s native “Add contact” screen (prefilled) // 🟢 This opens the phone’s native “Add contact” screen (prefilled)
await FlutterContacts.openExternalInsert(newContact); await FlutterContacts.openExternalInsert(newContact);
...@@ -88,7 +113,6 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -88,7 +113,6 @@ class _ContactListScreenState extends State<ContactListScreen> {
context: context, context: context,
message: "Contact form opened for $name", message: "Contact form opened for $name",
); );
} catch (e) { } catch (e) {
debugPrint("❌ Error opening contact form: $e"); debugPrint("❌ Error opening contact form: $e");
CustomSnackBar.hide(context); CustomSnackBar.hide(context);
...@@ -122,24 +146,26 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -122,24 +146,26 @@ class _ContactListScreenState extends State<ContactListScreen> {
void _showPermissionDialog() { void _showPermissionDialog() {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) => AlertDialog( builder:
title: const Text("Permission Required"), (BuildContext context) => AlertDialog(
content: const Text( title: const Text("Permission Required"),
"Contact permission is permanently denied. Please enable it in app settings to add contacts."), content: const Text(
actions: [ "Contact permission is permanently denied. Please enable it in app settings to add contacts.",
TextButton( ),
onPressed: () => Navigator.pop(context), actions: [
child: const Text("Cancel"), TextButton(
), onPressed: () => Navigator.pop(context),
TextButton( child: const Text("Cancel"),
onPressed: () { ),
Navigator.pop(context); TextButton(
openAppSettings(); onPressed: () {
}, Navigator.pop(context);
child: const Text("Open Settings"), openAppSettings();
},
child: const Text("Open Settings"),
),
],
), ),
],
),
); );
} }
...@@ -182,14 +208,24 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -182,14 +208,24 @@ class _ContactListScreenState extends State<ContactListScreen> {
onTap: () => Navigator.pop(context, true), onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset( child: SvgPicture.asset(
"assets/svg/appbar_back_button.svg", "assets/svg/appbar_back_button.svg",
height: isSmallScreen ? 22 : isTablet ? 28 : 25, height:
isSmallScreen
? 22
: isTablet
? 28
: 25,
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Text( Text(
"Contacts", "Contacts",
style: TextStyle( style: TextStyle(
fontSize: isSmallScreen ? 16 : isTablet ? 20 : 18, fontSize:
isSmallScreen
? 16
: isTablet
? 20
: 18,
fontFamily: "Plus Jakarta Sans", fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black87, color: Colors.black87,
...@@ -210,18 +246,22 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -210,18 +246,22 @@ class _ContactListScreenState extends State<ContactListScreen> {
child: Text( child: Text(
"No contacts found", "No contacts found",
style: TextStyle( style: TextStyle(
color: Colors.black54, color: Colors.black54,
fontSize: 16, fontSize: 16,
fontFamily: "Plus Jakarta Sans"), fontFamily: "Plus Jakarta Sans",
),
), ),
); );
} }
final filteredContacts = provider.contactList final filteredContacts =
.where((c) => (c.name ?? '') provider.contactList
.toLowerCase() .where(
.contains(_searchController.text.toLowerCase())) (c) => (c.name ?? '').toLowerCase().contains(
.toList(); _searchController.text.toLowerCase(),
),
)
.toList();
return Padding( return Padding(
padding: EdgeInsets.symmetric(horizontal: isSmallScreen ? 10 : 16), padding: EdgeInsets.symmetric(horizontal: isSmallScreen ? 10 : 16),
...@@ -251,7 +291,8 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -251,7 +291,8 @@ class _ContactListScreenState extends State<ContactListScreen> {
itemCount: filteredContacts.length, itemCount: filteredContacts.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final contact = filteredContacts[index]; final contact = filteredContacts[index];
final canSwipe = contact.mobileNumber != null && final canSwipe =
contact.mobileNumber != null &&
contact.mobileNumber!.trim().length == 10; contact.mobileNumber!.trim().length == 10;
return Padding( return Padding(
...@@ -259,53 +300,68 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -259,53 +300,68 @@ class _ContactListScreenState extends State<ContactListScreen> {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
child: Slidable( child: Slidable(
key: Key(contact.mobileNumber?.toString() ?? '${contact.name}_$index'), key: Key(
contact.mobileNumber?.toString() ??
'${contact.name}_$index',
),
// Left swipe (Add to contact) // Left swipe (Add to contact)
startActionPane: canSwipe startActionPane:
? ActionPane( canSwipe
motion: const ScrollMotion(), ? ActionPane(
extentRatio: 0.4, // Width percentage motion: const ScrollMotion(),
dragDismissible: false, extentRatio: 0.4, // Width percentage
children: [ dragDismissible: false,
// This will automatically take full height of the list item children: [
SlidableAction( // This will automatically take full height of the list item
onPressed: (_) async { SlidableAction(
_addToContact( onPressed: (_) async {
contact.name ?? 'Contact', _addToContact(
contact.mobileNumber ?? '', contact.name ?? 'Contact',
); contact.mobileNumber ?? '',
}, );
backgroundColor: const Color(0xFFE5FFE5), },
foregroundColor: const Color(0xFF38903c), backgroundColor: const Color(
icon: Icons.contact_page, 0xFFE5FFE5,
label: 'Add to Contact', ),
autoClose: true, foregroundColor: const Color(
), 0xFF38903c,
], ),
) icon: Icons.contact_page,
: null, label: 'Add to Contact',
autoClose: true,
),
],
)
: null,
// Right swipe (WhatsApp) // Right swipe (WhatsApp)
endActionPane: canSwipe endActionPane:
? ActionPane( canSwipe
motion: const ScrollMotion(), ? ActionPane(
extentRatio: 0.4, // Width percentage motion: const ScrollMotion(),
dragDismissible: false, extentRatio: 0.4, // Width percentage
children: [ dragDismissible: false,
SlidableAction( children: [
onPressed: (_) async { SlidableAction(
_openWhatsApp(contact.mobileNumber ?? ''); onPressed: (_) async {
}, _openWhatsApp(
backgroundColor: const Color(0xFFE9FFE8), contact.mobileNumber ?? '',
foregroundColor: const Color(0xFF4CB443), );
icon: Icons.chat, },
label: 'WhatsApp', backgroundColor: const Color(
autoClose: true, 0xFFE9FFE8,
), ),
], foregroundColor: const Color(
) 0xFF4CB443,
: null, ),
icon: Icons.chat,
label: 'WhatsApp',
autoClose: true,
),
],
)
: null,
// The main content - this determines the height // The main content - this determines the height
child: Container( child: Container(
...@@ -314,7 +370,9 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -314,7 +370,9 @@ class _ContactListScreenState extends State<ContactListScreen> {
borderRadius: BorderRadius.circular(1), borderRadius: BorderRadius.circular(1),
), ),
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 10), horizontal: 14,
vertical: 10,
),
child: Row( child: Row(
children: [ children: [
// 👤 Avatar // 👤 Avatar
...@@ -326,23 +384,43 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -326,23 +384,43 @@ class _ContactListScreenState extends State<ContactListScreen> {
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: (contact.profileImage != null && child:
contact.profileImage!.isNotEmpty) (contact.profileImage != null &&
? Image.network( contact
contact.profileImage!, .profileImage!
fit: BoxFit.cover, .isNotEmpty)
errorBuilder: (context, error, stackTrace) { ? Image.network(
return const Icon(Icons.person_outline, contact.profileImage!,
color: Color(0xFF2d2d2d)); fit: BoxFit.cover,
}, errorBuilder: (
loadingBuilder: (context, child, loadingProgress) { context,
if (loadingProgress == null) return child; error,
return const Center( stackTrace,
child: CircularProgressIndicator(strokeWidth: 2), ) {
); return const Icon(
}, Icons.person_outline,
) color: Color(0xFF2d2d2d),
: const Icon(Icons.person_outline, color: Color(0xFF2d2d2d)), );
},
loadingBuilder: (
context,
child,
loadingProgress,
) {
if (loadingProgress == null)
return child;
return const Center(
child:
CircularProgressIndicator(
strokeWidth: 2,
),
);
},
)
: const Icon(
Icons.person_outline,
color: Color(0xFF2d2d2d),
),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
...@@ -350,7 +428,8 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -350,7 +428,8 @@ class _ContactListScreenState extends State<ContactListScreen> {
// 📝 Info // 📝 Info
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Text( Text(
contact.name ?? "-", contact.name ?? "-",
...@@ -380,15 +459,25 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -380,15 +459,25 @@ class _ContactListScreenState extends State<ContactListScreen> {
if (contact.mobileNumber != null && if (contact.mobileNumber != null &&
contact.mobileNumber!.trim().length == 10) contact.mobileNumber!.trim().length == 10)
IconButton( IconButton(
icon: const Icon(Icons.call, color: Colors.green, size: 24), icon: const Icon(
Icons.call,
color: Colors.green,
size: 24,
),
onPressed: () async { onPressed: () async {
final phone = contact.mobileNumber!.trim(); final phone =
final Uri callUri = Uri(scheme: 'tel', path: phone); contact.mobileNumber!.trim();
final Uri callUri = Uri(
scheme: 'tel',
path: phone,
);
if (await canLaunchUrl(callUri)) { if (await canLaunchUrl(callUri)) {
await launchUrl(callUri); await launchUrl(callUri);
} else { } else {
debugPrint("❌ Could not launch dialer for $phone"); debugPrint(
"❌ Could not launch dialer for $phone",
);
CustomSnackBar.showError( CustomSnackBar.showError(
context: context, context: context,
message: "Unable to open dialer", message: "Unable to open dialer",
...@@ -412,4 +501,4 @@ class _ContactListScreenState extends State<ContactListScreen> { ...@@ -412,4 +501,4 @@ class _ContactListScreenState extends State<ContactListScreen> {
), ),
); );
} }
} }
\ No newline at end of file
...@@ -61,10 +61,10 @@ packages: ...@@ -61,10 +61,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.12.0" version: "2.13.0"
auto_size_text: auto_size_text:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -445,10 +445,10 @@ packages: ...@@ -445,10 +445,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" version: "1.3.3"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
...@@ -1188,26 +1188,26 @@ packages: ...@@ -1188,26 +1188,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.8" version: "11.0.1"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.9" version: "3.0.10"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
lints: lints:
dependency: transitive dependency: transitive
description: description:
...@@ -2033,10 +2033,10 @@ packages: ...@@ -2033,10 +2033,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.3.1" version: "15.0.0"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
......
...@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev ...@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.107+115 version: 1.0.107+116
environment: environment:
sdk: ^3.7.2 sdk: ^3.7.2
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment