From fed89cbef2c5876a3308941c5bb5b323d5ba0666 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Sun, 15 Mar 2026 00:03:38 +0530 Subject: [PATCH] feat: Implement contact form clearing, refactor chat avatar display to use S3Image, add unread count to notification filter, and enhance agent detail layout. --- .../screens/agent_detail_screen.dart | 60 ++++++------- .../presentation/screens/contact_screen.dart | 7 +- .../presentation/screens/chat_screen.dart | 89 ++++++++++++------- .../screens/notifications_screen.dart | 8 +- 4 files changed, 97 insertions(+), 67 deletions(-) diff --git a/lib/features/agents/presentation/screens/agent_detail_screen.dart b/lib/features/agents/presentation/screens/agent_detail_screen.dart index 4e0af20..a1d9c2e 100644 --- a/lib/features/agents/presentation/screens/agent_detail_screen.dart +++ b/lib/features/agents/presentation/screens/agent_detail_screen.dart @@ -479,6 +479,8 @@ class _AgentDetailScreenState extends ConsumerState { fontSize: 14, color: AppColors.primaryDark, ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 8), @@ -502,13 +504,14 @@ class _AgentDetailScreenState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.center, children: [ // Name + Verified badge + (Verified Expert) — badge after last name - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.baseline, - textBaseline: TextBaseline.alphabetic, + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 4, + runSpacing: 4, children: [ Text( - agent.firstName, + '${agent.firstName} ${agent.lastName}', style: const TextStyle( fontFamily: 'Fractul', fontSize: 18, @@ -516,35 +519,26 @@ class _AgentDetailScreenState extends ConsumerState { color: AppColors.primaryDark, ), ), - const SizedBox(width: 4), - Text( - agent.lastName, - style: const TextStyle( - fontFamily: 'Fractul', - fontSize: 18, - fontWeight: FontWeight.w400, - color: AppColors.primaryDark, - ), - ), if (agent.isVerified) ...[ - const SizedBox(width: 6), - const Padding( - padding: EdgeInsets.only(bottom: 1), - child: Icon( - Icons.verified, - size: 16, - color: Color(0xFF2196F3), - ), - ), - const SizedBox(width: 6), - const Text( - '(Verified Expert)', - style: TextStyle( - fontFamily: 'SourceSerif4', - fontSize: 14, - fontWeight: FontWeight.w500, - color: Color(0xFF638559), - ), + Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Icon( + Icons.verified, + size: 16, + color: Color(0xFF2196F3), + ), + SizedBox(width: 4), + Text( + '(Verified Expert)', + style: TextStyle( + fontFamily: 'SourceSerif4', + fontSize: 14, + fontWeight: FontWeight.w500, + color: Color(0xFF638559), + ), + ), + ], ), ], ], diff --git a/lib/features/contact/presentation/screens/contact_screen.dart b/lib/features/contact/presentation/screens/contact_screen.dart index 96d98b4..8dab679 100644 --- a/lib/features/contact/presentation/screens/contact_screen.dart +++ b/lib/features/contact/presentation/screens/contact_screen.dart @@ -241,7 +241,12 @@ class _ContactScreenState extends State { children: [ Expanded( child: GestureDetector( - onTap: () => context.pop(), + onTap: () { + _nameController.clear(); + _emailController.clear(); + _phoneController.clear(); + _messageController.clear(); + }, child: Container( height: 46, decoration: BoxDecoration( diff --git a/lib/features/messaging/presentation/screens/chat_screen.dart b/lib/features/messaging/presentation/screens/chat_screen.dart index a7e4c93..ab98e47 100644 --- a/lib/features/messaging/presentation/screens/chat_screen.dart +++ b/lib/features/messaging/presentation/screens/chat_screen.dart @@ -2,7 +2,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; -import 'package:real_estate_mobile/config/app_config.dart'; import 'package:real_estate_mobile/core/constants/app_colors.dart'; import 'package:real_estate_mobile/core/utils/image_url_resolver.dart'; import 'package:real_estate_mobile/core/widgets/s3_image.dart'; @@ -149,18 +148,6 @@ class _ChatScreenState extends ConsumerState { return '${parts[0][0]}${parts[1][0]}'.toUpperCase(); } - String _resolveAvatarUrl(String? avatarKey) { - if (avatarKey == null || avatarKey.isEmpty) return ''; - if (avatarKey.startsWith('http://') || avatarKey.startsWith('https://')) { - return avatarKey; - } - final base = AppConfig.storageBaseUrl; - if (avatarKey.startsWith('/uploads')) { - return '$base$avatarKey'; - } - return '$base/$avatarKey'; - } - String _formatFileSize(int? bytes) { if (bytes == null || bytes == 0) return ''; if (bytes < 1024) return '$bytes B'; @@ -209,6 +196,7 @@ class _ChatScreenState extends ConsumerState { _buildHeader( name: otherPartyName, isOnline: isOnline, + avatar: otherPartyAvatar, ), Expanded( child: messagingState.isLoadingMessages && messages.isEmpty @@ -242,7 +230,10 @@ class _ChatScreenState extends ConsumerState { Widget _buildHeader({ required String name, required bool isOnline, + String? avatar, }) { + final initials = _getInitials(name); + return Padding( padding: const EdgeInsets.fromLTRB(12, 10, 12, 6), child: Row( @@ -252,7 +243,7 @@ class _ChatScreenState extends ConsumerState { onTap: _handleBack, behavior: HitTestBehavior.opaque, child: const Padding( - padding: EdgeInsets.only(right: 16), + padding: EdgeInsets.only(right: 12), child: Icon( Icons.arrow_back, size: 22, @@ -260,6 +251,38 @@ class _ChatScreenState extends ConsumerState { ), ), ), + // Avatar + Stack( + children: [ + ClipOval( + child: (avatar != null && avatar.isNotEmpty) + ? S3Image( + imageUrl: avatar, + width: 40, + height: 40, + fit: BoxFit.cover, + errorWidget: (_) => + _AvatarFallback(letter: initials, size: 40), + ) + : _AvatarFallback(letter: initials, size: 40), + ), + if (isOnline) + Positioned( + right: 0, + bottom: 0, + child: Container( + width: 10, + height: 10, + decoration: BoxDecoration( + color: const Color(0xFF4CAF50), + shape: BoxShape.circle, + border: Border.all(color: Colors.white, width: 2), + ), + ), + ), + ], + ), + const SizedBox(width: 10), // Name and active status Expanded( child: Column( @@ -398,7 +421,6 @@ class _ChatScreenState extends ConsumerState { String? headline, required bool isOnline, }) { - final avatarUrl = _resolveAvatarUrl(avatar); final initials = _getInitials(name); return Padding( @@ -410,13 +432,13 @@ class _ChatScreenState extends ConsumerState { Stack( children: [ ClipOval( - child: avatarUrl.isNotEmpty - ? CachedNetworkImage( - imageUrl: avatarUrl, + child: (avatar != null && avatar.isNotEmpty) + ? S3Image( + imageUrl: avatar, width: 80, height: 80, fit: BoxFit.cover, - errorWidget: (_, e, s) => + errorWidget: (_) => _AvatarFallback(letter: initials, size: 80), ) : _AvatarFallback(letter: initials, size: 80), @@ -440,17 +462,21 @@ class _ChatScreenState extends ConsumerState { const SizedBox(height: 14), // Name with pronouns and orange dot Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - name, - style: const TextStyle( - fontFamily: 'Fractul', - fontSize: 14, - fontWeight: FontWeight.w600, - color: AppColors.primaryDark, + Flexible( + child: Text( + name, + style: const TextStyle( + fontFamily: 'Fractul', + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.primaryDark, + ), + overflow: TextOverflow.ellipsis, ), ), - const SizedBox(width: 4), + const SizedBox(width: 6), const Text( '(He/Him)', style: TextStyle( @@ -1041,17 +1067,16 @@ class _ChatScreenState extends ConsumerState { // -- Message Avatar -- Widget _buildMessageAvatar(String? avatarKey, String name, double size) { - final avatarUrl = _resolveAvatarUrl(avatarKey); final initials = _getInitials(name); return ClipOval( - child: avatarUrl.isNotEmpty - ? CachedNetworkImage( - imageUrl: avatarUrl, + child: (avatarKey != null && avatarKey.isNotEmpty) + ? S3Image( + imageUrl: avatarKey, width: size, height: size, fit: BoxFit.cover, - errorWidget: (_, e, s) => + errorWidget: (_) => _AvatarFallback(letter: initials, size: size), ) : _AvatarFallback(letter: initials, size: size), diff --git a/lib/features/notifications/presentation/screens/notifications_screen.dart b/lib/features/notifications/presentation/screens/notifications_screen.dart index 931dd37..e6e0d27 100644 --- a/lib/features/notifications/presentation/screens/notifications_screen.dart +++ b/lib/features/notifications/presentation/screens/notifications_screen.dart @@ -83,7 +83,13 @@ class _NotificationsScreenState extends ConsumerState { children: [ _buildFilterChip('All', 'all'), const SizedBox(width: 8), - _buildFilterChip('Unread', 'unread'), + Builder(builder: (context) { + final unreadCount = ref.watch(unreadCountProvider); + final label = unreadCount > 0 + ? 'Unread ($unreadCount)' + : 'Unread'; + return _buildFilterChip(label, 'unread'); + }), const Spacer(), GestureDetector( onTap: () {