From af340e97a6d8a4274ff87ecf7235e0b53b1ac3ed Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 12 Aug 2025 17:21:10 +0300 Subject: [PATCH 1/7] feat: add isEnabled toggle --- example/lib/main.dart | 8 ++++ .../controllers/language_tool_controller.dart | 47 +++++++++++++++---- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index da7d074..968adf1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -41,6 +41,14 @@ class _AppState extends State { controller: _controller, language: 'en-US', ), + ValueListenableBuilder( + valueListenable: _controller, + builder: (_, __, ___) => CheckboxListTile( + title: const Text("Enable spell checking"), + value: _controller.isEnabled, + onChanged: (value) => _controller.isEnabled = value ?? false, + ), + ), DropdownMenu( hintText: "Select alignment...", onSelected: (value) => setState(() { diff --git a/lib/src/core/controllers/language_tool_controller.dart b/lib/src/core/controllers/language_tool_controller.dart index 5f94bae..3214144 100644 --- a/lib/src/core/controllers/language_tool_controller.dart +++ b/lib/src/core/controllers/language_tool_controller.dart @@ -19,6 +19,8 @@ import 'package:languagetool_textfield/src/utils/mistake_popup.dart'; /// A TextEditingController with overrides buildTextSpan for building /// marked TextSpans with tap recognizer class LanguageToolController extends TextEditingController { + bool _isEnabled; + /// Color scheme to highlight mistakes final HighlightStyle highlightStyle; @@ -73,21 +75,38 @@ class LanguageToolController extends TextEditingController { _languageToolClient.language = language; } + /// Indicates whether spell checking is enabled + bool get isEnabled => _isEnabled; + + set isEnabled(bool value) { + _isEnabled = value; + + if (_isEnabled) { + _handleTextChange(text, force: true); + } else { + notifyListeners(); + } + } + /// An error that may have occurred during the API fetch. Object? get fetchError => _fetchError; @override set value(TextEditingValue newValue) { - _handleTextChange(newValue.text); + if (_isEnabled) { + _handleTextChange(newValue.text); + } + super.value = newValue; } /// Controller constructor LanguageToolController({ + bool isEnabled = true, this.highlightStyle = const HighlightStyle(), this.delay = Duration.zero, this.delayType = DelayType.debouncing, - }) { + }) : _isEnabled = isEnabled { _languageCheckService = _getLanguageCheckService(); } @@ -111,9 +130,17 @@ class LanguageToolController extends TextEditingController { @override TextSpan buildTextSpan({ required BuildContext context, - TextStyle? style, required bool withComposing, + TextStyle? style, }) { + if (!_isEnabled) { + return super.buildTextSpan( + context: context, + withComposing: withComposing, + style: style, + ); + } + final formattedTextSpans = _generateSpans( context, style: style, @@ -132,6 +159,10 @@ class LanguageToolController extends TextEditingController { /// Replaces mistake with given replacement void replaceMistake(Mistake mistake, String replacement) { + if (!_isEnabled) { + throw Exception('LanguageToolController is not enabled'); + } + final mistakes = List.from(_mistakes); mistakes.remove(mistake); _mistakes = mistakes; @@ -145,10 +176,10 @@ class LanguageToolController extends TextEditingController { /// Clear mistakes list when text mas modified and get a new list of mistakes /// via API - Future _handleTextChange(String newText) async { + Future _handleTextChange(String newText, {bool force = false}) async { ///set value triggers each time, even when cursor changes its location ///so this check avoid cleaning Mistake list when text wasn't really changed - if (newText == text || newText.isEmpty) return; + if (!force && (newText == text || newText.isEmpty)) return; final filteredMistakes = _filterMistakesOnChanged(newText); _mistakes = filteredMistakes.toList(); @@ -200,7 +231,7 @@ class LanguageToolController extends TextEditingController { final Color mistakeColor = _getMistakeColor(mistake.type); /// Create a gesture recognizer for mistake - final _onTap = TapGestureRecognizer() + final onTap = TapGestureRecognizer() ..onTapDown = (details) { popupWidget?.show( context, @@ -223,7 +254,7 @@ class LanguageToolController extends TextEditingController { }; /// Adding recognizer to the list for future disposing - _recognizers.add(_onTap); + _recognizers.add(onTap); /// Mistake highlighted TextSpan yield TextSpan( @@ -242,7 +273,7 @@ class LanguageToolController extends TextEditingController { decorationColor: mistakeColor, decorationThickness: highlightStyle.mistakeLineThickness, ), - recognizer: _onTap, + recognizer: onTap, ), ], ); From 840858aa14c880d57b75056cb318da637a687e95 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 12 Aug 2025 17:39:51 +0300 Subject: [PATCH 2/7] fix: dispose tap recognizers and clear mistakes on disabled --- lib/src/core/controllers/language_tool_controller.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/src/core/controllers/language_tool_controller.dart b/lib/src/core/controllers/language_tool_controller.dart index 3214144..62d8101 100644 --- a/lib/src/core/controllers/language_tool_controller.dart +++ b/lib/src/core/controllers/language_tool_controller.dart @@ -79,11 +79,19 @@ class LanguageToolController extends TextEditingController { bool get isEnabled => _isEnabled; set isEnabled(bool value) { + if (value == _isEnabled) return; + _isEnabled = value; if (_isEnabled) { _handleTextChange(text, force: true); } else { + _mistakes.clear(); + for (final recognizer in _recognizers) { + recognizer.dispose(); + } + _recognizers.clear(); + notifyListeners(); } } From 3e18bae479701fb1b343cac669a76b64fe7c8494 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 12 Aug 2025 17:40:19 +0300 Subject: [PATCH 3/7] fix: throw state error instead of exception --- lib/src/core/controllers/language_tool_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/core/controllers/language_tool_controller.dart b/lib/src/core/controllers/language_tool_controller.dart index 62d8101..50f7b45 100644 --- a/lib/src/core/controllers/language_tool_controller.dart +++ b/lib/src/core/controllers/language_tool_controller.dart @@ -168,7 +168,7 @@ class LanguageToolController extends TextEditingController { /// Replaces mistake with given replacement void replaceMistake(Mistake mistake, String replacement) { if (!_isEnabled) { - throw Exception('LanguageToolController is not enabled'); + throw StateError('LanguageToolController is not enabled'); } final mistakes = List.from(_mistakes); From 4099c15d85aa02143263ed5f4c3c6c9d509319c2 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 12 Aug 2025 18:16:10 +0300 Subject: [PATCH 4/7] fix: clear mistakes by assigning to new array reference --- lib/src/core/controllers/language_tool_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/core/controllers/language_tool_controller.dart b/lib/src/core/controllers/language_tool_controller.dart index 50f7b45..f698089 100644 --- a/lib/src/core/controllers/language_tool_controller.dart +++ b/lib/src/core/controllers/language_tool_controller.dart @@ -86,7 +86,7 @@ class LanguageToolController extends TextEditingController { if (_isEnabled) { _handleTextChange(text, force: true); } else { - _mistakes.clear(); + _mistakes = []; for (final recognizer in _recognizers) { recognizer.dispose(); } From 62c66b431a8c96f623d5b414c261b175e4b1fcd6 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 12 Aug 2025 18:19:53 +0300 Subject: [PATCH 5/7] refactor: rename parameter name for better readability style: avoid early returns for better readability --- .../controllers/language_tool_controller.dart | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/lib/src/core/controllers/language_tool_controller.dart b/lib/src/core/controllers/language_tool_controller.dart index f698089..eb39c0e 100644 --- a/lib/src/core/controllers/language_tool_controller.dart +++ b/lib/src/core/controllers/language_tool_controller.dart @@ -84,7 +84,7 @@ class LanguageToolController extends TextEditingController { _isEnabled = value; if (_isEnabled) { - _handleTextChange(text, force: true); + _handleTextChange(text, spellCheckSameText: true); } else { _mistakes = []; for (final recognizer in _recognizers) { @@ -184,33 +184,38 @@ class LanguageToolController extends TextEditingController { /// Clear mistakes list when text mas modified and get a new list of mistakes /// via API - Future _handleTextChange(String newText, {bool force = false}) async { + Future _handleTextChange( + String newText, { + bool spellCheckSameText = false, + }) async { ///set value triggers each time, even when cursor changes its location ///so this check avoid cleaning Mistake list when text wasn't really changed - if (!force && (newText == text || newText.isEmpty)) return; + if (spellCheckSameText || newText != text && newText.isNotEmpty) { + final filteredMistakes = _filterMistakesOnChanged(newText); + _mistakes = filteredMistakes.toList(); - final filteredMistakes = _filterMistakesOnChanged(newText); - _mistakes = filteredMistakes.toList(); + // If we have a text change and we have a popup on hold + // it will close the popup + _closePopup(); - // If we have a text change and we have a popup on hold - // it will close the popup - _closePopup(); - - for (final recognizer in _recognizers) { - recognizer.dispose(); - } - _recognizers.clear(); + for (final recognizer in _recognizers) { + recognizer.dispose(); + } + _recognizers.clear(); - final mistakesWrapper = await _latestResponseService.processLatestOperation( - () => _languageCheckService?.findMistakes(newText) ?? Future(() => null), - ); - if (mistakesWrapper == null || !mistakesWrapper.hasResult) return; + final mistakesWrapper = + await _latestResponseService.processLatestOperation( + () => + _languageCheckService?.findMistakes(newText) ?? Future(() => null), + ); + if (mistakesWrapper == null || !mistakesWrapper.hasResult) return; - final mistakes = mistakesWrapper.result(); - _fetchError = mistakesWrapper.error; + final mistakes = mistakesWrapper.result(); + _fetchError = mistakesWrapper.error; - _mistakes = mistakes; - notifyListeners(); + _mistakes = mistakes; + notifyListeners(); + } } /// Generator function to create TextSpan instances From 3989ef2cd7254f35516928147e68a1c20eda1779 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Fri, 15 Aug 2025 22:26:55 +0300 Subject: [PATCH 6/7] docs: add changelog chore: bump version --- CHANGELOG.md | 4 ++++ pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35cd6f0..2ffe3fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.0 + +- Add `isEnabled` to toggle spell check + ## 1.0.0 - BREAKING: require flutter 3.27.0 or higher diff --git a/pubspec.yaml b/pubspec.yaml index d475570..d1488af 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: languagetool_textfield description: The LanguageTool TextField package is a spell-checker designed for Flutter apps. This is useful for apps that need text input like messaging, notes, and email. -version: 1.0.0 +version: 1.1.0 homepage: https://github.com/solid-software/languagetool_textfield/ environment: From 5add9545d2c8aa56643e552a9614553f4297b1e8 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Mon, 18 Aug 2025 18:35:03 +0300 Subject: [PATCH 7/7] docs: squash chnages with v1.0.0 --- CHANGELOG.md | 5 +---- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ffe3fb..8d43d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,9 @@ -## 1.1.0 - -- Add `isEnabled` to toggle spell check - ## 1.0.0 - BREAKING: require flutter 3.27.0 or higher - BREAKING: require dart 3.0.0 or higher - BREAKING: rename autoFocus to autofocus to match `TextField`'s naming +- Add `isEnabled` to toggle spell check - Add missing properties from flutter's `TextField` - autofillHints - autofocus diff --git a/pubspec.yaml b/pubspec.yaml index d1488af..d475570 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: languagetool_textfield description: The LanguageTool TextField package is a spell-checker designed for Flutter apps. This is useful for apps that need text input like messaging, notes, and email. -version: 1.1.0 +version: 1.0.0 homepage: https://github.com/solid-software/languagetool_textfield/ environment: