From 50c20ce0ce0763ca9015efddf53bef3676a02df5 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Fri, 8 May 2026 18:41:06 +0530 Subject: [PATCH] feat: implement debounced live search with clear button, refactor UI components, and adjust layout spacing and image cropping across screens. --- .../screens/agent_search_screen.dart | 43 ++++++++++++++++- .../presentation/screens/contact_screen.dart | 2 +- .../presentation/widgets/home_header.dart | 47 +++++++------------ .../widgets/top_professionals_section.dart | 17 ++++--- .../screens/onboarding_screen.dart | 4 +- 5 files changed, 72 insertions(+), 41 deletions(-) diff --git a/lib/features/agents/presentation/screens/agent_search_screen.dart b/lib/features/agents/presentation/screens/agent_search_screen.dart index ce18a40..503856b 100644 --- a/lib/features/agents/presentation/screens/agent_search_screen.dart +++ b/lib/features/agents/presentation/screens/agent_search_screen.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -31,6 +33,7 @@ class AgentSearchScreen extends ConsumerStatefulWidget { class _AgentSearchScreenState extends ConsumerState { late TextEditingController _searchController; final ScrollController _scrollController = ScrollController(); + Timer? _searchDebounce; @override void initState() { @@ -67,11 +70,22 @@ class _AgentSearchScreenState extends ConsumerState { @override void dispose() { + _searchDebounce?.cancel(); _searchController.dispose(); _scrollController.dispose(); super.dispose(); } + /// Debounced live search — fires search() while the user types so results + /// update incrementally. Empty value resets to the default listing. + void _onSearchChanged(String value) { + _searchDebounce?.cancel(); + _searchDebounce = Timer(const Duration(milliseconds: 300), () { + if (!mounted) return; + ref.read(searchAgentsProvider.notifier).search(value.trim()); + }); + } + void _onScroll() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200) { @@ -185,10 +199,35 @@ class _AgentSearchScreenState extends ConsumerState { horizontal: 14, vertical: 10, ), + // Clear (×) button — resets the listing instantly. + suffixIcon: _searchController.text.isNotEmpty + ? IconButton( + icon: const Icon( + Icons.close, + size: 18, + color: AppColors.hintText, + ), + onPressed: () { + _searchDebounce?.cancel(); + _searchController.clear(); + setState(() {}); + ref + .read(searchAgentsProvider.notifier) + .search(''); + }, + ) + : null, ), + onChanged: (value) { + // Toggle suffix-icon visibility on each keystroke. + setState(() {}); + _onSearchChanged(value); + }, onSubmitted: (value) { - if (value.trim().isEmpty) return; - ref.read(searchAgentsProvider.notifier).search(value); + _searchDebounce?.cancel(); + ref + .read(searchAgentsProvider.notifier) + .search(value.trim()); }, ), ), diff --git a/lib/features/contact/presentation/screens/contact_screen.dart b/lib/features/contact/presentation/screens/contact_screen.dart index 09a5320..3e51ccb 100644 --- a/lib/features/contact/presentation/screens/contact_screen.dart +++ b/lib/features/contact/presentation/screens/contact_screen.dart @@ -29,7 +29,7 @@ class _ContactScreenState extends State { String _contactOfficeAddress = '123 Market Street'; String _contactOfficeCity = 'New York CA 234737'; String _pageTitle = 'Get In Touch'; - String _pageDescription = 'Have a question about a property or need assistance? Fill out the form below and our team will get back to you shortly.'; + String _pageDescription = 'Need assistance? Fill out the form below and our team will get back to you shortly.'; String _directionsUrl = 'https://maps.google.com/?q=123+Market+Street+New+York+CA+234737'; @override diff --git a/lib/features/home/presentation/widgets/home_header.dart b/lib/features/home/presentation/widgets/home_header.dart index 9a30d57..47d2aca 100644 --- a/lib/features/home/presentation/widgets/home_header.dart +++ b/lib/features/home/presentation/widgets/home_header.dart @@ -375,46 +375,35 @@ class _MenuDrawer extends ConsumerWidget { SizedBox( width: double.infinity, height: 54, - child: ElevatedButton( + child: ElevatedButton.icon( onPressed: () { Navigator.pop(context); context.push('/login'); }, + icon: const Icon( + Icons.login, + color: Colors.white, + size: 20, + ), + label: const Text( + 'Log In', + style: TextStyle( + fontFamily: 'Fractul', + fontSize: 16, + fontWeight: FontWeight.w700, + color: Colors.white, + ), + ), style: ElevatedButton.styleFrom( backgroundColor: AppColors.accentOrange, + foregroundColor: Colors.white, elevation: 0, + padding: const EdgeInsets.symmetric( + horizontal: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: 20, - width: 20, - child: Center( - child: Icon( - Icons.login, - color: Colors.white, - size: 18, - ), - ), - ), - const SizedBox(width: 8), - const Text( - 'Log In', - style: TextStyle( - fontFamily: 'Fractul', - fontSize: 16, - fontWeight: FontWeight.w700, - color: Colors.white, - ), - ), - ], - ), ), ), const SizedBox(height: 12), diff --git a/lib/features/home/presentation/widgets/top_professionals_section.dart b/lib/features/home/presentation/widgets/top_professionals_section.dart index c72b1c6..eb580ae 100644 --- a/lib/features/home/presentation/widgets/top_professionals_section.dart +++ b/lib/features/home/presentation/widgets/top_professionals_section.dart @@ -105,9 +105,10 @@ class _TopProfessionalsSectionState _buildEmptyState(_activeTab) else ...[ // Professional Cards - horizontal scroll - // Use screen-relative height so cards fit on all devices + // Use screen-relative height so cards fit on all devices. + // Bumped to 0.72 to fit the taller image (300) + content area. SizedBox( - height: MediaQuery.of(context).size.height * 0.64, + height: MediaQuery.of(context).size.height * 0.72, child: ListView.builder( // Unique key per tab forces Flutter to rebuild (not reuse) // the list items, preventing stale images from the other tab. @@ -318,10 +319,12 @@ class _TopProfessionalsSectionState crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - // Image (226px per Figma) + // Image — taller box for portrait photos so faces aren't cropped. + // Uses topCenter alignment as a fallback when the photo is much + // taller than the box (crops bottom, keeps the face visible). SizedBox( width: double.infinity, - height: 226, + height: 300, child: ClipRRect( borderRadius: const BorderRadius.vertical( top: Radius.circular(15), @@ -334,9 +337,9 @@ class _TopProfessionalsSectionState key: ValueKey('${_activeTab}_$imageUrl'), imageUrl: imageUrl, width: double.infinity, - height: 226, + height: 300, fit: BoxFit.cover, - alignment: Alignment.topCenter, + alignment: const Alignment(0, -0.4), errorWidget: (_) => _buildImagePlaceholder(index), ) : _buildImagePlaceholder(index), @@ -569,7 +572,7 @@ class _TopProfessionalsSectionState children: [ // Image placeholder Container( - height: 226, + height: 300, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(15)), diff --git a/lib/features/onboarding/presentation/screens/onboarding_screen.dart b/lib/features/onboarding/presentation/screens/onboarding_screen.dart index 03d4fda..996e375 100644 --- a/lib/features/onboarding/presentation/screens/onboarding_screen.dart +++ b/lib/features/onboarding/presentation/screens/onboarding_screen.dart @@ -23,7 +23,7 @@ class _OnboardingScreenState extends State { iconHeight: 100, title: 'Connect with Top Professionals', subtitle: - 'Professionals can list properties and find qualified buyers faster than ever.', + 'Agents can highlight their specializations for buyers to find faster than ever', ), _OnboardingData( bgAsset: 'assets/images/onboarding_2_bg.svg', @@ -32,7 +32,7 @@ class _OnboardingScreenState extends State { iconHeight: 75, title: 'Real help. Real professionals. Instantly.', subtitle: - 'Chat with professionals and get answers about properties instantly.', + 'Chat with agents instantly and find your service provider with ease', ), ];