Skip to content

Instantly share code, notes, and snippets.

@pulyaevskiy
Created June 28, 2018 19:39
Show Gist options
  • Save pulyaevskiy/d7af7217c2e71f31dfb78699f91dfbb5 to your computer and use it in GitHub Desktop.
Save pulyaevskiy/d7af7217c2e71f31dfb78699f91dfbb5 to your computer and use it in GitHub Desktop.
AnnotatedEditableText
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);
}
}
@rubberbird
Copy link

This is throwing an error around a null controller when I try and run it

@bionara
Copy link

bionara commented Apr 20, 2020

This is throwing an error around a null controller when I try and run it

you need to pass a controller param in - i suspect the docs were different when this was written.

@craigomac
Copy link

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?

@bionara
Copy link

bionara commented May 13, 2020

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?

@Jpec57
Copy link

Jpec57 commented Aug 2, 2020

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

@Paul-cbt
Copy link

Paul-cbt commented Jan 8, 2021

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

@deepatavy
Copy link

It is not supporting multiline , btw I am working on it ..to resolve this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment