4

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:

enter image description here

Is there a way to do that in Flutter ?

Théo Champion
  • 1,701
  • 1
  • 21
  • 46

4 Answers4

5

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

pulyaevskiy
  • 267
  • 2
  • 7
1

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.

https://pub.dev/packages/hashtagable

0

The TextField does not provide that functionality.

https://pub.dartlang.org/packages/zefyr can do that though.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
-1

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

Fellipe Malta
  • 3,160
  • 1
  • 7
  • 12