feat: implement debounced live search with clear button, refactor UI components, and adjust layout spacing and image cropping across screens.
This commit is contained in:
@@ -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<AgentSearchScreen> {
|
||||
late TextEditingController _searchController;
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
Timer? _searchDebounce;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -67,11 +70,22 @@ class _AgentSearchScreenState extends ConsumerState<AgentSearchScreen> {
|
||||
|
||||
@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<AgentSearchScreen> {
|
||||
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());
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user