I'm implementing a custom text field and I would like to style certain keywords (namely hashtags) differently than the rest of the text as the user type them in.
Kind of like this:
Is there a way to do that in Flutter ?
I'm implementing a custom text field and I would like to style certain keywords (namely hashtags) differently than the rest of the text as the user type them in.
Kind of like this:
Is there a way to do that in Flutter ?
This question is very similar to How to change color of particular text in a text field dynamically?
I answered it there in: https://stackoverflow.com/a/57846261/5280562
In short: you can extend EditableText
widget including its EditableTextState
class and override buildTextSpan
method.
Below is a working example called AnnotatedEditableText
that I use in my app.
You need to supply a list of Annotation
objects which describe which ranges of text need to be highlighted and what style to use.
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);
}
}
It's also available in this Gist: https://gist.github.com/pulyaevskiy/d7af7217c2e71f31dfb78699f91dfbb5
I actually had the same problem and found the AnnotatedEditbleText
which helped me a lot.
I published the helpful package to solve this kind of problem.
The TextField
does not provide that functionality.
https://pub.dartlang.org/packages/zefyr can do that though.
I think there are some more ''hard'' ways to do this
The first one:
Make a row widget, add a part of the String until the word you want to highlight, add the special word, style it and add the rest of your string.
Or, you could try RichText
Günter post it about the zefyr package, I didn't use it yet, but if suits you, I'll be glad that helped