diff --git a/core/lib/presentation/views/quick_search/quick_search_input_form.dart b/core/lib/presentation/views/quick_search/quick_search_input_form.dart index 4d0a00f8b..95c7af273 100644 --- a/core/lib/presentation/views/quick_search/quick_search_input_form.dart +++ b/core/lib/presentation/views/quick_search/quick_search_input_form.dart @@ -35,7 +35,7 @@ final supportedPlatform = (kIsWeb || Platform.isAndroid || Platform.isIOS); /// /// * [TypeAheadFieldQuickSearch], A [TextField](https://docs.flutter.io/flutter/material/TextField-class.html) /// that displays a list of suggestions as the user types -class QuickSearchInputForm extends FormField { +class QuickSearchInputForm extends FormField { /// The configuration of the [TextField](https://docs.flutter.io/flutter/material/TextField-class.html) /// that the TypeAhead widget displays final QuickSearchTextFieldConfiguration textFieldConfiguration; @@ -87,6 +87,9 @@ class QuickSearchInputForm extends FormField { BoxDecoration? decoration, double? maxHeight, bool isDirectionRTL = false, + ItemBuilder

? contactItemBuilder, + SuggestionsCallback

? contactSuggestionsCallback, + SuggestionSelectionCallback

? onContactSuggestionSelected, }) : assert( initialValue == null || textFieldConfiguration.controller == null), assert(minCharsForSuggestions >= 0), @@ -101,7 +104,7 @@ class QuickSearchInputForm extends FormField { autovalidateMode: autovalidateMode, builder: (FormFieldState field) { final _TypeAheadFormFieldState state = - field as _TypeAheadFormFieldState; + field as _TypeAheadFormFieldState; return TypeAheadFieldQuickSearch( getImmediateSuggestions: getImmediateSuggestions, @@ -151,21 +154,24 @@ class QuickSearchInputForm extends FormField { decoration: decoration, maxHeight: maxHeight, isDirectionRTL: isDirectionRTL, + contactItemBuilder: contactItemBuilder, + contactSuggestionsCallback: contactSuggestionsCallback, + onContactSuggestionSelected: onContactSuggestionSelected, ); }); @override - FormFieldState createState() => _TypeAheadFormFieldState(); + FormFieldState createState() => _TypeAheadFormFieldState(); } -class _TypeAheadFormFieldState extends FormFieldState { +class _TypeAheadFormFieldState extends FormFieldState { TextEditingController? _controller; TextEditingController? get _effectiveController => widget.textFieldConfiguration.controller ?? _controller; @override - QuickSearchInputForm get widget => super.widget as QuickSearchInputForm; + QuickSearchInputForm get widget => super.widget as QuickSearchInputForm; @override void initState() { @@ -239,7 +245,7 @@ class _TypeAheadFormFieldState extends FormFieldState { /// * [QuickSearchInputForm], a [FormField](https://docs.flutter.io/flutter/widgets/FormField-class.html) /// implementation of [TypeAheadFieldQuickSearch] that allows the value to be saved, /// validated, etc. -class TypeAheadFieldQuickSearch extends StatefulWidget { +class TypeAheadFieldQuickSearch extends StatefulWidget { /// Called with the search pattern to get the search suggestions. /// /// This callback must not be null. It is be called by the TypeAhead widget @@ -517,8 +523,14 @@ class TypeAheadFieldQuickSearch extends StatefulWidget { final BoxDecoration? decoration; /// Max height search input final double? maxHeight; - + /// Check direction text input final bool isDirectionRTL; + /// Widget contact item + final ItemBuilder

? contactItemBuilder; + /// Get all contact callback + final SuggestionsCallback

? contactSuggestionsCallback; + /// On listen select contact + final SuggestionSelectionCallback

? onContactSuggestionSelected; /// Creates a [TypeAheadFieldQuickSearch] const TypeAheadFieldQuickSearch( @@ -562,6 +574,9 @@ class TypeAheadFieldQuickSearch extends StatefulWidget { this.decoration, this.maxHeight, this.isDirectionRTL = false, + this.contactItemBuilder, + this.contactSuggestionsCallback, + this.onContactSuggestionSelected, }) : assert(animationStart >= 0.0 && animationStart <= 1.0), assert( direction == AxisDirection.down || direction == AxisDirection.up), @@ -569,10 +584,10 @@ class TypeAheadFieldQuickSearch extends StatefulWidget { super(key: key); @override - State> createState() => _TypeAheadFieldQuickSearchState(); + State> createState() => _TypeAheadFieldQuickSearchState(); } -class _TypeAheadFieldQuickSearchState extends State> +class _TypeAheadFieldQuickSearchState extends State> with WidgetsBindingObserver { FocusNode? _focusNode; TextEditingController? _textEditingController; @@ -707,7 +722,7 @@ class _TypeAheadFieldQuickSearchState extends State( + final suggestionsList = _SuggestionsList( suggestionsBox: _suggestionsBox, decoration: widget.suggestionsBoxDecoration, debounceDuration: widget.debounceDuration, @@ -754,6 +769,15 @@ class _TypeAheadFieldQuickSearchState extends State extends State extends StatefulWidget { +class _SuggestionsList extends StatefulWidget { final _SuggestionsBox? suggestionsBox; final TextEditingController? controller; final bool getImmediateSuggestions; @@ -915,6 +939,9 @@ class _SuggestionsList extends StatefulWidget { final EdgeInsets? listActionPadding; final bool hideSuggestionsBox; final bool isDirectionRTL; + final ItemBuilder

? contactSuggestionBuilder; + final SuggestionsCallback

? contactSuggestionCallback; + final SuggestionSelectionCallback

? onContactSuggestionSelected; const _SuggestionsList({ required this.suggestionsBox, @@ -949,16 +976,20 @@ class _SuggestionsList extends StatefulWidget { this.listActionPadding, this.hideSuggestionsBox = false, this.isDirectionRTL = false, + this.contactSuggestionBuilder, + this.contactSuggestionCallback, + this.onContactSuggestionSelected, }); @override - _SuggestionsListState createState() => _SuggestionsListState(); + _SuggestionsListState createState() => _SuggestionsListState(); } -class _SuggestionsListState extends State<_SuggestionsList> +class _SuggestionsListState extends State<_SuggestionsList> with SingleTickerProviderStateMixin { Iterable? _suggestions; Iterable? _recentItems; + Iterable

? _contacts; late bool _suggestionsValid; late VoidCallback _controllerListener; Timer? _debounceTimer; @@ -991,6 +1022,7 @@ class _SuggestionsListState extends State<_SuggestionsList> setState(() { _isLoading = false; _suggestions = null; + _contacts = null; _recentItems = recentItems; _suggestionsValid = true; }); @@ -1015,7 +1047,7 @@ class _SuggestionsListState extends State<_SuggestionsList> } @override - void didUpdateWidget(_SuggestionsList oldWidget) { + void didUpdateWidget(_SuggestionsList oldWidget) { super.didUpdateWidget(oldWidget); widget.controller!.addListener(_controllerListener); _getSuggestions(); @@ -1067,6 +1099,7 @@ class _SuggestionsListState extends State<_SuggestionsList> Iterable? suggestions; Iterable? recentItems; + Iterable

? contacts; Object? error; try { @@ -1074,6 +1107,9 @@ class _SuggestionsListState extends State<_SuggestionsList> if (widget.fetchRecentActionCallback != null) { recentItems = await widget.fetchRecentActionCallback!(widget.controller!.text); } + if (widget.contactSuggestionCallback != null) { + contacts = await widget.contactSuggestionCallback!(widget.controller!.text); + } } catch (e) { error = e; } @@ -1091,6 +1127,7 @@ class _SuggestionsListState extends State<_SuggestionsList> _isLoading = false; _suggestions = suggestions; _recentItems = recentItems; + _contacts = contacts; }); } } @@ -1111,7 +1148,7 @@ class _SuggestionsListState extends State<_SuggestionsList> Widget child; - if (_suggestions?.isNotEmpty == true && widget.controller?.text.isNotEmpty == true) { + if ((_suggestions?.isNotEmpty == true || _contacts?.isNotEmpty == true) && widget.controller?.text.isNotEmpty == true) { child = createSuggestionsWidget(); } else { child = createRecentWidget(); @@ -1165,12 +1202,10 @@ class _SuggestionsListState extends State<_SuggestionsList> Widget createSuggestionsWidget() { final listItemSuggestionWidget = _suggestions?.map((T suggestion) { - if ( widget.itemBuilder != null) { + if (widget.itemBuilder != null) { return InkWell( child: widget.itemBuilder!(context, suggestion), - onTap: () { - widget.onSuggestionSelected!(suggestion); - }, + onTap: () => widget.onSuggestionSelected?.call(suggestion), ); } else { return const SizedBox.shrink(); @@ -1212,6 +1247,20 @@ class _SuggestionsListState extends State<_SuggestionsList> } }).toList()); + final listItemContactWidget = _contacts?.map((P contact) { + if (widget.contactSuggestionBuilder != null) { + return Material( + color: Colors.transparent, + child: InkWell( + child: widget.contactSuggestionBuilder!(context, contact), + onTap: () => widget.onContactSuggestionSelected?.call(contact), + ), + ); + } else { + return const SizedBox.shrink(); + } + }).toList() ?? []; + Widget child = ListView( padding: EdgeInsets.zero, primary: false, @@ -1226,6 +1275,8 @@ class _SuggestionsListState extends State<_SuggestionsList> ), if (_isLoading == true && widget.hideOnLoading == false && widget.keepSuggestionsOnLoading == false) loadingWidget, + if (listItemContactWidget.isNotEmpty) + ... listItemContactWidget, if (widget.buttonShowAllResult != null && widget.controller?.text.isNotEmpty == true) widget.buttonShowAllResult!(context, widget.controller?.text), if (listItemSuggestionWidget.isNotEmpty)