diff --git a/example/lib/chat_view_list_screen.dart b/example/lib/chat_view_list_screen.dart index fbd0c59d..dcea2210 100644 --- a/example/lib/chat_view_list_screen.dart +++ b/example/lib/chat_view_list_screen.dart @@ -96,6 +96,7 @@ class _ChatViewListScreenState extends State { ), searchConfig: SearchConfig( textEditingController: _searchController, + debounceDuration: const Duration(milliseconds: 300), border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10)), ), diff --git a/lib/src/controller/chat_list_view_controller.dart b/lib/src/controller/chat_list_view_controller.dart index 47c578a4..74e8ca3f 100644 --- a/lib/src/controller/chat_list_view_controller.dart +++ b/lib/src/controller/chat_list_view_controller.dart @@ -54,7 +54,9 @@ class ChatViewListController { if (initialChatList[i] case final chat) chat.id: chat, }; - chatListMap = chatsMap; + chatListMap + ..clear() + ..addAll(chatsMap); // Adds the current chat map to the stream controller // after the first frame render. @@ -67,10 +69,10 @@ class ChatViewListController { /// Stores and manages chat items by their unique IDs. /// A map is used for efficient lookup, update, and removal of chats /// by their unique id. - Map chatListMap = {}; + final Map chatListMap = {}; /// Provides scroll controller for chat list. - ScrollController scrollController; + final ScrollController scrollController; final bool disposeOtherResources; diff --git a/lib/src/models/config_models/chat_view_list/search_config.dart b/lib/src/models/config_models/chat_view_list/search_config.dart index 4f169e10..8544c8d2 100644 --- a/lib/src/models/config_models/chat_view_list/search_config.dart +++ b/lib/src/models/config_models/chat_view_list/search_config.dart @@ -52,6 +52,7 @@ class SearchConfig { this.border, this.suffixIcon, this.onSearch, + this.debounceDuration, this.decoration, this.maxLength, }); @@ -122,6 +123,9 @@ class SearchConfig { /// Callback function that is called when the search text changes. final SearchUserCallback? onSearch; + /// Duration to debounce the search callback. + final Duration? debounceDuration; + /// Decoration for the search text field. final InputDecoration? decoration; diff --git a/lib/src/utils/debounce.dart b/lib/src/utils/debounce.dart index 83b3a85a..e605018d 100644 --- a/lib/src/utils/debounce.dart +++ b/lib/src/utils/debounce.dart @@ -25,18 +25,17 @@ import 'dart:async'; import 'package:flutter/material.dart'; class Debouncer { - Timer? _debounce; - Duration duration; - Debouncer(this.duration); - void run(VoidCallback callbackAfterTimeLapsed, - VoidCallback callbackBeforeTimeLapsed) { + final Duration duration; + Timer? _debounce; + + void run({required VoidCallback onComplete, VoidCallback? onInterrupt}) { if (_debounce?.isActive ?? false) { - callbackBeforeTimeLapsed(); + onInterrupt?.call(); _debounce?.cancel(); } - _debounce = Timer(duration, callbackAfterTimeLapsed); + _debounce = Timer(duration, onComplete); } void dispose() { diff --git a/lib/src/widgets/chat_view_list/search_text_field.dart b/lib/src/widgets/chat_view_list/search_text_field.dart index 78ac5b11..fb45220b 100644 --- a/lib/src/widgets/chat_view_list/search_text_field.dart +++ b/lib/src/widgets/chat_view_list/search_text_field.dart @@ -25,6 +25,7 @@ import 'package:flutter/material.dart'; import '../../controller/chat_list_view_controller.dart'; import '../../models/config_models/chat_view_list/search_config.dart'; +import '../../utils/debounce.dart'; import '../../utils/package_strings.dart'; class SearchTextField extends StatefulWidget { @@ -50,6 +51,10 @@ class SearchTextField extends StatefulWidget { class _SearchTextFieldState extends State { final ValueNotifier _inputText = ValueNotifier(''); + late final _debouncer = _config.debounceDuration == null + ? null + : Debouncer(_config.debounceDuration!); + SearchConfig get _config => widget.config; InputBorder get _outlineBorder => @@ -121,6 +126,14 @@ class _SearchTextFieldState extends State { FutureOr _onSearchChanged(String value) async { _inputText.value = value; + if (_debouncer != null) { + _debouncer.run(onComplete: () => _performSearch(value)); + } else { + await _performSearch(value); + } + } + + FutureOr _performSearch(String value) async { final chatList = await _config.onSearch?.call(value); if (chatList != null) { widget.chatViewListController?.setSearchChats(chatList); diff --git a/lib/src/widgets/chatui_textfield.dart b/lib/src/widgets/chatui_textfield.dart index 18d5589e..e951c5ab 100644 --- a/lib/src/widgets/chatui_textfield.dart +++ b/lib/src/widgets/chatui_textfield.dart @@ -444,9 +444,9 @@ class _ChatUITextFieldState extends State { } void _onChanged(String inputText) { - debouncer.run(() { + debouncer.run(onComplete: () { composingStatus.value = TypeWriterStatus.typed; - }, () { + }, onInterrupt: () { composingStatus.value = TypeWriterStatus.typing; }); _inputText.value = inputText;