Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- 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
Expand Down
8 changes: 8 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ class _AppState extends State<App> {
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(() {
Expand Down
100 changes: 72 additions & 28 deletions lib/src/core/controllers/language_tool_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -73,21 +75,46 @@ class LanguageToolController extends TextEditingController {
_languageToolClient.language = language;
}

/// Indicates whether spell checking is enabled
bool get isEnabled => _isEnabled;

set isEnabled(bool value) {
if (value == _isEnabled) return;

_isEnabled = value;

if (_isEnabled) {
_handleTextChange(text, spellCheckSameText: true);
} else {
_mistakes = [];
for (final recognizer in _recognizers) {
recognizer.dispose();
}
_recognizers.clear();

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();
}

Expand All @@ -111,9 +138,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,
Expand All @@ -132,6 +167,10 @@ class LanguageToolController extends TextEditingController {

/// Replaces mistake with given replacement
void replaceMistake(Mistake mistake, String replacement) {
if (!_isEnabled) {
throw StateError('LanguageToolController is not enabled');
}

final mistakes = List<Mistake>.from(_mistakes);
mistakes.remove(mistake);
_mistakes = mistakes;
Expand All @@ -145,33 +184,38 @@ class LanguageToolController extends TextEditingController {

/// Clear mistakes list when text mas modified and get a new list of mistakes
/// via API
Future<void> _handleTextChange(String newText) async {
Future<void> _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 (newText == text || newText.isEmpty) return;
if (spellCheckSameText || newText != text && newText.isNotEmpty) {
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();

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 filteredMistakes = _filterMistakesOnChanged(newText);
_mistakes = filteredMistakes.toList();
final mistakes = mistakesWrapper.result();
_fetchError = mistakesWrapper.error;

// 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();
_mistakes = mistakes;
notifyListeners();
}
_recognizers.clear();

final mistakesWrapper = await _latestResponseService.processLatestOperation(
() => _languageCheckService?.findMistakes(newText) ?? Future(() => null),
);
if (mistakesWrapper == null || !mistakesWrapper.hasResult) return;

final mistakes = mistakesWrapper.result();
_fetchError = mistakesWrapper.error;

_mistakes = mistakes;
notifyListeners();
}

/// Generator function to create TextSpan instances
Expand Down Expand Up @@ -200,7 +244,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,
Expand All @@ -223,7 +267,7 @@ class LanguageToolController extends TextEditingController {
};

/// Adding recognizer to the list for future disposing
_recognizers.add(_onTap);
_recognizers.add(onTap);

/// Mistake highlighted TextSpan
yield TextSpan(
Expand All @@ -242,7 +286,7 @@ class LanguageToolController extends TextEditingController {
decorationColor: mistakeColor,
decorationThickness: highlightStyle.mistakeLineThickness,
),
recognizer: _onTap,
recognizer: onTap,
),
],
);
Expand Down