extended_text 9.1.1 icon indicating copy to clipboard operation
extended_text: ^9.1.1 copied to clipboard

Extended official text to build special text like inline image or @somebody quickly,it also support custom background,custom over flow and custom selection toolbar and handles.

extended_text #

pub package GitHub stars GitHub forks GitHub license GitHub issues flutter-candies

Language: English | 中文简体

Extended official text to build special text like inline image or @somebody quickly,it also support custom background,custom over flow and custom selection toolbar and handles.

Web demo for ExtendedText

Table of contents #

Speical Text #

Create Speical Text #

extended text helps to convert your text to speical textSpan quickly.

for example, follwing code show how to create @xxxx speical textSpan.

class AtText extends SpecialText {
  AtText(TextStyle textStyle, SpecialTextGestureTapCallback onTap,
      {this.showAtBackground = false, this.start})
      : super(flag, ' ', textStyle, onTap: onTap);
  static const String flag = '@';
  final int start;

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

  InlineSpan finishText() {
    final TextStyle textStyle =
        this.textStyle?.copyWith(color: Colors.blue, fontSize: 16.0);

    final String atText = toString();

    return showAtBackground
        ? BackgroundTextSpan(
            background: Paint()..color = Colors.blue.withOpacity(0.15),
            text: atText,
            actualText: atText,
            start: start,

            ///caret can move into special text
            deleteAll: true,
            style: textStyle,
            recognizer: (TapGestureRecognizer()
              ..onTap = () {
                if (onTap != null) {
        : SpecialTextSpan(
            text: atText,
            actualText: atText,
            start: start,
            style: textStyle,
            recognizer: (TapGestureRecognizer()
              ..onTap = () {
                if (onTap != null) {

SpecialTextSpanBuilder #

create your SpecialTextSpanBuilder

class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder {
  MySpecialTextSpanBuilder({this.showAtBackground = false});

  /// whether show background for @somebody
  final bool showAtBackground;
  TextSpan build(String data,
      {TextStyle textStyle, SpecialTextGestureTapCallback onTap}) {
    if (kIsWeb) {
      return TextSpan(text: data, style: textStyle);

    return super.build(data, textStyle: textStyle, onTap: onTap);

  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(
        start: index - (AtText.flag.length - 1),
        showAtBackground: showAtBackground,
    } else if (isStart(flag, EmojiText.flag)) {
      return EmojiText(textStyle, start: index - (EmojiText.flag.length - 1));
    } else if (isStart(flag, DollarText.flag)) {
      return DollarText(textStyle, onTap,
          start: index - (DollarText.flag.length - 1));
    return null;

Image #

ImageSpan #

show inline image by using ImageSpan.

class ImageSpan extends ExtendedWidgetSpan {
    ImageProvider image, {
    Key key,
    @required double imageWidth,
    @required double imageHeight,
    EdgeInsets margin,
    int start = 0,
    ui.PlaceholderAlignment alignment = ui.PlaceholderAlignment.bottom,
    String actualText,
    TextBaseline baseline,
    BoxFit fit= BoxFit.scaleDown,
    ImageLoadingBuilder loadingBuilder,
    ImageFrameBuilder frameBuilder,
    String semanticLabel,
    bool excludeFromSemantics = false,
    Color color,
    BlendMode colorBlendMode,
    AlignmentGeometry imageAlignment = Alignment.center,
    ImageRepeat repeat = ImageRepeat.noRepeat,
    Rect centerSlice,
    bool matchTextDirection = false,
    bool gaplessPlayback = false,
    FilterQuality filterQuality = FilterQuality.low,
    GestureTapCallback onTap,
    HitTestBehavior behavior = HitTestBehavior.deferToChild,
imageThe image to display(ImageProvider).-
imageWidthThe width of image(not include margin)required
imageHeightThe height of image(not include margin)required
marginThe margin of image-
actualTextActual text, take care of it when enable selection,something likes "[love]"'\uFFFC'
startStart index of text,take care of it when enable selection.0

Selection #

selectionEnabledWhether enable selectionfalse
selectionColorColor of selectionTheme.of(context).textSelectionColor
dragStartBehaviorDragStartBehavior for text selectionDragStartBehavior.start
textSelectionControlsAn interface for building the selection UI, to be provided by the implementor of the toolbar widget or handle widgetextendedMaterialTextSelectionControls/extendedCupertinoTextSelectionControls

TextSelectionControls #

default value of textSelectionControls are MaterialExtendedTextSelectionControls/CupertinoExtendedTextSelectionControls

override buildToolbar or buildHandle to custom your toolbar widget or handle widget

class MyExtendedMaterialTextSelectionControls
    extends ExtendedMaterialTextSelectionControls {
  Widget buildToolbar(
    BuildContext context,
    Rect globalEditableRegion,
    double textLineHeight,
    Offset selectionMidpoint,
    List<TextSelectionPoint> endpoints,
    TextSelectionDelegate delegate,
  ) {}

  Widget buildHandle(
      BuildContext context, TextSelectionHandleType type, double textHeight) {

Control ToolBar Handle #

contain your page into ExtendedTextSelectionPointerHandler, so you can control toolbar and handle.

Default Behavior

set your page as child of ExtendedTextSelectionPointerHandler

 return ExtendedTextSelectionPointerHandler(
      //default behavior
       child: result,
  • tap region outside of extended text, hide toolbar and handle
  • scorll, hide toolbar and handle

Custom Behavior

get selectionStates(ExtendedTextSelectionState) by builder call back, and handle by your self.

 return ExtendedTextSelectionPointerHandler(
      //default behavior
      // child: result,
      //custom your behavior
      builder: (states) {
        return Listener(
          child: result,
          behavior: HitTestBehavior.translucent,
          onPointerDown: (value) {
            for (var state in states) {
              if (!state.containsPosition(value.position)) {
                //clear other selection
          onPointerMove: (value) {
            //clear other selection
            for (var state in states) {

Custom Background #

refer to issues 24335/24337 about background

          "This text has nice background with borderradius,no mattter how many line,it likes nice",
      background: Paint()..color = Colors.indigo,
      clipBorderRadius: BorderRadius.all(Radius.circular(3.0))),
backgroundBackground painter-
clipBorderRadiusClip BorderRadius-
paintBackgroundPaint background call back, you can paint background by self-

Custom Overflow #

refer to issue 26748

childThe widget of TextOverflow.@required
maxHeightThe maxHeight of [TextOverflowWidget], default is preferredLineHeight.preferredLineHeight
alignThe Align of [TextOverflowWidget], left/right.right
positionThe position which TextOverflowWidget should be shown.TextOverflowPosition.end
   overflowWidget: TextOverflowWidget(
     position: TextOverflowPosition.end,
     align: TextOverflowAlign.center,
     // just for debug
     debugOverflowRectColor: Colors.red.withOpacity(0.1),
     child: Container(
       child: Row(
         mainAxisSize: MainAxisSize.min,
         children: <Widget>[
           const Text('\u2026 '),
             child: const Text(
             onTap: () {

Join Zero-Width Space #

refer to issue 18761

if [ExtendedText.joinZeroWidthSpace] is true, it will join '\u{200B}' into text, make line breaking and overflow style better.

      joinZeroWidthSpace: true,

or you can convert by following method:

  1. String
  String input='abc'.joinChar();
  1. InlineSpan
     InlineSpan innerTextSpan;
     innerTextSpan = joinChar(

Take care of following things:

  1. the word is not a word, it will not working when you want to double tap to select a word.

  2. text is changed, if [ExtendedText.selectionEnabled] is true, you should override TextSelectionControls and remove zeroWidthSpace.

class MyTextSelectionControls extends TextSelectionControls {

  void handleCopy(TextSelectionDelegate delegate,
      ClipboardStatusNotifier? clipboardStatus) {
    final TextEditingValue value = delegate.textEditingValue;

    String data = value.selection.textInside(value.text);
    // remove zeroWidthSpace
    data = data.replaceAll(zeroWidthSpace, '');

      text: value.selection.textInside(value.text),
    delegate.textEditingValue = TextEditingValue(
      text: value.text,
      selection: TextSelection.collapsed(offset: value.selection.end),

pub points


verified publisher iconfluttercandies.com

Extended official text to build special text like inline image or @somebody quickly,it also support custom background,custom over flow and custom selection toolbar and handles.



API reference


Icon for licenses.MIT (LICENSE)


extended_text_library, flutter


Packages that depend on extended_text