From 7633590a8b27fb1f6e987b17e811264568a02602 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:58:49 +0300 Subject: [PATCH 01/14] Created text editing controller which builds TextSpans from list of Mistake --- .../colored_text_editing_controller.dart | 107 ++++++++++++++++++ lib/domain/mistake.dart | 6 + .../debounce_lang_tool_service.dart | 10 +- lib/languagetool_textfield.dart | 9 ++ .../language_tool_text_field.dart | 33 +++++- 5 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 lib/core/controllers/colored_text_editing_controller.dart diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart new file mode 100644 index 0000000..9aec67e --- /dev/null +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -0,0 +1,107 @@ +import 'dart:developer'; + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:languagetool_textfield/domain/mistake.dart'; + +/// A TextEditingController with overrides buildTextSpan for building +/// marked TextSpans with tap recognizer +class ColoredTextEditingController extends TextEditingController { + /// List which contains Mistake objects spans are built from + List _mistakes = []; + + final double _backGroundOpacity = + 0.2; // background opacity for mistake TextSpan + + final double _mistakeLineThickness = + 1.5; // mistake TextSpan underline thickness + + /// A method sets new list of Mistake and triggers buildTextSpan + void setMistakes(List list) { + _mistakes = list; + notifyListeners(); + } + + /// builds TextSpan from Mistake list + @override + TextSpan buildTextSpan({ + required BuildContext context, + TextStyle? style, + required bool withComposing, + }) { + int currentOffset = 0; // enter index + final List spans = []; // List of TextSpan + final int textLength = text.length; // Length of text to be built + + /// Iterates _mistakes and adds TextSpans from Mistake offset and length + for (final Mistake mistake in _mistakes) { + /// Breaks the loop if iterated Mistake offset is bigger than text length. + if (mistake.offset > textLength || + mistake.offset + mistake.length > textLength) { + break; + } + + /// TextSpan before mistake + spans.add( + TextSpan( + text: text.substring( + currentOffset, + mistake.offset, + ), + style: style, + ), + ); + + /// Setting color of the mistake by its type + final Color mistakeColor = _getMistakeColor(mistake.type); + + /// The mistake TextSpan + spans.add( + TextSpan( + text: text.substring(mistake.offset, mistake.offset + mistake.length), + mouseCursor: MaterialStateMouseCursor.clickable, + recognizer: TapGestureRecognizer() + ..onTapDown = _callOverlay, // calls overlay with mistakes details + style: style?.copyWith( + backgroundColor: mistakeColor.withOpacity(_backGroundOpacity), + decoration: TextDecoration.underline, + decorationColor: mistakeColor, + decorationThickness: _mistakeLineThickness, + ), + ), + ); + + /// Changing enter index position for the next iteration + currentOffset = mistake.offset + mistake.length; + } + + /// TextSpan after mistake + spans.add( + TextSpan( + text: text.substring(currentOffset), + style: style, + ), + ); + + /// Returns TextSpan + return TextSpan(children: spans); + } + + void _callOverlay(TapDownDetails details) { + log(details.globalPosition.toString()); + } + + /// Returns color for mistake TextSpan style + Color _getMistakeColor(String type) { + switch (type) { + case 'misspelling': + return Colors.red; + case 'style': + return Colors.blue; + case 'uncategorized': + return Colors.amber; + default: + return Colors.green; + } + } +} diff --git a/lib/domain/mistake.dart b/lib/domain/mistake.dart index ecbd3a8..9433887 100644 --- a/lib/domain/mistake.dart +++ b/lib/domain/mistake.dart @@ -25,4 +25,10 @@ class Mistake { required this.length, this.replacements = const [], }); + + @override + String toString() { + return 'Mistake{message: $message, type: $type, offset: $offset, ' + 'length: $length, replacements: $replacements}'; + } } diff --git a/lib/implementations/debounce_lang_tool_service.dart b/lib/implementations/debounce_lang_tool_service.dart index ed14755..7aabc2d 100644 --- a/lib/implementations/debounce_lang_tool_service.dart +++ b/lib/implementations/debounce_lang_tool_service.dart @@ -17,7 +17,11 @@ class DebounceLangToolService extends LanguageCheckService { ) : debouncing = Debouncing(duration: debouncingDuration); @override - Future> findMistakes(String text) => - debouncing.debounce(() => baseService.findMistakes(text)) - as Future>; + Future> findMistakes(String text) async { + final value = await debouncing.debounce(() { + return baseService.findMistakes(text); + }) as List?; + + return value ?? []; + } } diff --git a/lib/languagetool_textfield.dart b/lib/languagetool_textfield.dart index abe7c4c..d3178aa 100644 --- a/lib/languagetool_textfield.dart +++ b/lib/languagetool_textfield.dart @@ -1 +1,10 @@ library languagetool_textfield; + +export 'package:language_tool/language_tool.dart'; + +export 'domain/language_check_service.dart'; +export 'domain/mistake.dart'; +export 'implementations/debounce_lang_tool_service.dart'; +export 'implementations/lang_tool_service.dart'; +export 'implementations/throttling_lang_tool_service.dart'; +export "presentation/language_tool_text_field.dart"; diff --git a/lib/presentation/language_tool_text_field.dart b/lib/presentation/language_tool_text_field.dart index 0652baf..23e75df 100644 --- a/lib/presentation/language_tool_text_field.dart +++ b/lib/presentation/language_tool_text_field.dart @@ -1,4 +1,6 @@ + import 'package:flutter/material.dart'; +import 'package:languagetool_textfield/core/controllers/colored_text_editing_controller.dart'; import 'package:languagetool_textfield/domain/language_check_service.dart'; /// A TextField widget that checks the grammar using the given [langService] @@ -29,8 +31,37 @@ class LanguageToolTextField extends StatefulWidget { } class _LanguageToolTextFieldState extends State { + final ColoredTextEditingController _controller = + ColoredTextEditingController(); + final int _maxLines = 8; // max lines of the TextFiled + + /// Sends API request to get a list of Mistake + Future _check(String text) async { + final list = await widget.langService.findMistakes(text); + if (list.isNotEmpty) { + _controller.setMistakes(list); + } + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); // disposes controller + } + @override Widget build(BuildContext context) { - return const Placeholder(); + return Padding( + padding: const EdgeInsets.all(24.0), + child: Center( + child: TextField( + controller: _controller, + maxLines: _maxLines, + onChanged: _check, + style: widget.style, + decoration: widget.decoration, + ), + ), + ); } } From 9fa3c1dc229eecb82d8556393f82957ffef9dd05 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 15:15:12 +0300 Subject: [PATCH 02/14] dart format fix --- lib/core/controllers/colored_text_editing_controller.dart | 4 ++-- lib/presentation/language_tool_text_field.dart | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index 9aec67e..ce2f62a 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -11,10 +11,10 @@ class ColoredTextEditingController extends TextEditingController { List _mistakes = []; final double _backGroundOpacity = - 0.2; // background opacity for mistake TextSpan + 0.2; // background opacity for mistake TextSpan final double _mistakeLineThickness = - 1.5; // mistake TextSpan underline thickness + 1.5; // mistake TextSpan underline thickness /// A method sets new list of Mistake and triggers buildTextSpan void setMistakes(List list) { diff --git a/lib/presentation/language_tool_text_field.dart b/lib/presentation/language_tool_text_field.dart index 23e75df..26c9df1 100644 --- a/lib/presentation/language_tool_text_field.dart +++ b/lib/presentation/language_tool_text_field.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:languagetool_textfield/core/controllers/colored_text_editing_controller.dart'; import 'package:languagetool_textfield/domain/language_check_service.dart'; From 182fc3dd555098960441efc977adf1bd3a01337f Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 15:34:38 +0300 Subject: [PATCH 03/14] Example app added LanguageToolTextField widget --- example/lib/app.dart | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/example/lib/app.dart b/example/lib/app.dart index f3d9ca4..d01fb45 100644 --- a/example/lib/app.dart +++ b/example/lib/app.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:languagetool_textfield/languagetool_textfield.dart'; class App extends StatefulWidget { const App({super.key}); @@ -8,8 +9,25 @@ class App extends StatefulWidget { } class _AppState extends State { + static final LanguageTool _languageTool = LanguageTool(); + final DebounceLangToolService _debouncedLangService; + + _AppState() + : _debouncedLangService = DebounceLangToolService( + LangToolService(_languageTool), + const Duration(milliseconds: 500), + ); @override Widget build(BuildContext context) { - return const Placeholder(); + return Material( + child: LanguageToolTextField( + langService: _debouncedLangService, + style: const TextStyle(), + decoration: const InputDecoration(), + mistakeBuilder: () { + return Container(); + }, + ), + ); } } From c1e1a858a38f1e4505375dfa934bb405fe314575 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 15:35:44 +0300 Subject: [PATCH 04/14] format fix --- example/lib/app.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/lib/app.dart b/example/lib/app.dart index d01fb45..ef31c77 100644 --- a/example/lib/app.dart +++ b/example/lib/app.dart @@ -14,9 +14,9 @@ class _AppState extends State { _AppState() : _debouncedLangService = DebounceLangToolService( - LangToolService(_languageTool), - const Duration(milliseconds: 500), - ); + LangToolService(_languageTool), + const Duration(milliseconds: 500), + ); @override Widget build(BuildContext context) { return Material( From 4a18686d105ce29978880b6763249c59637bf669 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 15:38:58 +0300 Subject: [PATCH 05/14] added comments to Example App --- example/lib/app.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/example/lib/app.dart b/example/lib/app.dart index ef31c77..e22db0a 100644 --- a/example/lib/app.dart +++ b/example/lib/app.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:languagetool_textfield/languagetool_textfield.dart'; +/// Example App main page class App extends StatefulWidget { + /// Example app constructor const App({super.key}); @override @@ -9,9 +11,13 @@ class App extends StatefulWidget { } class _AppState extends State { + /// Initialize LanguageTool static final LanguageTool _languageTool = LanguageTool(); + + /// Initialize DebounceLangToolService final DebounceLangToolService _debouncedLangService; + /// Set DebounceLangToolService _AppState() : _debouncedLangService = DebounceLangToolService( LangToolService(_languageTool), From 8af61948ef84bec35ff44896c61175d062052607 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 19:11:00 +0300 Subject: [PATCH 06/14] All proposed changes have been made. --- .../colored_text_editing_controller.dart | 157 ++++++++++++------ lib/core/enums/mistake_type.dart | 23 +++ lib/domain/highlight_colors.dart | 36 ++++ lib/domain/mistake.dart | 10 +- lib/implementations/lang_tool_service.dart | 24 ++- lib/languagetool_textfield.dart | 1 + .../language_tool_text_field.dart | 36 ++-- 7 files changed, 213 insertions(+), 74 deletions(-) create mode 100644 lib/core/enums/mistake_type.dart create mode 100644 lib/domain/highlight_colors.dart diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index ce2f62a..4a7bff7 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -1,28 +1,33 @@ -import 'dart:developer'; - -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:languagetool_textfield/core/enums/mistake_type.dart'; +import 'package:languagetool_textfield/domain/highlight_colors.dart'; import 'package:languagetool_textfield/domain/mistake.dart'; /// A TextEditingController with overrides buildTextSpan for building /// marked TextSpans with tap recognizer class ColoredTextEditingController extends TextEditingController { + /// Color scheme to highlight mistakes + final HighlightColors? highlightColorScheme; + /// List which contains Mistake objects spans are built from List _mistakes = []; - final double _backGroundOpacity = + final double _backgroundOpacity = 0.2; // background opacity for mistake TextSpan final double _mistakeLineThickness = 1.5; // mistake TextSpan underline thickness - /// A method sets new list of Mistake and triggers buildTextSpan - void setMistakes(List list) { - _mistakes = list; - notifyListeners(); + @override + set value(TextEditingValue newValue) { + _handleTextChange(newValue.text); + super.value = newValue; } - /// builds TextSpan from Mistake list + /// Controller constructor + ColoredTextEditingController({this.highlightColorScheme}); + + /// Generates TextSpan from Mistake list @override TextSpan buildTextSpan({ required BuildContext context, @@ -30,78 +35,120 @@ class ColoredTextEditingController extends TextEditingController { required bool withComposing, }) { int currentOffset = 0; // enter index - final List spans = []; // List of TextSpan - final int textLength = text.length; // Length of text to be built - - /// Iterates _mistakes and adds TextSpans from Mistake offset and length - for (final Mistake mistake in _mistakes) { - /// Breaks the loop if iterated Mistake offset is bigger than text length. - if (mistake.offset > textLength || - mistake.offset + mistake.length > textLength) { - break; - } - /// TextSpan before mistake - spans.add( - TextSpan( + final int textLength = text.length; + + /// Generator function to create TextSpan instances + Iterable generateSpans() sync* { + for (final Mistake mistake in _mistakes) { + /// Breaks the loop if iterated Mistake offset is bigger than text + /// length. + if (mistake.offset > textLength || + mistake.offset + mistake.length > textLength) { + break; + } + + /// TextSpan before mistake + yield TextSpan( text: text.substring( currentOffset, mistake.offset, ), style: style, - ), - ); + ); - /// Setting color of the mistake by its type - final Color mistakeColor = _getMistakeColor(mistake.type); + /// Get a highlight color + final Color mistakeColor = _getMistakeColor(mistake.type); - /// The mistake TextSpan - spans.add( - TextSpan( + /// Mistake highlighted TextSpan + yield TextSpan( text: text.substring(mistake.offset, mistake.offset + mistake.length), mouseCursor: MaterialStateMouseCursor.clickable, - recognizer: TapGestureRecognizer() - ..onTapDown = _callOverlay, // calls overlay with mistakes details style: style?.copyWith( - backgroundColor: mistakeColor.withOpacity(_backGroundOpacity), + backgroundColor: mistakeColor.withOpacity(_backgroundOpacity), decoration: TextDecoration.underline, decorationColor: mistakeColor, decorationThickness: _mistakeLineThickness, ), - ), - ); + ); - /// Changing enter index position for the next iteration - currentOffset = mistake.offset + mistake.length; - } + currentOffset = mistake.offset + mistake.length; + } - /// TextSpan after mistake - spans.add( - TextSpan( + /// TextSpan after mistake + yield TextSpan( text: text.substring(currentOffset), style: style, - ), - ); + ); + } - /// Returns TextSpan - return TextSpan(children: spans); + return TextSpan(children: generateSpans().toList()); } - void _callOverlay(TapDownDetails details) { - log(details.globalPosition.toString()); + /// Apply changes to Mistake list while new data being fetched + void _handleTextChange(String newText) { + final int deltaLength = newText.length - text.length; + + /// Update the _mistakes list in-place based on the text modifications + _mistakes = _mistakes + .map((mistake) { + int newOffset = mistake.offset; + int newLength = mistake.length; + + /// If the text modification starts within the mistake + if (selection.start >= mistake.offset && + selection.start <= mistake.offset + mistake.length) { + newLength += deltaLength; + } + + /// If the text modification starts before the mistake + else if (selection.start < mistake.offset) { + newOffset += deltaLength; + } + + /// Return the updated mistake (if the length is greater than 0) + return newLength > 0 + ? Mistake( + message: mistake.message, + type: mistake.type, + offset: newOffset, + length: newLength, + replacements: mistake.replacements, + ) + : null; + }) + .whereType() + .toList(); + + /// Notify listeners to rebuild the widget + notifyListeners(); + } + + /// A method sets new list of Mistake and triggers buildTextSpan + void highlightMistakes(List list) { + _mistakes = list; + notifyListeners(); } /// Returns color for mistake TextSpan style - Color _getMistakeColor(String type) { + Color _getMistakeColor(MistakeType type) { switch (type) { - case 'misspelling': - return Colors.red; - case 'style': - return Colors.blue; - case 'uncategorized': - return Colors.amber; - default: - return Colors.green; + case MistakeType.misspelling: + return highlightColorScheme?.misspellingMistakeColor ?? Colors.red; + case MistakeType.typographical: + return highlightColorScheme?.typographicalMistakeColor ?? Colors.green; + case MistakeType.grammar: + return highlightColorScheme?.grammarMistakeColor ?? Colors.amber; + case MistakeType.uncategorized: + return highlightColorScheme?.uncategorizedMistakeColor ?? Colors.blue; + case MistakeType.nonConformance: + return highlightColorScheme?.nonConformanceMistakeColor ?? + Colors.greenAccent; + case MistakeType.style: + return highlightColorScheme?.styleMistakeColor ?? + Colors.deepPurpleAccent; + case MistakeType.other: + return highlightColorScheme?.otherMistakeColor ?? Colors.white60; } } } diff --git a/lib/core/enums/mistake_type.dart b/lib/core/enums/mistake_type.dart new file mode 100644 index 0000000..513304c --- /dev/null +++ b/lib/core/enums/mistake_type.dart @@ -0,0 +1,23 @@ +///Enumerate several mistake types +enum MistakeType { + /// Misspelling mistake type + misspelling, + + /// Typographical mistake type + typographical, + + /// Grammar mistake type + grammar, + + /// Uncategorized mistake type + uncategorized, + + /// NonConformance mistake type + nonConformance, + + /// Style mistake type + style, + + /// Any other mistake type + other, +} diff --git a/lib/domain/highlight_colors.dart b/lib/domain/highlight_colors.dart new file mode 100644 index 0000000..29a3080 --- /dev/null +++ b/lib/domain/highlight_colors.dart @@ -0,0 +1,36 @@ +import 'dart:ui'; + +/// Class creates color scheme for highlighting mistakes +class HighlightColors { + /// Misspelling mistake highlight color + final Color? misspellingMistakeColor; + + /// Misspelling mistake highlight color + final Color? typographicalMistakeColor; + + /// Typographical mistake highlight color + final Color? grammarMistakeColor; + + /// Uncategorized mistake highlight color + final Color? uncategorizedMistakeColor; + + /// NonConformance mistake highlight color + final Color? nonConformanceMistakeColor; + + /// Style mistake highlight color + final Color? styleMistakeColor; + + /// Other mistake highlight color + final Color? otherMistakeColor; + + ///Color scheme constructor + HighlightColors( + this.misspellingMistakeColor, + this.typographicalMistakeColor, + this.grammarMistakeColor, + this.uncategorizedMistakeColor, + this.nonConformanceMistakeColor, + this.styleMistakeColor, + this.otherMistakeColor, + ); +} diff --git a/lib/domain/mistake.dart b/lib/domain/mistake.dart index 9433887..7c556b0 100644 --- a/lib/domain/mistake.dart +++ b/lib/domain/mistake.dart @@ -1,10 +1,12 @@ +import 'package:languagetool_textfield/core/enums/mistake_type.dart'; + /// A data model class that stores information about a single writing mistake. class Mistake { /// A brief description of the mistake. final String message; /// A type of this mistake. - final String type; + final MistakeType type; /// A position of the beginning of this mistake. final int offset; @@ -25,10 +27,4 @@ class Mistake { required this.length, this.replacements = const [], }); - - @override - String toString() { - return 'Mistake{message: $message, type: $type, offset: $offset, ' - 'length: $length, replacements: $replacements}'; - } } diff --git a/lib/implementations/lang_tool_service.dart b/lib/implementations/lang_tool_service.dart index 923b21c..60ea278 100644 --- a/lib/implementations/lang_tool_service.dart +++ b/lib/implementations/lang_tool_service.dart @@ -1,4 +1,5 @@ import 'package:language_tool/language_tool.dart'; +import 'package:languagetool_textfield/core/enums/mistake_type.dart'; import 'package:languagetool_textfield/domain/language_check_service.dart'; import 'package:languagetool_textfield/domain/mistake.dart'; @@ -16,7 +17,9 @@ class LangToolService extends LanguageCheckService { final mistakes = writingMistakes.map( (m) => Mistake( message: m.message, - type: m.issueType, + type: _stringToMistakeType( + m.issueType, + ), offset: m.offset, length: m.length, replacements: m.replacements, @@ -25,4 +28,23 @@ class LangToolService extends LanguageCheckService { return mistakes.toList(); } + + MistakeType _stringToMistakeType(String issueType) { + switch (issueType.toLowerCase()) { + case 'misspelling': + return MistakeType.misspelling; + case 'typographical': + return MistakeType.typographical; + case 'grammar': + return MistakeType.grammar; + case 'uncategorized': + return MistakeType.uncategorized; + case 'non-conformance': + return MistakeType.nonConformance; + case 'style': + return MistakeType.style; + default: + return MistakeType.other; + } + } } diff --git a/lib/languagetool_textfield.dart b/lib/languagetool_textfield.dart index d3178aa..b86e916 100644 --- a/lib/languagetool_textfield.dart +++ b/lib/languagetool_textfield.dart @@ -2,6 +2,7 @@ library languagetool_textfield; export 'package:language_tool/language_tool.dart'; +export 'domain/highlight_colors.dart'; export 'domain/language_check_service.dart'; export 'domain/mistake.dart'; export 'implementations/debounce_lang_tool_service.dart'; diff --git a/lib/presentation/language_tool_text_field.dart b/lib/presentation/language_tool_text_field.dart index 26c9df1..4f25664 100644 --- a/lib/presentation/language_tool_text_field.dart +++ b/lib/presentation/language_tool_text_field.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:languagetool_textfield/core/controllers/colored_text_editing_controller.dart'; +import 'package:languagetool_textfield/domain/highlight_colors.dart'; import 'package:languagetool_textfield/domain/language_check_service.dart'; /// A TextField widget that checks the grammar using the given [langService] @@ -16,6 +17,9 @@ class LanguageToolTextField extends StatefulWidget { /// A builder function used to build errors. final Widget Function()? mistakeBuilder; + /// Color scheme to highlight mistakes + final HighlightColors? highlightColorScheme; + /// Creates a widget that checks grammar errors. const LanguageToolTextField({ Key? key, @@ -23,6 +27,7 @@ class LanguageToolTextField extends StatefulWidget { required this.style, required this.decoration, this.mistakeBuilder, + this.highlightColorScheme, }) : super(key: key); @override @@ -30,22 +35,22 @@ class LanguageToolTextField extends StatefulWidget { } class _LanguageToolTextFieldState extends State { - final ColoredTextEditingController _controller = - ColoredTextEditingController(); - final int _maxLines = 8; // max lines of the TextFiled + ColoredTextEditingController? _controller; /// Sends API request to get a list of Mistake Future _check(String text) async { - final list = await widget.langService.findMistakes(text); - if (list.isNotEmpty) { - _controller.setMistakes(list); + final mistakes = await widget.langService.findMistakes(text); + if (mistakes.isNotEmpty) { + _controller?.highlightMistakes(mistakes); } } @override - void dispose() { - super.dispose(); - _controller.dispose(); // disposes controller + void initState() { + _controller = ColoredTextEditingController( + highlightColorScheme: widget.highlightColorScheme, + ); + super.initState(); } @override @@ -55,12 +60,21 @@ class _LanguageToolTextFieldState extends State { child: Center( child: TextField( controller: _controller, - maxLines: _maxLines, - onChanged: _check, + onChanged: (String text) { + if (_controller != null) { + _check(text); + } + }, style: widget.style, decoration: widget.decoration, ), ), ); } + + @override + void dispose() { + super.dispose(); + _controller?.dispose(); // disposes controller + } } From 8fa0fb70557c31ad6fa168ca5d68cb62ae50e4ea Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 20:29:05 +0300 Subject: [PATCH 07/14] Customization approach fix --- example/lib/app.dart | 6 + .../colored_text_editing_controller.dart | 117 +++++++++--------- lib/domain/highlight_colors.dart | 36 ------ lib/domain/highlight_style.dart | 48 +++++++ lib/languagetool_textfield.dart | 3 +- .../language_tool_text_field.dart | 22 ++-- 6 files changed, 123 insertions(+), 109 deletions(-) delete mode 100644 lib/domain/highlight_colors.dart create mode 100644 lib/domain/highlight_style.dart diff --git a/example/lib/app.dart b/example/lib/app.dart index e22db0a..2f518fe 100644 --- a/example/lib/app.dart +++ b/example/lib/app.dart @@ -17,12 +17,17 @@ class _AppState extends State { /// Initialize DebounceLangToolService final DebounceLangToolService _debouncedLangService; + /// Initialize ColoredTextEditingController + final ColoredTextEditingController controller = + ColoredTextEditingController(); + /// Set DebounceLangToolService _AppState() : _debouncedLangService = DebounceLangToolService( LangToolService(_languageTool), const Duration(milliseconds: 500), ); + @override Widget build(BuildContext context) { return Material( @@ -33,6 +38,7 @@ class _AppState extends State { mistakeBuilder: () { return Container(); }, + coloredController: controller, ), ); } diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index 4a7bff7..5d7cd35 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -1,23 +1,17 @@ import 'package:flutter/material.dart'; import 'package:languagetool_textfield/core/enums/mistake_type.dart'; -import 'package:languagetool_textfield/domain/highlight_colors.dart'; +import 'package:languagetool_textfield/domain/highlight_style.dart'; import 'package:languagetool_textfield/domain/mistake.dart'; /// A TextEditingController with overrides buildTextSpan for building /// marked TextSpans with tap recognizer class ColoredTextEditingController extends TextEditingController { /// Color scheme to highlight mistakes - final HighlightColors? highlightColorScheme; + final HighlightStyle highlightStyle; /// List which contains Mistake objects spans are built from List _mistakes = []; - final double _backgroundOpacity = - 0.2; // background opacity for mistake TextSpan - - final double _mistakeLineThickness = - 1.5; // mistake TextSpan underline thickness - @override set value(TextEditingValue newValue) { _handleTextChange(newValue.text); @@ -25,7 +19,9 @@ class ColoredTextEditingController extends TextEditingController { } /// Controller constructor - ColoredTextEditingController({this.highlightColorScheme}); + ColoredTextEditingController({ + this.highlightStyle = const HighlightStyle(), + }); /// Generates TextSpan from Mistake list @override @@ -34,55 +30,64 @@ class ColoredTextEditingController extends TextEditingController { TextStyle? style, required bool withComposing, }) { - int currentOffset = 0; // enter index - final int textLength = text.length; + final Iterable spanList = + _generateSpans(textLength: textLength, style: style); - /// Generator function to create TextSpan instances - Iterable generateSpans() sync* { - for (final Mistake mistake in _mistakes) { - /// Breaks the loop if iterated Mistake offset is bigger than text - /// length. - if (mistake.offset > textLength || - mistake.offset + mistake.length > textLength) { - break; - } - - /// TextSpan before mistake - yield TextSpan( - text: text.substring( - currentOffset, - mistake.offset, - ), - style: style, - ); - - /// Get a highlight color - final Color mistakeColor = _getMistakeColor(mistake.type); - - /// Mistake highlighted TextSpan - yield TextSpan( - text: text.substring(mistake.offset, mistake.offset + mistake.length), - mouseCursor: MaterialStateMouseCursor.clickable, - style: style?.copyWith( - backgroundColor: mistakeColor.withOpacity(_backgroundOpacity), - decoration: TextDecoration.underline, - decorationColor: mistakeColor, - decorationThickness: _mistakeLineThickness, - ), - ); + return TextSpan( + children: spanList.toList(), + ); + } + + /// Generator function to create TextSpan instances + Iterable _generateSpans({ + required int textLength, + TextStyle? style, + }) sync* { + int currentOffset = 0; // enter index - currentOffset = mistake.offset + mistake.length; + for (final Mistake mistake in _mistakes) { + /// Breaks the loop if iterated Mistake offset is bigger than text + /// length. + if (mistake.offset > textLength || + mistake.offset + mistake.length > textLength) { + break; } - /// TextSpan after mistake + /// TextSpan before mistake yield TextSpan( - text: text.substring(currentOffset), + text: text.substring( + currentOffset, + mistake.offset, + ), style: style, ); + + /// Get a highlight color + final Color mistakeColor = _getMistakeColor(mistake.type); + + /// Mistake highlighted TextSpan + yield TextSpan( + text: text.substring(mistake.offset, mistake.offset + mistake.length), + mouseCursor: MaterialStateMouseCursor.clickable, + style: style?.copyWith( + backgroundColor: mistakeColor.withOpacity( + highlightStyle.backgroundOpacity, + ), + decoration: TextDecoration.underline, + decorationColor: mistakeColor, + decorationThickness: highlightStyle.mistakeLineThickness, + ), + ); + + currentOffset = mistake.offset + mistake.length; } - return TextSpan(children: generateSpans().toList()); + /// TextSpan after mistake + yield TextSpan( + text: text.substring(currentOffset), + style: style, + ); } /// Apply changes to Mistake list while new data being fetched @@ -134,21 +139,19 @@ class ColoredTextEditingController extends TextEditingController { Color _getMistakeColor(MistakeType type) { switch (type) { case MistakeType.misspelling: - return highlightColorScheme?.misspellingMistakeColor ?? Colors.red; + return highlightStyle.misspellingMistakeColor; case MistakeType.typographical: - return highlightColorScheme?.typographicalMistakeColor ?? Colors.green; + return highlightStyle.typographicalMistakeColor; case MistakeType.grammar: - return highlightColorScheme?.grammarMistakeColor ?? Colors.amber; + return highlightStyle.grammarMistakeColor; case MistakeType.uncategorized: - return highlightColorScheme?.uncategorizedMistakeColor ?? Colors.blue; + return highlightStyle.uncategorizedMistakeColor; case MistakeType.nonConformance: - return highlightColorScheme?.nonConformanceMistakeColor ?? - Colors.greenAccent; + return highlightStyle.nonConformanceMistakeColor; case MistakeType.style: - return highlightColorScheme?.styleMistakeColor ?? - Colors.deepPurpleAccent; + return highlightStyle.styleMistakeColor; case MistakeType.other: - return highlightColorScheme?.otherMistakeColor ?? Colors.white60; + return highlightStyle.otherMistakeColor; } } } diff --git a/lib/domain/highlight_colors.dart b/lib/domain/highlight_colors.dart deleted file mode 100644 index 29a3080..0000000 --- a/lib/domain/highlight_colors.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'dart:ui'; - -/// Class creates color scheme for highlighting mistakes -class HighlightColors { - /// Misspelling mistake highlight color - final Color? misspellingMistakeColor; - - /// Misspelling mistake highlight color - final Color? typographicalMistakeColor; - - /// Typographical mistake highlight color - final Color? grammarMistakeColor; - - /// Uncategorized mistake highlight color - final Color? uncategorizedMistakeColor; - - /// NonConformance mistake highlight color - final Color? nonConformanceMistakeColor; - - /// Style mistake highlight color - final Color? styleMistakeColor; - - /// Other mistake highlight color - final Color? otherMistakeColor; - - ///Color scheme constructor - HighlightColors( - this.misspellingMistakeColor, - this.typographicalMistakeColor, - this.grammarMistakeColor, - this.uncategorizedMistakeColor, - this.nonConformanceMistakeColor, - this.styleMistakeColor, - this.otherMistakeColor, - ); -} diff --git a/lib/domain/highlight_style.dart b/lib/domain/highlight_style.dart new file mode 100644 index 0000000..f7c0925 --- /dev/null +++ b/lib/domain/highlight_style.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +/// Class creates color scheme for highlighting mistakes +class HighlightStyle { + ///Initial values + static const double _initialBackgroundOpacity = 0.2; + static const double _initialLineHeight = 1.5; + + /// Misspelling mistake highlight color + final Color misspellingMistakeColor; + + /// Misspelling mistake highlight color + final Color typographicalMistakeColor; + + /// Typographical mistake highlight color + final Color grammarMistakeColor; + + /// Uncategorized mistake highlight color + final Color uncategorizedMistakeColor; + + /// NonConformance mistake highlight color + final Color nonConformanceMistakeColor; + + /// Style mistake highlight color + final Color styleMistakeColor; + + /// Other mistake highlight color + final Color otherMistakeColor; + + /// background opacity for mistake TextSpan + final double backgroundOpacity; + + /// mistake TextSpan underline thickness + final double mistakeLineThickness; + + ///Color scheme constructor + const HighlightStyle({ + this.misspellingMistakeColor = Colors.red, + this.typographicalMistakeColor = Colors.green, + this.grammarMistakeColor = Colors.amber, + this.uncategorizedMistakeColor = Colors.blue, + this.nonConformanceMistakeColor = Colors.greenAccent, + this.styleMistakeColor = Colors.deepPurpleAccent, + this.otherMistakeColor = Colors.white60, + this.backgroundOpacity = _initialBackgroundOpacity, + this.mistakeLineThickness = _initialLineHeight, + }); +} diff --git a/lib/languagetool_textfield.dart b/lib/languagetool_textfield.dart index b86e916..1050872 100644 --- a/lib/languagetool_textfield.dart +++ b/lib/languagetool_textfield.dart @@ -2,7 +2,8 @@ library languagetool_textfield; export 'package:language_tool/language_tool.dart'; -export 'domain/highlight_colors.dart'; +export 'core/controllers/colored_text_editing_controller.dart'; +export 'domain/highlight_style.dart'; export 'domain/language_check_service.dart'; export 'domain/mistake.dart'; export 'implementations/debounce_lang_tool_service.dart'; diff --git a/lib/presentation/language_tool_text_field.dart b/lib/presentation/language_tool_text_field.dart index 4f25664..0fa9dab 100644 --- a/lib/presentation/language_tool_text_field.dart +++ b/lib/presentation/language_tool_text_field.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:languagetool_textfield/core/controllers/colored_text_editing_controller.dart'; -import 'package:languagetool_textfield/domain/highlight_colors.dart'; +import 'package:languagetool_textfield/domain/highlight_style.dart'; import 'package:languagetool_textfield/domain/language_check_service.dart'; /// A TextField widget that checks the grammar using the given [langService] @@ -18,7 +18,7 @@ class LanguageToolTextField extends StatefulWidget { final Widget Function()? mistakeBuilder; /// Color scheme to highlight mistakes - final HighlightColors? highlightColorScheme; + final ColoredTextEditingController coloredController; /// Creates a widget that checks grammar errors. const LanguageToolTextField({ @@ -27,7 +27,7 @@ class LanguageToolTextField extends StatefulWidget { required this.style, required this.decoration, this.mistakeBuilder, - this.highlightColorScheme, + required this.coloredController, }) : super(key: key); @override @@ -35,21 +35,17 @@ class LanguageToolTextField extends StatefulWidget { } class _LanguageToolTextFieldState extends State { - ColoredTextEditingController? _controller; /// Sends API request to get a list of Mistake Future _check(String text) async { final mistakes = await widget.langService.findMistakes(text); if (mistakes.isNotEmpty) { - _controller?.highlightMistakes(mistakes); + widget.coloredController.highlightMistakes(mistakes); } } @override void initState() { - _controller = ColoredTextEditingController( - highlightColorScheme: widget.highlightColorScheme, - ); super.initState(); } @@ -59,12 +55,8 @@ class _LanguageToolTextFieldState extends State { padding: const EdgeInsets.all(24.0), child: Center( child: TextField( - controller: _controller, - onChanged: (String text) { - if (_controller != null) { - _check(text); - } - }, + controller: widget.coloredController, + onChanged: _check, style: widget.style, decoration: widget.decoration, ), @@ -75,6 +67,6 @@ class _LanguageToolTextFieldState extends State { @override void dispose() { super.dispose(); - _controller?.dispose(); // disposes controller + widget.coloredController.dispose(); } } From ee0f0ec2809836a39c26782e6fa2bd601c375fca Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 20:30:23 +0300 Subject: [PATCH 08/14] dart format fix --- lib/domain/highlight_style.dart | 2 +- lib/presentation/language_tool_text_field.dart | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/domain/highlight_style.dart b/lib/domain/highlight_style.dart index f7c0925..bbfa756 100644 --- a/lib/domain/highlight_style.dart +++ b/lib/domain/highlight_style.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; class HighlightStyle { ///Initial values static const double _initialBackgroundOpacity = 0.2; - static const double _initialLineHeight = 1.5; + static const double _initialLineHeight = 1.5; /// Misspelling mistake highlight color final Color misspellingMistakeColor; diff --git a/lib/presentation/language_tool_text_field.dart b/lib/presentation/language_tool_text_field.dart index 0fa9dab..2cb1e15 100644 --- a/lib/presentation/language_tool_text_field.dart +++ b/lib/presentation/language_tool_text_field.dart @@ -35,7 +35,6 @@ class LanguageToolTextField extends StatefulWidget { } class _LanguageToolTextFieldState extends State { - /// Sends API request to get a list of Mistake Future _check(String text) async { final mistakes = await widget.langService.findMistakes(text); From e11c6f26ce9e31f039fc45d1d1db07ee689b344a Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Thu, 27 Apr 2023 20:53:57 +0300 Subject: [PATCH 09/14] applied fixes for mistake list --- .../colored_text_editing_controller.dart | 61 ++++--------------- .../language_tool_text_field.dart | 1 - 2 files changed, 11 insertions(+), 51 deletions(-) diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index 5d7cd35..ffe498c 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -23,6 +23,13 @@ class ColoredTextEditingController extends TextEditingController { this.highlightStyle = const HighlightStyle(), }); + /// Clear mistakes list when text mas modified + void _handleTextChange(String newValue) { + if (newValue.length != text.length) { + _mistakes.clear(); + } + } + /// Generates TextSpan from Mistake list @override TextSpan buildTextSpan({ @@ -30,9 +37,9 @@ class ColoredTextEditingController extends TextEditingController { TextStyle? style, required bool withComposing, }) { - final int textLength = text.length; - final Iterable spanList = - _generateSpans(textLength: textLength, style: style); + final Iterable spanList = _generateSpans( + style: style, + ); return TextSpan( children: spanList.toList(), @@ -41,19 +48,12 @@ class ColoredTextEditingController extends TextEditingController { /// Generator function to create TextSpan instances Iterable _generateSpans({ - required int textLength, + // required int textLength, TextStyle? style, }) sync* { int currentOffset = 0; // enter index for (final Mistake mistake in _mistakes) { - /// Breaks the loop if iterated Mistake offset is bigger than text - /// length. - if (mistake.offset > textLength || - mistake.offset + mistake.length > textLength) { - break; - } - /// TextSpan before mistake yield TextSpan( text: text.substring( @@ -90,45 +90,6 @@ class ColoredTextEditingController extends TextEditingController { ); } - /// Apply changes to Mistake list while new data being fetched - void _handleTextChange(String newText) { - final int deltaLength = newText.length - text.length; - - /// Update the _mistakes list in-place based on the text modifications - _mistakes = _mistakes - .map((mistake) { - int newOffset = mistake.offset; - int newLength = mistake.length; - - /// If the text modification starts within the mistake - if (selection.start >= mistake.offset && - selection.start <= mistake.offset + mistake.length) { - newLength += deltaLength; - } - - /// If the text modification starts before the mistake - else if (selection.start < mistake.offset) { - newOffset += deltaLength; - } - - /// Return the updated mistake (if the length is greater than 0) - return newLength > 0 - ? Mistake( - message: mistake.message, - type: mistake.type, - offset: newOffset, - length: newLength, - replacements: mistake.replacements, - ) - : null; - }) - .whereType() - .toList(); - - /// Notify listeners to rebuild the widget - notifyListeners(); - } - /// A method sets new list of Mistake and triggers buildTextSpan void highlightMistakes(List list) { _mistakes = list; diff --git a/lib/presentation/language_tool_text_field.dart b/lib/presentation/language_tool_text_field.dart index 2cb1e15..9d073c5 100644 --- a/lib/presentation/language_tool_text_field.dart +++ b/lib/presentation/language_tool_text_field.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:languagetool_textfield/core/controllers/colored_text_editing_controller.dart'; -import 'package:languagetool_textfield/domain/highlight_style.dart'; import 'package:languagetool_textfield/domain/language_check_service.dart'; /// A TextField widget that checks the grammar using the given [langService] From 8ac8f802c07115472f5c9fe2d19ddb5427185dc3 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Fri, 28 Apr 2023 15:03:00 +0300 Subject: [PATCH 10/14] Made some minor adjustments and transferred the check service to the controller. --- example/lib/app.dart | 26 ++++----- .../colored_text_editing_controller.dart | 53 ++++++++++++------- .../language_tool_text_field.dart | 28 +--------- 3 files changed, 49 insertions(+), 58 deletions(-) diff --git a/example/lib/app.dart b/example/lib/app.dart index 2f518fe..b6b2ad6 100644 --- a/example/lib/app.dart +++ b/example/lib/app.dart @@ -15,31 +15,33 @@ class _AppState extends State { static final LanguageTool _languageTool = LanguageTool(); /// Initialize DebounceLangToolService - final DebounceLangToolService _debouncedLangService; + static final DebounceLangToolService _debouncedLangService = + DebounceLangToolService( + LangToolService(_languageTool), + const Duration(milliseconds: 500), + ); /// Initialize ColoredTextEditingController - final ColoredTextEditingController controller = - ColoredTextEditingController(); - - /// Set DebounceLangToolService - _AppState() - : _debouncedLangService = DebounceLangToolService( - LangToolService(_languageTool), - const Duration(milliseconds: 500), - ); + final ColoredTextEditingController _controller = + ColoredTextEditingController(languageCheckService: _debouncedLangService); @override Widget build(BuildContext context) { return Material( child: LanguageToolTextField( - langService: _debouncedLangService, style: const TextStyle(), decoration: const InputDecoration(), mistakeBuilder: () { return Container(); }, - coloredController: controller, + coloredController: _controller, ), ); } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } } diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index ffe498c..0bd5f50 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -1,6 +1,8 @@ + import 'package:flutter/material.dart'; import 'package:languagetool_textfield/core/enums/mistake_type.dart'; import 'package:languagetool_textfield/domain/highlight_style.dart'; +import 'package:languagetool_textfield/domain/language_check_service.dart'; import 'package:languagetool_textfield/domain/mistake.dart'; /// A TextEditingController with overrides buildTextSpan for building @@ -9,6 +11,9 @@ class ColoredTextEditingController extends TextEditingController { /// Color scheme to highlight mistakes final HighlightStyle highlightStyle; + /// Language tool API index + final LanguageCheckService languageCheckService; + /// List which contains Mistake objects spans are built from List _mistakes = []; @@ -20,13 +25,22 @@ class ColoredTextEditingController extends TextEditingController { /// Controller constructor ColoredTextEditingController({ + required this.languageCheckService, this.highlightStyle = const HighlightStyle(), }); - /// Clear mistakes list when text mas modified - void _handleTextChange(String newValue) { - if (newValue.length != text.length) { + /// Clear mistakes list when text mas modified and get a new list of mistakes + /// via API + Future _handleTextChange(String newText) 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.length != text.length) { _mistakes.clear(); + final mistakes = await languageCheckService.findMistakes(newText); + if (mistakes.isNotEmpty) { + _mistakes = mistakes; + } + notifyListeners(); } } @@ -37,12 +51,12 @@ class ColoredTextEditingController extends TextEditingController { TextStyle? style, required bool withComposing, }) { - final Iterable spanList = _generateSpans( + final formattedTextSpans = _generateSpans( style: style, ); return TextSpan( - children: spanList.toList(), + children: formattedTextSpans.toList(), ); } @@ -68,16 +82,21 @@ class ColoredTextEditingController extends TextEditingController { /// Mistake highlighted TextSpan yield TextSpan( - text: text.substring(mistake.offset, mistake.offset + mistake.length), - mouseCursor: MaterialStateMouseCursor.clickable, - style: style?.copyWith( - backgroundColor: mistakeColor.withOpacity( - highlightStyle.backgroundOpacity, + children: [ + TextSpan( + text: + text.substring(mistake.offset, mistake.offset + mistake.length), + mouseCursor: MaterialStateMouseCursor.clickable, + style: style?.copyWith( + backgroundColor: mistakeColor.withOpacity( + highlightStyle.backgroundOpacity, + ), + decoration: TextDecoration.underline, + decorationColor: mistakeColor, + decorationThickness: highlightStyle.mistakeLineThickness, + ), ), - decoration: TextDecoration.underline, - decorationColor: mistakeColor, - decorationThickness: highlightStyle.mistakeLineThickness, - ), + ], ); currentOffset = mistake.offset + mistake.length; @@ -90,12 +109,6 @@ class ColoredTextEditingController extends TextEditingController { ); } - /// A method sets new list of Mistake and triggers buildTextSpan - void highlightMistakes(List list) { - _mistakes = list; - notifyListeners(); - } - /// Returns color for mistake TextSpan style Color _getMistakeColor(MistakeType type) { switch (type) { diff --git a/lib/presentation/language_tool_text_field.dart b/lib/presentation/language_tool_text_field.dart index 9d073c5..b51523c 100644 --- a/lib/presentation/language_tool_text_field.dart +++ b/lib/presentation/language_tool_text_field.dart @@ -1,12 +1,9 @@ import 'package:flutter/material.dart'; import 'package:languagetool_textfield/core/controllers/colored_text_editing_controller.dart'; -import 'package:languagetool_textfield/domain/language_check_service.dart'; -/// A TextField widget that checks the grammar using the given [langService] +/// A TextField widget that checks the grammar using the given +/// [coloredController] class LanguageToolTextField extends StatefulWidget { - /// A service for checking errors. - final LanguageCheckService langService; - /// A style to use for the text being edited. final TextStyle style; @@ -22,7 +19,6 @@ class LanguageToolTextField extends StatefulWidget { /// Creates a widget that checks grammar errors. const LanguageToolTextField({ Key? key, - required this.langService, required this.style, required this.decoration, this.mistakeBuilder, @@ -34,19 +30,6 @@ class LanguageToolTextField extends StatefulWidget { } class _LanguageToolTextFieldState extends State { - /// Sends API request to get a list of Mistake - Future _check(String text) async { - final mistakes = await widget.langService.findMistakes(text); - if (mistakes.isNotEmpty) { - widget.coloredController.highlightMistakes(mistakes); - } - } - - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { return Padding( @@ -54,17 +37,10 @@ class _LanguageToolTextFieldState extends State { child: Center( child: TextField( controller: widget.coloredController, - onChanged: _check, style: widget.style, decoration: widget.decoration, ), ), ); } - - @override - void dispose() { - super.dispose(); - widget.coloredController.dispose(); - } } From d2b3b99d576c31d5c7958cd996d6541e999d56f0 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Fri, 28 Apr 2023 15:05:22 +0300 Subject: [PATCH 11/14] dart format fix --- lib/core/controllers/colored_text_editing_controller.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index 0bd5f50..c5c6d71 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:languagetool_textfield/core/enums/mistake_type.dart'; import 'package:languagetool_textfield/domain/highlight_style.dart'; From a31c983b84c10ca852b3ebf5d1d58a7cc5bd12d3 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Fri, 28 Apr 2023 15:47:04 +0300 Subject: [PATCH 12/14] small notifyListeners() fix --- lib/core/controllers/colored_text_editing_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index c5c6d71..87291cd 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -38,8 +38,8 @@ class ColoredTextEditingController extends TextEditingController { final mistakes = await languageCheckService.findMistakes(newText); if (mistakes.isNotEmpty) { _mistakes = mistakes; + notifyListeners(); } - notifyListeners(); } } From 24bd25d52c56c86dfb73156d14db6df2b36fc3c7 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:47:18 +0300 Subject: [PATCH 13/14] added TextDecoration property to HighlightStyle --- .../colored_text_editing_controller.dart | 15 +++++++-------- lib/domain/highlight_style.dart | 4 ++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index 87291cd..5045a77 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -33,13 +33,12 @@ class ColoredTextEditingController extends TextEditingController { Future _handleTextChange(String newText) 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.length != text.length) { - _mistakes.clear(); - final mistakes = await languageCheckService.findMistakes(newText); - if (mistakes.isNotEmpty) { - _mistakes = mistakes; - notifyListeners(); - } + if (newText.length == text.length) return; + _mistakes.clear(); + final mistakes = await languageCheckService.findMistakes(newText); + if (mistakes.isNotEmpty) { + _mistakes = mistakes; + notifyListeners(); } } @@ -90,7 +89,7 @@ class ColoredTextEditingController extends TextEditingController { backgroundColor: mistakeColor.withOpacity( highlightStyle.backgroundOpacity, ), - decoration: TextDecoration.underline, + decoration: highlightStyle.decoration, decorationColor: mistakeColor, decorationThickness: highlightStyle.mistakeLineThickness, ), diff --git a/lib/domain/highlight_style.dart b/lib/domain/highlight_style.dart index bbfa756..6712d7d 100644 --- a/lib/domain/highlight_style.dart +++ b/lib/domain/highlight_style.dart @@ -33,6 +33,9 @@ class HighlightStyle { /// mistake TextSpan underline thickness final double mistakeLineThickness; + /// Mistaken text decoration style + final TextDecoration decoration; + ///Color scheme constructor const HighlightStyle({ this.misspellingMistakeColor = Colors.red, @@ -44,5 +47,6 @@ class HighlightStyle { this.otherMistakeColor = Colors.white60, this.backgroundOpacity = _initialBackgroundOpacity, this.mistakeLineThickness = _initialLineHeight, + this.decoration = TextDecoration.underline, }); } From 096d049dc15db01a89d5a50a1d759d1a00630e68 Mon Sep 17 00:00:00 2001 From: nazarski <110051407+nazarski@users.noreply.github.com> Date: Fri, 28 Apr 2023 17:09:13 +0300 Subject: [PATCH 14/14] Update lib/core/controllers/colored_text_editing_controller.dart Co-authored-by: Yuri Prykhodko <40931732+solid-yuriiprykhodko@users.noreply.github.com> --- lib/core/controllers/colored_text_editing_controller.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/core/controllers/colored_text_editing_controller.dart b/lib/core/controllers/colored_text_editing_controller.dart index 5045a77..3eb93cd 100644 --- a/lib/core/controllers/colored_text_editing_controller.dart +++ b/lib/core/controllers/colored_text_editing_controller.dart @@ -60,7 +60,6 @@ class ColoredTextEditingController extends TextEditingController { /// Generator function to create TextSpan instances Iterable _generateSpans({ - // required int textLength, TextStyle? style, }) sync* { int currentOffset = 0; // enter index