-
-
Save pulyaevskiy/d7af7217c2e71f31dfb78699f91dfbb5 to your computer and use it in GitHub Desktop.
import 'package:flutter/widgets.dart'; | |
class Annotation extends Comparable<Annotation> { | |
Annotation({@required this.range, this.style}); | |
final TextRange range; | |
final TextStyle style; | |
@override | |
int compareTo(Annotation other) { | |
return range.start.compareTo(other.range.start); | |
} | |
@override | |
String toString() { | |
return 'Annotation(range:$range, style:$style)'; | |
} | |
} | |
class AnnotatedEditableText extends EditableText { | |
AnnotatedEditableText({ | |
Key key, | |
FocusNode focusNode, | |
TextEditingController controller, | |
TextStyle style, | |
ValueChanged<String> onChanged, | |
ValueChanged<String> onSubmitted, | |
Color cursorColor, | |
Color selectionColor, | |
TextSelectionControls selectionControls, | |
this.annotations, | |
}) : super( | |
key: key, | |
focusNode: focusNode, | |
controller: controller, | |
cursorColor: cursorColor, | |
style: style, | |
keyboardType: TextInputType.text, | |
autocorrect: true, | |
autofocus: true, | |
selectionColor: selectionColor, | |
selectionControls: selectionControls, | |
onChanged: onChanged, | |
onSubmitted: onSubmitted, | |
); | |
final List<Annotation> annotations; | |
@override | |
AnnotatedEditableTextState createState() => new AnnotatedEditableTextState(); | |
} | |
class AnnotatedEditableTextState extends EditableTextState { | |
@override | |
AnnotatedEditableText get widget => super.widget; | |
List<Annotation> getRanges() { | |
var source = widget.annotations; | |
source.sort(); | |
var result = new List<Annotation>(); | |
Annotation prev; | |
for (var item in source) { | |
if (prev == null) { | |
// First item, check if we need one before it. | |
if (item.range.start > 0) { | |
result.add(new Annotation( | |
range: TextRange(start: 0, end: item.range.start), | |
)); | |
} | |
result.add(item); | |
prev = item; | |
continue; | |
} else { | |
// Consequent item, check if there is a gap between. | |
if (prev.range.end > item.range.start) { | |
// Invalid ranges | |
throw new StateError( | |
'Invalid (intersecting) ranges for annotated field'); | |
} else if (prev.range.end < item.range.start) { | |
result.add(Annotation( | |
range: TextRange(start: prev.range.end, end: item.range.start), | |
)); | |
} | |
// Also add current annotation | |
result.add(item); | |
prev = item; | |
} | |
} | |
// Also check for trailing range | |
final String text = textEditingValue.text; | |
if (result.last.range.end < text.length) { | |
result.add(Annotation( | |
range: TextRange(start: result.last.range.end, end: text.length), | |
)); | |
} | |
return result; | |
} | |
@override | |
TextSpan buildTextSpan() { | |
final String text = textEditingValue.text; | |
if (widget.annotations != null) { | |
var items = getRanges(); | |
var children = <TextSpan>[]; | |
for (var item in items) { | |
children.add( | |
TextSpan(style: item.style, text: item.range.textInside(text)), | |
); | |
} | |
return new TextSpan(style: widget.style, children: children); | |
} | |
return new TextSpan(style: widget.style, text: text); | |
} | |
} |
This is having problems when you try to delete a character from any word that has an annotation. Im trying to solve it somehow
@joselicht90 did you solve this?
Any idea how to test this? because it extends EditableText, the flutter test for enterText fails with no widget: apparantly it cannot find a TextInput or EditableText (because, by extending, it becomes that!).
For example:
final String dummyDesc = 'hellow world';
final Finder annoted = find.byType(AnnotatedEditableText);
await tester.enterText(annoted, dummyDesc);
this will show the error
Bad state: No element
Any idea how to test this?
This is having problems when you try to delete a character from any word that has an annotation. Im trying to solve it somehow
You simply have to correct the buildTextSpan part. The error was raised when you delete a character because the Range could raise an exception. Here is my version that may fix your problem:
@override
TextSpan buildTextSpan() {
final String text = textEditingValue.text;
int textLength = text.length;
if (widget.annotations != null && textLength > 0) {
var items = getRanges();
var children = <TextSpan>[];
for (var item in items) {
if (item.range.end < textLength) {
children.add(
TextSpan(style: item.style, text: item.range.textInside(text)),
);
} else if (item.range.start <= textLength) {
children.add(
TextSpan(
style: item.style,
text: TextRange(start: item.range.start, end: text.length)
.textInside(text)),
);
}
}
return new TextSpan(style: widget.style, children: children);
}
return new TextSpan(style: widget.style, text: text);
}
}
When i try to write in it it gives me :
════════ Exception caught by widgets library ═══════════════════════════════════
The following StateError was thrown building Scrollable(axisDirection: right, physics: null, restorationId: null, dirty, dependencies: [ScrollConfiguration, _InheritedTheme, _LocalizationsScope-[GlobalKey#71eea], _EffectiveTickerMode, UnmanagedRestorationScope, MediaQuery], state: ScrollableState#af8e0(position: ScrollPositionWithSingleContext#d5829(offset: 0.0, range: 0.0..0.0, viewport: 200.0, ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#a96de, ScrollDirection.idle), effective physics: ClampingScrollPhysics -> RangeMaintainingScrollPhysics)):
Bad state: No element
i run on chrome and i am on beta channel
EDIT: oups my fault, you need to refresh the annotions list at all changes, even if the list is empty or you don't use any for the moment
It is not supporting multiline , btw I am working on it ..to resolve this issue
you need to pass a controller param in - i suspect the docs were different when this was written.