# extended_text_field gives Flutter's TextField inline images, mentions and real copy values

> extended_text_field is a Flutter package that extends the official TextField so a field can hold special text such as inline images, @mentions and custom backgrounds, and so the copied clipboard value is the real text rather than the rendered one. The API is a SpecialText subclass plus a span builder, and the README documents two hard limitations around right to left text and obscured input.

**fluttercandies/extended_text_field** — extended official text field to quickly build special text like inline image, @somebody, custom background etc.

- Repository: https://github.com/fluttercandies/extended_text_field
- Stars: 611 · Forks: 174
- Language: Dart
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/fluttercandies-extended-text-field

## The gap is between what a field displays and what it copies

extended_text_field is a third-party extension library for Flutter's official TextField component, and the README is clear about the direction of travel: it extends the official text field to build special text like inline image, @somebody and custom background quickly, and it also supports building a custom selection toolbar and handles.

The feature comparison table in the README shows why anyone would want it. Against the plain TextField, the extended version supports inline images and mixed text, where the official widget only supports displaying text and has issues with text selection. It supports copying the actual value, which the official TextField does not, and it supports quick construction of rich text based on a text format, which the official widget does not.

The copying point is the one worth dwelling on. In a rich text field, what is rendered and what the clipboard receives can be two different things, and the official TextField copies the rendered representation. For a mention or an inline image, the rendered form is not the value your backend wants. This package's answer is that the underlying text stays intact and the special text is a rendering layer over it.

The package is published on pub.dev, there is a web demo hosted by the FlutterCandies organisation, and the repository ships an example/ directory with the platform folders for android, ios, web, windows and macos, so the demo covers more platforms than a typical package example.

## Special text is a subclass with a flag and a finished span

The mechanism is a class hierarchy. A piece of special text extends SpecialText, declares the character that flags it, and overrides finishText to return the InlineSpan that should be drawn for that run. The README's example builds an @mention, and the class shape is:

```dart
class AtText extends SpecialText {
  static const String flag = "@";
  final int start;

  /// whether show background for @somebody
  final bool showAtBackground;

  AtText(TextStyle textStyle, SpecialTextGestureTapCallback onTap,
      {this.showAtBackground: false, this.start})
      : super(
          flag,
          " ",
          textStyle,
        );
}
```

Two details carry the design. The static flag is what the builder scans for, so the trigger character is a property of the text type rather than a hardcoded string in the parsing loop. And the class carries a start offset, which is the index in the source string where the special text begins, so that the underlying text and the rendered spans can be reconciled when the user edits the field.

The overridden method returns either a BackgroundTextSpan, when the background should be shown, or a SpecialTextSpan, which is the plain variant. Both take the actual text, the start offset, a style and a recognizer that fires the onTap callback. The BackgroundTextSpan in the example is constructed with deleteAll set to true, with a comment noting that the caret can move into special text.

So a special text type is a small, self-contained object: it knows its trigger, it knows where it sits in the string, and it knows how to draw itself and how to react to a tap.

## The builder is where the string gets parsed into spans

Parsing is separated from rendering. You subclass SpecialTextSpanBuilder, and the package asks it to produce spans for the raw string. The README example overrides createSpecialText, and the structure is:

```dart
  @override
  SpecialText createSpecialText(String flag,
      {TextStyle textStyle, SpecialTextGestureTapCallback onTap, int index}) {
    if (flag == null || flag == "") return null;

    ///index is end index of start flag, so text start index should be index-(flag.length-1)
    if (isStart(flag, AtText.flag)) {
      return AtText(textStyle, onTap,
```

The comment in that snippet is the part to understand. The index handed to the method is the end index of the start flag, so the text start index is index minus the flag length minus one. Get that arithmetic wrong and every span is offset by one character, which shows up as a highlight that starts in the wrong place.

The builder dispatches on which flag matched, so a single field can handle mentions, emoji runs and dollar-prefixed tokens at once, with each type being its own class. The example checks three flags in sequence, and the base class helper isStart does the comparison against the type's own flag constant rather than a literal.

The builder also has a build method that takes the whole string and a style and returns a TextSpan, and the README example overrides it to call the parent implementation and then adjust the result. That is the extension point for cases where the whole span tree needs different treatment rather than a single token.

## Inline images are a span with a required size

Images inside the field are handled by a dedicated span type. The README shows ImageSpan being constructed with an image provider and a set of parameters:

```dart
ImageSpan(
    ImageProvider image, {
    Key key,
    @required double imageWidth,
    @required double imageHeight,
    EdgeInsets margin,
    int start: 0,
    ui.PlaceholderAlignment alignment = ui.PlaceholderAlignment.bottom,
```

The image width and height are required, and that is the interesting constraint. A span embedded in a text flow has no intrinsic size, so the caller has to decide how large the image is in the field, and the package makes that explicit rather than guessing. The margin and the placeholder alignment control how the image sits on the text baseline, and the start offset ties the image back to its position in the underlying string, the same mechanism the mention type uses.

The parameter list continues past what the README shows, with the text baseline, a style, a BoxFit that defaults to scaleDown, loading and frame builders, a semantic label and a flag for whether the image is decorative. Those last two are the accessibility hooks, and their presence is a decent signal that the package was written with screen readers in mind rather than bolted on later.

The section on the image span is followed in the README by a section on caching images, and the table of contents also lists TextSelectionControls and WidgetSpan, so an image is one of several kinds of non plain text the package can place inside a field.

## Two limitations the README states outright

The Limitation section is short and worth reading before you commit. The first entry: the package will not handle special text when TextDirection.rtl, and the reason given is that the image position calculated by TextPainter is strange. The second: it will not handle special text when obscureText is true.

Both are real constraints rather than missing features, and the first one in particular is structural. TextPainter measures and positions glyphs, and a right to left layout inverts the direction in which offsets advance, so an offset computed for a left to right run is meaningless in the other direction. The workaround would be to maintain mirrored offsets, which is a substantial change rather than a patch.

The obscureText case is different in kind. Obscured text is a password style field, and there is no sensible inline image or mention in a password field, so the constraint costs almost nothing in practice. It is listed because the package does not silently misbehave there.

One more limitation is implied rather than stated. The whole approach rests on the text staying in sync with the rendered spans, which is why every special text carries a start offset. The package handles the common editing cases, but the README does not describe how selection, paste and undo interact with the special text in detail, so those are worth exercising in your own test suite rather than assuming.

## A HarmonyOS build and a version line that needs checking

The README states that HarmonyOS is supported and that the latest version containing the ohos tag should be used, with the tag visible in the Versions tab. The dependency snippet it gives is:

```yaml
dependencies:
  extended_text_field: 11.0.1-ohos
```

That number does not match the release history in the repository listing, which records a single release, v3.7.0 with the description flutter 3.7.0, published on 2023-05-14. The pub.dev badge in the README tracks the current published version separately from that tag.

So there is a version question to settle before you pin anything, and it is not a cosmetic one. The recorded release list is the same one the project metadata exposes, and it has not moved in more than three years, while the README demonstrates a version numbered 11.0.1 for a specific platform. Either the recorded release history is not the whole story, or the HarmonyOS build follows a different numbering line. Either way, the README's own instruction to check the Versions tab is the right one, and it should be done before you write a dependency constraint.

On maintenance, the last push was on 2026-07-25 and the repository is not archived, so the code is being touched. The repository has a CHANGELOG.md, a CODEOWNERS file, an analysis_options.yaml, a .metadata file, a pubspec.yaml and an example/ application, and the README is published in English and in Simplified Chinese, with the English one being the one you are reading.

The licence is MIT, with a LICENSE file at the repository root, so there is no copyleft obligation on the code you build with it. That is a licensing note rather than legal advice, and it does not speak to the platform specific considerations of any particular target.

## Conclusion

Adopt extended_text_field if you are building a chat composer, a mention field or any Flutter input where users insert inline images and you need the clipboard to contain the real text, and if your field is left to right and not obscured. Do not adopt it for right to left layouts or for password style fields with obscureText set, because the README states both are not supported. Verify first which version you install, since the README shows a HarmonyOS build tagged 11.0.1-ohos while the recorded release history stops at v3.7.0, and test the copy and paste behaviour of your own field rather than assuming the official TextField limitations are all covered.

## FAQ

### What does extended_text_field add to Flutter's TextField?

The README's comparison table lists inline images and mixed text, copying the actual value of the text rather than the rendered form, and quick construction of rich text based on a text format. The official TextField supports only text display and has issues with text selection.

### How do I create an @mention in extended_text_field?

Extend SpecialText, declare a static flag such as @, keep the start index, and override finishText to return a SpecialTextSpan or BackgroundTextSpan with the text, start offset, style and a tap recognizer. Then override createSpecialText in a SpecialTextSpanBuilder so the flag is recognised while parsing.

### What are the limitations of extended_text_field?

The README states two. Special text is not handled when TextDirection.rtl, because the image position calculated by TextPainter is strange, and it is not handled when obscureText is true.

### How do I show an inline image in an extended_text_field?

Use the ImageSpan with an ImageProvider. The image width and height are required parameters, and you can also pass a margin, a placeholder alignment, a start offset, a BoxFit that defaults to scaleDown, loading and frame builders, and a semantic label.

### How do I install extended_text_field for HarmonyOS?

The README says HarmonyOS is supported using the latest version that carries the ohos tag, shown in the Versions tab, and gives a dependency entry of extended_text_field: 11.0.1-ohos. Check the published versions yourself before pinning, since that number does not match the v3.7.0 release recorded in the repository listing.

## Sources

- [fluttercandies/extended_text_field on GitHub](https://github.com/fluttercandies/extended_text_field)
- [Issues](https://github.com/fluttercandies/extended_text_field/issues)
- [License: MIT](https://github.com/fluttercandies/extended_text_field/blob/master/LICENSE)
- [README](https://github.com/fluttercandies/extended_text_field/blob/master/README.md)
- [Releases](https://github.com/fluttercandies/extended_text_field/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/fluttercandies-extended-text-field
