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(
@ -53,7 +51,7 @@ class DirectoryMainScreen extends StatelessWidget {
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
MyText.titleLarge( MyText.titleLarge(
'Directory', 'Directory',
@ -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(
children: [ crossAxisAlignment: CrossAxisAlignment.center,
Avatar( children: [
firstName: firstName, // Leading Icon
lastName: lastName, Avatar(
size: 31, firstName: firstName,
), lastName: lastName,
MySpacing.width(12), size: 45,
Expanded( ),
child: Column( MySpacing.width(12),
crossAxisAlignment: CrossAxisAlignment.start,
children: [ // Middle Content
MyText.titleSmall( Expanded(
contact.name, child: Column(
fontWeight: 700, crossAxisAlignment: CrossAxisAlignment.start,
color: Colors.black87, children: [
), MyText.titleSmall(
MyText.bodySmall( contact.name,
contact.organization, fontWeight: 600,
fontWeight: 500, overflow: TextOverflow.ellipsis,
), ),
], MyText.bodySmall(
), contact.organization,
), color: Colors.grey[700],
GestureDetector( overflow: TextOverflow.ellipsis,
onTap: () { ),
Get.to(() => MySpacing.height(6),
ContactDetailScreen(contact: contact));
}, // Launcher Row
child: const Icon(Icons.arrow_forward_ios, Wrap(
color: Colors.black, size: 15), spacing: 12,
), runSpacing: 6,
MySpacing.width(4), children: [
], if (email != '-')
),
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: [
GestureDetector( GestureDetector(
onTap: () => onTap: () =>
LauncherUtils.launchEmail(email), LauncherUtils.launchEmail(email),
child: const Padding( onLongPress: () =>
padding: LauncherUtils.copyToClipboard(
EdgeInsets.only(right: 8.0), email,
child: Icon(Icons.email_outlined, typeLabel: 'Email'),
color: Colors.blue, size: 25), 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,
overflow:
TextOverflow.ellipsis,
color: Colors.indigo,
decoration:
TextDecoration.underline,
),
),
],
), ),
), ),
Expanded( if (phone != '-')
child: GestureDetector( GestureDetector(
onTap: () =>
LauncherUtils.launchEmail(
email),
onLongPress: () =>
LauncherUtils.copyToClipboard(
email,
typeLabel: 'Email'),
child: MyText.bodyMedium(
email,
maxLines: 1,
overflow: TextOverflow.ellipsis,
color: Colors.blue,
fontWeight: 600,
textAlign: TextAlign.start,
decoration:
TextDecoration.underline,
),
),
),
],
),
),
// Phone Row with icons at the end
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(
phone, mainAxisSize: MainAxisSize.min,
maxLines: 1, children: [
overflow: TextOverflow.ellipsis, const Icon(Icons.phone_outlined,
color: Colors.blue, size: 16,
fontWeight: 600, color: Colors.indigo),
textAlign: TextAlign.start, MySpacing.width(4),
decoration: ConstrainedBox(
TextDecoration.underline, constraints:
const BoxConstraints(
maxWidth: 100),
child: MyText.labelSmall(
phone,
overflow:
TextOverflow.ellipsis,
color: Colors.indigo,
decoration:
TextDecoration.underline,
),
),
],
), ),
), ),
),
// WhatsApp Icon
Padding(
padding:
const EdgeInsets.only(right: 6.0),
child: GestureDetector(
onTap: () =>
LauncherUtils.launchWhatsApp(
phone),
child: const FaIcon(
FontAwesomeIcons.whatsapp,
color: Colors.green,
size: 25),
),
),
], ],
), ),
],
),
MySpacing.height(8), if (tags.isNotEmpty) ...[
// Tags Section MySpacing.height(4),
if (tags.isNotEmpty) MyText.labelSmall(
Padding( tags.join(', '),
padding: const EdgeInsets.only(top: 2), color: Colors.grey[500],
child: Row( maxLines: 1,
mainAxisAlignment: MainAxisAlignment.end, overflow: TextOverflow.ellipsis,
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(),
), ),
), ],
], ],
), ),
), ),
],
// 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: () =>
LauncherUtils.launchWhatsApp(phone),
child: const FaIcon(
FontAwesomeIcons.whatsapp,
color: Colors.green,
size: 20,
),
),
],
),
],
),
), ),
); );
}, },