diff --git a/apps/weblibre/lib/features/geckoview/features/search/presentation/widgets/search_field.dart b/apps/weblibre/lib/features/geckoview/features/search/presentation/widgets/search_field.dart index da2c9fd9..58ccbdb5 100644 --- a/apps/weblibre/lib/features/geckoview/features/search/presentation/widgets/search_field.dart +++ b/apps/weblibre/lib/features/geckoview/features/search/presentation/widgets/search_field.dart @@ -129,6 +129,9 @@ class SearchField extends HookConsumerWidget { : null, minLines: minLines, autofocus: autofocus, + onSuggestionDismiss: () { + suggestion.value = null; + }, decoration: InputDecoration( border: InputBorder.none, contentPadding: const EdgeInsetsDirectional.fromSTEB(12, 12, 0, 12), diff --git a/apps/weblibre/lib/presentation/widgets/auto_suggest_text_field.dart b/apps/weblibre/lib/presentation/widgets/auto_suggest_text_field.dart index 5773fb8e..21fbcabf 100644 --- a/apps/weblibre/lib/presentation/widgets/auto_suggest_text_field.dart +++ b/apps/weblibre/lib/presentation/widgets/auto_suggest_text_field.dart @@ -50,6 +50,7 @@ class AutoSuggestTextField extends HookWidget { final bool? enableIMEPersonalizedLearning; final TapRegionCallback? onTapOutside; final VoidCallback? onTap; + final VoidCallback? onSuggestionDismiss; final bool autocorrect; final GlobalKey? textFieldKey; @@ -80,6 +81,7 @@ class AutoSuggestTextField extends HookWidget { this.enableIMEPersonalizedLearning = true, this.onTapOutside, this.onTap, + this.onSuggestionDismiss, this.autocorrect = false, this.textFieldKey, }); @@ -96,6 +98,20 @@ class AutoSuggestTextField extends HookWidget { final effectiveStyle = style ?? Theme.of(context).textTheme.bodyLarge!; + final hasSuggestionNotifier = useValueNotifier(false); + + useEffect(() { + hasSuggestionNotifier.value = _suggestionHasMatch(); + return null; + }, [suggestion, controller.text]); + + final dismissFormatter = useMemoized( + () => _SuggestionDismissFormatter( + hasSuggestionNotifier: hasSuggestionNotifier, + onSuggestionDismiss: onSuggestionDismiss, + ), + ); + final showSuggestion = useListenableSelector(controller, () { if (maxLines != 1) { final lines = getTextFieldLineCount( @@ -140,30 +156,6 @@ class AutoSuggestTextField extends HookWidget { () => controller.text, ); - useEffect(() { - TextSelection? lastSelection; - - void handleSelectionChange() { - if (lastSelection != controller.selection) { - lastSelection = controller.selection; - if (lastSelection!.start != lastSelection!.end) { - if (_suggestionHasMatch()) { - controller.value = controller.value.copyWith( - text: suggestion, - selection: lastSelection!.expandTo( - TextPosition(offset: suggestion!.length), - ), - ); - } - } - } - } - - controller.addListener(handleSelectionChange); - return () => - controller.removeListener(handleSelectionChange); - }); - if (!_suggestionHasMatch()) { return const SizedBox.shrink(); } @@ -230,7 +222,7 @@ class AutoSuggestTextField extends HookWidget { } }, ), - inputFormatters: inputFormatters, + inputFormatters: [dismissFormatter, ...?inputFormatters], enabled: enabled, cursorColor: cursorColor, enableIMEPersonalizedLearning: enableIMEPersonalizedLearning ?? true, @@ -241,3 +233,28 @@ class AutoSuggestTextField extends HookWidget { ); } } + +class _SuggestionDismissFormatter extends TextInputFormatter { + final ValueNotifier hasSuggestionNotifier; + final VoidCallback? onSuggestionDismiss; + + _SuggestionDismissFormatter({ + required this.hasSuggestionNotifier, + this.onSuggestionDismiss, + }); + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) { + if (hasSuggestionNotifier.value && + newValue.text.length < oldValue.text.length && + oldValue.selection.isCollapsed && + oldValue.selection.baseOffset == oldValue.text.length) { + onSuggestionDismiss?.call(); + return oldValue; + } + return newValue; + } +}