feat(directory): enhance contact card UI with improved layout and interaction elements

This commit is contained in:
Vaibhav Surve 2025-07-04 17:29:26 +05:30
parent 549d8cce3c
commit becdec1a79

View File

@ -2,10 +2,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:marco/controller/directory/directory_controller.dart'; import 'package:marco/controller/directory/directory_controller.dart';
import 'package:marco/controller/project_controller.dart'; import 'package:marco/controller/project_controller.dart';
import 'package:marco/helpers/widgets/my_card.dart';
import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_spacing.dart';
import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_text.dart';
import 'package:marco/helpers/utils/my_shadow.dart';
import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/helpers/widgets/avatar.dart';
import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
import 'package:marco/model/directory/directory_filter_bottom_sheet.dart'; import 'package:marco/model/directory/directory_filter_bottom_sheet.dart';
@ -31,7 +29,7 @@ class DirectoryMainScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFFF5F5F5), backgroundColor: Colors.white,
appBar: PreferredSize( appBar: PreferredSize(
preferredSize: const Size.fromHeight(72), preferredSize: const Size.fromHeight(72),
child: AppBar( child: AppBar(
@ -308,218 +306,145 @@ class DirectoryMainScreen extends StatelessWidget {
final firstName = nameParts.first; final firstName = nameParts.first;
final lastName = nameParts.length > 1 ? nameParts.last : ""; final lastName = nameParts.length > 1 ? nameParts.last : "";
final tags = contact.tags.map((tag) => tag.name).toList(); final tags = contact.tags.map((tag) => tag.name).toList();
return MyCard.bordered( return InkWell(
margin: MySpacing.only(bottom: 2),
paddingAll: 8,
borderRadiusAll: 8,
shadow: MyShadow(
elevation: 1.5,
position: MyShadowPosition.bottom,
),
onTap: () { onTap: () {
Get.to(() => ContactDetailScreen(contact: contact)); Get.to(() => ContactDetailScreen(contact: contact));
}, },
child: Column( child: Padding(
crossAxisAlignment: CrossAxisAlignment.start, padding: const EdgeInsets.symmetric(
children: [ horizontal: 12.0, vertical: 10),
Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// Leading Icon
Avatar( Avatar(
firstName: firstName, firstName: firstName,
lastName: lastName, lastName: lastName,
size: 31, size: 45,
), ),
MySpacing.width(12), MySpacing.width(12),
// Middle Content
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
MyText.titleSmall( MyText.titleSmall(
contact.name, contact.name,
fontWeight: 700, fontWeight: 600,
color: Colors.black87, overflow: TextOverflow.ellipsis,
), ),
MyText.bodySmall( MyText.bodySmall(
contact.organization, contact.organization,
fontWeight: 500, color: Colors.grey[700],
overflow: TextOverflow.ellipsis,
), ),
], MySpacing.height(6),
),
), // Launcher Row
GestureDetector( Wrap(
onTap: () { spacing: 12,
Get.to(() => runSpacing: 6,
ContactDetailScreen(contact: contact));
},
child: const Icon(Icons.arrow_forward_ios,
color: Colors.black, size: 15),
),
MySpacing.width(4),
],
),
const Divider(),
if (contact.contactEmails.isNotEmpty ||
contact.contactPhones.isNotEmpty)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Email Row
if (contact.contactEmails.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Row(
children: [ children: [
if (email != '-')
GestureDetector( GestureDetector(
onTap: () => onTap: () =>
LauncherUtils.launchEmail(email), LauncherUtils.launchEmail(email),
child: const Padding(
padding:
EdgeInsets.only(right: 8.0),
child: Icon(Icons.email_outlined,
color: Colors.blue, size: 25),
),
),
Expanded(
child: GestureDetector(
onTap: () =>
LauncherUtils.launchEmail(
email),
onLongPress: () => onLongPress: () =>
LauncherUtils.copyToClipboard( LauncherUtils.copyToClipboard(
email, email,
typeLabel: 'Email'), typeLabel: 'Email'),
child: MyText.bodyMedium( child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.email_outlined,
size: 16,
color: Colors.indigo),
MySpacing.width(4),
ConstrainedBox(
constraints:
const BoxConstraints(
maxWidth: 120),
child: MyText.labelSmall(
email, email,
maxLines: 1, overflow:
overflow: TextOverflow.ellipsis, TextOverflow.ellipsis,
color: Colors.blue, color: Colors.indigo,
fontWeight: 600,
textAlign: TextAlign.start,
decoration: decoration:
TextDecoration.underline, TextDecoration.underline,
), ),
), ),
),
], ],
), ),
), ),
if (phone != '-')
// Phone Row with icons at the end GestureDetector(
if (contact.contactPhones.isNotEmpty)
Row(
children: [
// Phone Icon
Padding(
padding:
const EdgeInsets.only(right: 6.0),
child: GestureDetector(
onTap: () =>
LauncherUtils.launchPhone(phone),
child: const Icon(
Icons.phone_outlined,
color: Colors.blue,
size: 25),
),
),
// Phone number text
Expanded(
child: GestureDetector(
onTap: () => onTap: () =>
LauncherUtils.launchPhone(phone), LauncherUtils.launchPhone(phone),
onLongPress: () => onLongPress: () =>
LauncherUtils.copyToClipboard( LauncherUtils.copyToClipboard(
phone, phone,
typeLabel: 'Phone number'), typeLabel: 'Phone number'),
child: MyText.bodyMedium( child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.phone_outlined,
size: 16,
color: Colors.indigo),
MySpacing.width(4),
ConstrainedBox(
constraints:
const BoxConstraints(
maxWidth: 100),
child: MyText.labelSmall(
phone, phone,
maxLines: 1, overflow:
overflow: TextOverflow.ellipsis, TextOverflow.ellipsis,
color: Colors.blue, color: Colors.indigo,
fontWeight: 600,
textAlign: TextAlign.start,
decoration: decoration:
TextDecoration.underline, TextDecoration.underline,
), ),
), ),
],
),
),
],
), ),
// WhatsApp Icon if (tags.isNotEmpty) ...[
Padding( MySpacing.height(4),
padding: MyText.labelSmall(
const EdgeInsets.only(right: 6.0), tags.join(', '),
child: GestureDetector( color: Colors.grey[500],
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
// WhatsApp launcher icon
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.arrow_forward_ios,
color: Colors.grey, size: 16),
MySpacing.height(12),
if (phone != '-')
GestureDetector(
onTap: () => onTap: () =>
LauncherUtils.launchWhatsApp( LauncherUtils.launchWhatsApp(phone),
phone),
child: const FaIcon( child: const FaIcon(
FontAwesomeIcons.whatsapp, FontAwesomeIcons.whatsapp,
color: Colors.green, color: Colors.green,
size: 25), size: 20,
), ),
), ),
], ],
), ),
], ],
), ),
MySpacing.height(8),
// Tags Section
if (tags.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
reverse:
true, // ensures scroll starts from right
child: Row(
children: tags.map((name) {
return Container(
margin:
const EdgeInsets.only(left: 6),
child: TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding:
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
),
backgroundColor:
const Color.fromARGB(
255, 179, 207, 246),
tapTargetSize:
MaterialTapTargetSize
.shrinkWrap,
minimumSize: Size.zero,
visualDensity:
VisualDensity.standard,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5),
),
),
child: Text(
name,
style: const TextStyle(
fontSize: 10,
color: Color.fromARGB(
255, 0, 0, 0),
height: 1.2,
),
),
),
);
}).toList(),
),
),
],
),
),
],
), ),
); );
}, },