Code Monkey home page Code Monkey logo

extended_text's Introduction

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

ExtendedText is a third-party extension library for Flutter's official Text component. The main extended features are as follows:

Feature ExtendedText Text
Customized text overflow effects Supported, allows customizing the overflow widget and controlling overflow positions (before, middle, after) Not supported (26748,45336)
Copying the actual value of special text Supported, enables copying the actual value of the text, not just the placeholder value of WidgetSpan Can only copy the placeholder value of WidgetSpan (\uFFFC)
Quick construction of rich text based on text format Supported, enables quick construction of rich text based on text format Not supported

HarmonyOS is supported. Please use the latest version which contains ohos tag. You can check it in Versions tab.

dependencies:
  extended_text: 10.0.1-ohos

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;

  @override
  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) {
                  onTap(atText);
                }
              }))
        : SpecialTextSpan(
            text: atText,
            actualText: atText,
            start: start,
            style: textStyle,
            recognizer: (TapGestureRecognizer()
              ..onTap = () {
                if (onTap != null) {
                  onTap(atText);
                }
              }));
  }
}

SpecialTextSpanBuilder

create your SpecialTextSpanBuilder

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

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

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

  @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,
        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 {
  ImageSpan(
    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,
  })
parameter description default
image The image to display(ImageProvider). -
imageWidth The width of image(not include margin) required
imageHeight The height of image(not include margin) required
margin The margin of image -
actualText Actual text, take care of it when enable selection,something likes "[love]" '\uFFFC'
start Start index of text,take care of it when enable selection. 0

Selection

parameter description default
selectionEnabled Whether enable selection false
selectionColor Color of selection Theme.of(context).textSelectionColor
dragStartBehavior DragStartBehavior for text selection DragStartBehavior.start
textSelectionControls An interface for building the selection UI, to be provided by the implementor of the toolbar widget or handle widget extendedMaterialTextSelectionControls/extendedCupertinoTextSelectionControls

TextSelectionControls

override [SelectionArea.contextMenuBuilder] and [TextSelectionControls] to custom your toolbar widget or handle widget

const double _kHandleSize = 22.0;

/// Android Material styled text selection controls.

class MyTextSelectionControls extends TextSelectionControls
    with TextSelectionHandleControls {
  MyTextSelectionControls({this.joinZeroWidthSpace = false});
  final bool joinZeroWidthSpace;

  /// Returns the size of the Material handle.
  @override
  Size getHandleSize(double textLineHeight) =>
      const Size(_kHandleSize, _kHandleSize);

  /// Builder for material-style text selection handles.
  @override
  Widget buildHandle(
      BuildContext context, TextSelectionHandleType type, double textLineHeight,
      [VoidCallback? onTap, double? startGlyphHeight, double? endGlyphHeight]) {
    final Widget handle = SizedBox(
      width: _kHandleSize,
      height: _kHandleSize,
      child: Image.asset(
        'assets/40.png',
      ),
    );

    // [handle] is a circle, with a rectangle in the top left quadrant of that
    // circle (an onion pointing to 10:30). We rotate [handle] to point
    // straight up or up-right depending on the handle type.
    switch (type) {
      case TextSelectionHandleType.left: // points up-right
        return Transform.rotate(
          angle: math.pi / 4.0,
          child: handle,
        );
      case TextSelectionHandleType.right: // points up-left
        return Transform.rotate(
          angle: -math.pi / 4.0,
          child: handle,
        );
      case TextSelectionHandleType.collapsed: // points up
        return handle;
    }
  }

  /// Gets anchor for material-style text selection handles.
  ///
  /// See [TextSelectionControls.getHandleAnchor].
  @override
  Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight,
      [double? startGlyphHeight, double? endGlyphHeight]) {
    switch (type) {
      case TextSelectionHandleType.left:
        return const Offset(_kHandleSize, 0);
      case TextSelectionHandleType.right:
        return Offset.zero;
      default:
        return const Offset(_kHandleSize / 2, -4);
    }
  }
}

class CommonSelectionArea extends StatelessWidget {
  const CommonSelectionArea({
    super.key,
    required this.child,
    this.joinZeroWidthSpace = false,
  });
  final Widget child;
  final bool joinZeroWidthSpace;

  @override
  Widget build(BuildContext context) {
    SelectedContent? _selectedContent;
    return SelectionArea(
      contextMenuBuilder:
          (BuildContext context, SelectableRegionState selectableRegionState) {
        return AdaptiveTextSelectionToolbar.buttonItems(
          buttonItems: <ContextMenuButtonItem>[
            ContextMenuButtonItem(
              onPressed: () {
                // TODO(zmtzawqlp):  how to get Selectable
                // and  _clearSelection is not public
                // https://github.com/flutter/flutter/issues/126980

                //  onCopy: () {
                //   _copy();

                //   // In Android copy should clear the selection.
                //   switch (defaultTargetPlatform) {
                //     case TargetPlatform.android:
                //     case TargetPlatform.fuchsia:
                //       _clearSelection();
                //     case TargetPlatform.iOS:
                //       hideToolbar(false);
                //     case TargetPlatform.linux:
                //     case TargetPlatform.macOS:
                //     case TargetPlatform.windows:
                //       hideToolbar();
                //   }
                // },

                // if (_selectedContent != null) {
                //   String content = _selectedContent!.plainText;
                //   if (joinZeroWidthSpace) {
                //     content = content.replaceAll(zeroWidthSpace, '');
                //   }

                //   Clipboard.setData(ClipboardData(text: content));
                //   selectableRegionState.hideToolbar(true);
                //   selectableRegionState._clearSelection();
                // }

                selectableRegionState
                    .copySelection(SelectionChangedCause.toolbar);

                // remove zeroWidthSpace
                if (joinZeroWidthSpace) {
                  Clipboard.getData('text/plain').then((ClipboardData? value) {
                    if (value != null) {
                      // remove zeroWidthSpace
                      final String? plainText =
                          value.text?.replaceAll(ExtendedTextLibraryUtils.zeroWidthSpace, '');
                      if (plainText != null) {
                        Clipboard.setData(ClipboardData(text: plainText));
                      }
                    }
                  });
                }
              },
              type: ContextMenuButtonType.copy,
            ),
            ContextMenuButtonItem(
              onPressed: () {
                selectableRegionState.selectAll(SelectionChangedCause.toolbar);
              },
              type: ContextMenuButtonType.selectAll,
            ),
            ContextMenuButtonItem(
              onPressed: () {
                launchUrl(Uri.parse(
                    'mailto:[email protected]?subject=extended_text_share&body=${_selectedContent?.plainText}'));
                selectableRegionState.hideToolbar();
              },
              type: ContextMenuButtonType.custom,
              label: 'like',
            ),
          ],
          anchors: selectableRegionState.contextMenuAnchors,
        );
        // return AdaptiveTextSelectionToolbar.selectableRegion(
        //   selectableRegionState: selectableRegionState,
        // );
      },
      // magnifierConfiguration: TextMagnifierConfiguration(
      //   magnifierBuilder: (
      //     BuildContext context,
      //     MagnifierController controller,
      //     ValueNotifier<MagnifierInfo> magnifierInfo,
      //   ) {
      //     return TextMagnifier(
      //       magnifierInfo: magnifierInfo,
      //     );
      //     // switch (defaultTargetPlatform) {
      //     //   case TargetPlatform.iOS:
      //     //     return CupertinoTextMagnifier(
      //     //       controller: controller,
      //     //       magnifierInfo: magnifierInfo,
      //     //     );
      //     //   case TargetPlatform.android:
      //     //     return TextMagnifier(
      //     //       magnifierInfo: magnifierInfo,
      //     //     );
      //     //   case TargetPlatform.fuchsia:
      //     //   case TargetPlatform.linux:
      //     //   case TargetPlatform.macOS:
      //     //   case TargetPlatform.windows:
      //     //     return null;
      //     // }
      //   },
      // ),
      // selectionControls: MyTextSelectionControls(),
      onSelectionChanged: (SelectedContent? value) {
        print(value?.plainText);
        _selectedContent = value;
      },
      child: child,
    );
  }
}

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
                state.clearSelection();
              }
            }
          },
          onPointerMove: (value) {
            //clear other selection
            for (var state in states) {
              state.clearSelection();
            }
          },
        );
      },
    );

Custom Background

refer to issues 24335/24337 about background

  BackgroundTextSpan(
      text:
          "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))),
parameter description default
background Background painter -
clipBorderRadius Clip BorderRadius -
paintBackground Paint background call back, you can paint background by self -

Custom Overflow

refer to issue 26748

parameter description default
child The widget of TextOverflow. @required
maxHeight The maxHeight of [TextOverflowWidget], default is preferredLineHeight. preferredLineHeight
align The Align of [TextOverflowWidget], left/right. right
position The position which TextOverflowWidget should be shown. TextOverflowPosition.end
  ExtendedText(
   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 '),
           InkWell(
             child: const Text(
               'more',
             ),
             onTap: () {
               launch(
                   'https://github.com/fluttercandies/extended_text');
             },
           )
         ],
       ),
     ),
   ),
  )

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.

  ExtendedText(
      joinZeroWidthSpace: true,
    )

or you can convert by following method:

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

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 {

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

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

    Clipboard.setData(ClipboardData(
      text: value.selection.textInside(value.text),
    ));
    clipboardStatus?.update();
    delegate.textEditingValue = TextEditingValue(
      text: value.text,
      selection: TextSelection.collapsed(offset: value.selection.end),
    );
    delegate.bringIntoView(delegate.textEditingValue.selection.extent);
    delegate.hideToolbar();
  }
}

extended_text's People

Contributors

alexv525 avatar patrykpawlak avatar zmtzawqlp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

extended_text's Issues

[Feature] 能不能加入一个最简单的一行排满再换行的规则

使用Text(),或者ExtendedText()时,类似ADASDAS-123SFDASE1-SDA12EQW12这种字符串(常见于很长的编号),会在一行没有铺满时换行,实际上在中文语境并没有这种智能换行的需求。如下图效果
BMGKeg.png
虽然我可以自己一行一行绘制但太简陋了,而且项目里已经大量使用了ExtendedText,所以请问能作为一个功能添加吗,或者我可以结合ExtendedText实现这个效果吗该如何做。
因为我不了解ExtendedText的源代码所以只能提出这个问题不能给出具体的代码上的建议。
以下是我自己写的换行的函数,质量不是太好,按照最简单的思路写的。分行之后然后就用canvas一行一行画。

lineBreak ```dart

List _lineBreak(double width) {
List res = [];
ParagraphBuilder paragraphBuilder = ParagraphBuilder(ParagraphStyle());
ParagraphConstraints paragraphConstraints = ParagraphConstraints(width: width);
String tempStr = text;
while(tempStr.isNotEmpty){
paragraphBuilder.pushStyle(ui.TextStyle(
color: style.color,fontSize: style.fontSize,fontWeight: style.fontWeight,fontFamily: style.fontFamily));
double lastWidth = width; //一行的剩余宽度
StringBuffer tempBuffer = StringBuffer();
while(lastWidth >= 0){ //一个字一个字的循环,我也不懂有没有更好的办法
if(tempStr.isEmpty){
break;
}
String firstStr = tempStr.substring(0,1);
tempStr = tempStr.replaceFirst(RegExp(r'.'), '');
double firstStrWidth = calculateTextWidth(firstStr, width);
if(lastWidth > firstStrWidth){
tempBuffer.write(firstStr);
lastWidth -= firstStrWidth;
} else {
break;
}
}
paragraphBuilder.addText(tempBuffer.toString()); //得到一行
Paragraph paragraph = paragraphBuilder.build()..layout(paragraphConstraints);
res.add(paragraph);
paragraphBuilder.pop();
}
return res;
}

</details>

path_provider version update to latest version

Error when get packages.

Because every version of extended_text depends on path_provider ^0.4.1 and xxx depends on path_provider ^0.5.0+1, extended_text is forbidden.
So, because xxx depends on extended_text ^0.2.7, version solving failed.
pub get failed (1)

type 'List<InlineSpan>' is not a subtype of type 'List<TextSpan>'

extended_text版本:0.6.0
Flutter版本:1.7.8
重现步骤:列表中调用setState刷新
异常栈:

I/flutter ( 4742): #0      OverFlowTextSpan.== (package:extended_text/src/over_flow_text_span.dart:35:41)
I/flutter ( 4742): #1      ExtendedRenderParagraph.overFlowTextSpan= (package:extended_text/src/extended_render_paragraph.dart:116:15)
I/flutter ( 4742): #2      ExtendedRichText.updateRenderObject (package:extended_text/src/extended_rich_text.dart:167:9)
I/flutter ( 4742): #3      RenderObjectElement.update (package:flutter/src/widgets/framework.dart:4726:12)
I/flutter ( 4742): #4      MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5206:11)
I/flutter ( 4742): #5      Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #6      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #7      Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #8      StatelessElement.update (package:flutter/src/widgets/framework.dart:3981:5)
I/flutter ( 4742): #9      Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #10     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #11     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #12     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #13     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #14     StatelessElement.update (package:flutter/src/widgets/framework.dart:3981:5)
I/flutter ( 4742): #15     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #16     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4817:32)
I/flutter ( 4742): #17     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5208:17)
I/flutter ( 4742): #18     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #19     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #20     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #21     StatelessElement.update (package:flutter/src/widgets/framework.dart:3981:5)
I/flutter ( 4742): #22     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #23     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4817:32)
I/flutter ( 4742): #24     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5208:17)
I/flutter ( 4742): #25     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #26     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #27     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #28     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #29     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #30     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #31     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #32     StatefulElement.update (package:flutter/src/widgets/framework.dart:4085:5)
I/flutter ( 4742): #33     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #34     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #35     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #36     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #37     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #38     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #39     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #40     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #41     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #42     StatefulElement.update (package:flutter/src/widgets/framework.dart:4085:5)
I/flutter ( 4742): #43     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #44     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #45     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #46     StatelessElement.update (package:flutter/src/widgets/framework.dart:3981:5)
I/flutter ( 4742): #47     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #48     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #49     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #50     StatefulElement.update (package:flutter/src/widgets/framework.dart:4085:5)
I/flutter ( 4742): #51     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #52     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4817:32)
I/flutter ( 4742): #53     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5208:17)
I/flutter ( 4742): #54     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #55     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #56     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #57     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #58     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #59     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #60     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #61     StatelessElement.update (package:flutter/src/widgets/framework.dart:3981:5)
I/flutter ( 4742): #62     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #63     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #64     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #65     ProxyElement.update (package:flutter/src/widgets/framework.dart:4219:5)
I/flutter ( 4742): #66     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #67     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #68     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #69     StatefulElement.update (package:flutter/src/widgets/framework.dart:4085:5)
I/flutter ( 4742): #70     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #71     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #72     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #73     StatelessElement.update (package:flutter/src/widgets/framework.dart:3981:5)
I/flutter ( 4742): #74     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #75     SliverMultiBoxAdaptorElement.updateChild (package:flutter/src/widgets/sliver.dart:1181:36)
I/flutter ( 4742): #76     SliverMultiBoxAdaptorElement.performRebuild.processElement (package:flutter/src/widgets/sliver.dart:1113:34)
I/flutter ( 4742): #77     Iterable.forEach (dart:core/iterable.dart:277:30)
I/flutter ( 4742): #78     SliverMultiBoxAdaptorElement.performRebuild (package:flutter/src/widgets/sliver.dart:1139:24)
I/flutter ( 4742): #79     SliverMultiBoxAdaptorElement.update (package:flutter/src/widgets/sliver.dart:1084:7)
I/flutter ( 4742): #80     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #81     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #82     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #83     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4817:32)
I/flutter ( 4742): #84     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5208:17)
I/flutter ( 4742): #85     _ViewportElement.update (package:flutter/src/widgets/viewport.dart:192:11)
I/flutter ( 4742): #86     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #87     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #88     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #89     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #90     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #91     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #92     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #93     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #94     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #95     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #96     Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #97     StatefulElement.update (package:flutter/src/widgets/framework.dart:4085:5)
I/flutter ( 4742): #98     Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #99     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #100    Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #101    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #102    Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #103    ProxyElement.update (package:flutter/src/widgets/framework.dart:4219:5)
I/flutter ( 4742): #104    Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #105    SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5099:14)
I/flutter ( 4742): #106    Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #107    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #108    Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #109    StatefulElement.update (package:flutter/src/widgets/framework.dart:4085:5)
I/flutter ( 4742): #110    Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #111    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #112    Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #113    StatelessElement.update (package:flutter/src/widgets/framework.dart:3981:5)
I/flutter ( 4742): #114    Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #115    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #116    Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #117    StatefulElement.update (package:flutter/src/widgets/framework.dart:4085:5)
I/flutter ( 4742): #118    Element.updateChild (package:flutter/src/widgets/framework.dart:2876:15)
I/flutter ( 4742): #119    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3935:16)
I/flutter ( 4742): #120    Element.rebuild (package:flutter/src/widgets/framework.dart:3721:5)
I/flutter ( 4742): #121    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2340:33)
I/flutter ( 4742): #122    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:700:20)
I/flutter ( 4742): #123    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:285:5)
I/flutter ( 4742): #124    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1016:15)
I/flutter ( 4742): #125    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:958:9)
I/flutter ( 4742): #126    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:874:5)
I/flutter ( 4742): #127    _rootRun (dart:async/zone.dart:1124:13)
I/flutter ( 4742): #128    _CustomZone.run (dart:async/zone.dart:1021:19)
I/flutter ( 4742): #129    _CustomZone.runGuarded (dart:async/zone.dart:923:7)
I/flutter ( 4742): #130    _invoke (dart:ui/hooks.dart:236:10)
I/flutter ( 4742): #131    _drawFrame (dart:ui/hooks.dart:194:3)

Weird size with OverFlowTextSpan.

Behavior just like the screenshot. The OverFlowTextSpan was overflowed with abnormal size.
image

Demo is here:
SpecialText.dart:

import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:extended_text_library/extended_text_library.dart';

final double _fontSize = 16.0;
final double _fontSizeField = 18.0;

class LinkText extends SpecialText {
    static const String startKey = "https://wb.jmu.edu.cn/";
    static const String endKey = " ";

    LinkText(TextStyle textStyle, SpecialTextGestureTapCallback onTap) : super(startKey, endKey, textStyle, onTap: onTap);

    @override
    TextSpan finishText() {
        return TextSpan(
            text: " 网页链接 ",
            style: textStyle?.copyWith(color: Colors.blue, fontSize: _fontSize),
            recognizer: TapGestureRecognizer()
                ..onTap = () {
                    Map<String, dynamic> data = {'content': toString()};
                    if (onTap != null) onTap(data);
                },
        );
    }
}

class LinkOlderText extends SpecialText {
    static const String startKey = "http://wb.jmu.edu.cn/";
    static const String endKey = " ";

    LinkOlderText(TextStyle textStyle, SpecialTextGestureTapCallback onTap) : super(startKey, endKey, textStyle, onTap: onTap);

    @override
    TextSpan finishText() {
        return TextSpan(
            text: " 网页链接 ",
            style: textStyle?.copyWith(color: Colors.blue, fontSize: _fontSize),
            recognizer: TapGestureRecognizer()
                ..onTap = () {
                    Map<String, dynamic> data = {'content': toString()};
                    if (onTap != null) onTap(data);
                },
        );
    }
}

class MentionText extends SpecialText {
    static const String startKey = "<M";
    static const String endKey = "<\/M>";
    final RegExp mTagStartReg = RegExp(r"<M?\w+.*?\/?>");
    final RegExp mTagEndReg = RegExp(r"<\/M?\w+.*?\/?>");
    final int start;
    final BuilderType type;

    MentionText(TextStyle textStyle, SpecialTextGestureTapCallback onTap, {this.start, this.type})
            : super(startKey, endKey, textStyle, onTap: onTap);

    @override
    bool isEnd(String value) {
        return (getContent() + value).endsWith(endKey);
    }

    int getUidFromContent(content) {
        Iterable<Match> matches = mTagStartReg.allMatches(content);
        String result;
        for (Match m in matches) result = m.group(0);
        return int.parse(result.substring(3, result.length - 1));
    }

    String removeUidFromContent(content) {
        content = content.replaceAllMapped(mTagStartReg, (match) => "");
        content = content.replaceAllMapped(mTagEndReg, (match) => "");
        return content;
    }

    @override
    TextSpan finishText() {
        String mentionOriginalText = toString();
        String mentionText = removeUidFromContent(mentionOriginalText);
        mentionOriginalText = "${mentionOriginalText.substring(0, mentionOriginalText.length - MentionText.endKey.length)}>";

        if (type == BuilderType.extendedTextField) {
            print("mentionText: $mentionText");
            print("mentionOriginalText: $mentionOriginalText");
            return SpecialTextSpan(
                text: mentionText,
                actualText: mentionOriginalText,
                start: start,
                deleteAll: true,
                style: TextStyle(color: Colors.blue, fontSize: _fontSizeField),
            );
        } else {
            int uid = getUidFromContent(mentionOriginalText);
            return TextSpan(
                text: mentionText,
                style: TextStyle(color: Colors.blue, fontSize: _fontSize),
                recognizer: TapGestureRecognizer()
                    ..onTap = () {
                        Map<String, dynamic> data = {'content': mentionText, 'uid': uid};
                        if (onTap != null) onTap(data);
                    },
            );
        }
    }
}

class PoundText extends SpecialText {
    static const String flag = "#";
    final int start;
    final BuilderType type;
    PoundText(TextStyle textStyle, SpecialTextGestureTapCallback onTap, {this.start, this.type}) : super(flag, flag, textStyle, onTap: onTap);

    @override
    TextSpan finishText() {
        final String poundText = getContent();
        if (type == BuilderType.extendedTextField) {
            return SpecialTextSpan(
                text: "#$poundText#",
                actualText: "#$poundText#",
                start: start,
                deleteAll: true,
                style: TextStyle(color: Colors.orangeAccent, fontSize: _fontSizeField),
            );
        } else {
            return TextSpan(
                text: "#$poundText#",
                style: TextStyle(color: Colors.orangeAccent, fontSize: _fontSize),
                recognizer: TapGestureRecognizer()
                    ..onTap = () {
                        Map<String, dynamic> data = {'content': toString()};
                        if (onTap != null) onTap(data);
                    },
            );
        }
    }
}

class StackSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
    final BuilderType type;
    StackSpecialTextSpanBuilder({this.type: BuilderType.extendedText});

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

        if (isStart(flag, MentionText.startKey)) {
            return MentionText(textStyle, onTap, type: BuilderType.extendedText);
        } else if (isStart(flag, PoundText.flag)) {
            return PoundText(textStyle, onTap, type: BuilderType.extendedText);
        } else if (isStart(flag, LinkText.startKey)) {
            return LinkText(textStyle, onTap);
        } else if (isStart(flag, LinkOlderText.startKey)) {
            return LinkOlderText(textStyle, onTap);
        }
        return null;
    }
}

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

        if (isStart(flag, MentionText.startKey)) {
            return MentionText(textStyle, onTap,
                start: index - (MentionText.startKey.length - 1), // Using minus to keep position correct.
                type: BuilderType.extendedTextField,
            );
        } else if (isStart(flag, PoundText.flag)) {
            return PoundText(textStyle, onTap, start: index, type: BuilderType.extendedTextField);
        }
        return null;
    }
}
enum BuilderType { extendedText, extendedTextField }

main.dart:

import 'package:flutter/material.dart';

import 'package:extended_text/extended_text.dart';

import 'package:xxx/model/SpecialText.dart';

void main() {
    runApp(TestPage());
}

class TestPage extends StatefulWidget {
    @override
    _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
    bool isDetail;
    String testText = "<M 123456>@甭管是谁</M>: 天天要人!150-180元高薪充场移动兼职流量话费卡\n【充场内容】:此活动是苹果手机通讯店季度业绩冲量,需要大量人员,只需本人持身份证到现场免费领取5张移动流量卡,卡要回收,不接受勿扰!绝对安全可靠且放心(不使用三个月可以注销。没有黑名单,百度可以查),无需充值,无需交费!领完立马结20的路费,隔天过来收到多少张卡激活多少张,就按一张30来算,全程差不多10分钟就能做完,欢迎组队报名。\n【地点】:管你是哪\n【时间】:管你什么时候(任意时间段来都可以。10分钟即可)\n微信手机同号:管你什么电话号码\n🎤管你什么实习生宣讲会\n【宣讲会地点、时间】\n地点:管你什么破烂地方\n时间: 管你什么狗屁时间\n【交流群】\n请有兴趣的同学们加入海康威视&集美大学咨询QQ群:管你什么群号。\n【海康威视公司简介】\nl	“全球安防50强”第一位(a&s《安全自动化》,2016-2018)\nl	**电子科技集团公司下属最大上市企业,2010年5月上市(股票代码:002415)\nl “全球品牌500强”第216位(2018 Brand Finance榜单) \nl	**最佳雇主30强(智联招聘,2018)\nl	全球雇员超26000人\nl	业务覆盖全球150+国家及地区\nl	人工智能、云计算、大数据、物联网\n【海康威视福利】\n享受六险一金、节日津贴,生日福利,餐饮、交通、通讯、高温、医疗等补贴应有尽有,同时还有全行业有竞争力的薪酬,丰厚年终奖金,大规模股权激励与跟投,还有各色员工活动等你来体验······";

    @override
    void initState() {
        super.initState();
    }

    Widget getExtendedText(content, {isRoot}) {
        return ExtendedText(
            content != null ? "$content " : null,
            style: TextStyle(fontSize: 16.0),
            onSpecialTextTap: (dynamic data) {
                String text = data['content'];
                if (text.startsWith("#")) {
                    Scaffold.of(context).showSnackBar(
                        SnackBar(content: Text("#")),
                    );
                } else if (text.startsWith("@")) {
                    Scaffold.of(context).showSnackBar(
                        SnackBar(content: Text("@")),
                    );
                } else if (text.startsWith("http")) {
                    Scaffold.of(context).showSnackBar(
                        SnackBar(content: Text("link")),
                    );
                }
            },
            maxLines: this.isDetail ?? false ? null : 10,
            overFlowTextSpan: this.isDetail ?? false ? null : OverFlowTextSpan(
                text: "查看全文",
                style: TextStyle(
                    color: Color(0xFFE5322D),
                    fontSize: 16.0,
                ),
                background: isRoot ?? false ? Theme.of(context).canvasColor : Theme.of(context).cardColor
            ),
            specialTextSpanBuilder: StackSpecialTextSpanBuilder(),
        );
    }

    Widget card() {
        return Container(
            child: Card(
                margin: EdgeInsets.symmetric(vertical: 4.0),
                child: Stack(
                    children: <Widget>[
                        Column(
                            mainAxisSize: MainAxisSize.min,
                            children: <Widget>[
                                ListTile(
                                    leading: Container(
                                        width: 40.0,
                                        height: 40.0,
                                        child: CircleAvatar(
                                            backgroundImage: NetworkImage("http://oap99.jmu.edu.cn/face?uid=136172&size=f152"),
                                        ),
                                    ),
                                    title: Text("测试名称"),
                                    subtitle: Text("测试副标题"),
                                ),
                                Row(children: <Widget>[
                                    Expanded(
                                        child: Container(
                                            padding: const EdgeInsets.symmetric(horizontal: 16.0),
                                            child: Column(
                                                mainAxisAlignment: MainAxisAlignment.start,
                                                mainAxisSize: MainAxisSize.min,
                                                crossAxisAlignment: CrossAxisAlignment.start,
                                                children: <Widget>[
                                                    ExtendedText(
                                                        "$testText ",
                                                        style: TextStyle(fontSize: 16.0),
                                                        onSpecialTextTap: (dynamic data) {
                                                            String text = data['content'];
                                                            if (text.startsWith("#")) {
                                                                showDialog(context: context, builder: (_) => AlertDialog(
                                                                    title: Text("#"),
                                                                    content: Text(text),
                                                                ));
                                                            } else if (text.startsWith("@")) {
                                                                showDialog(context: context, builder: (_) => AlertDialog(
                                                                    title: Text("@"),
                                                                    content: Text(text),
                                                                ));
                                                            } else if (text.startsWith("http")) {
                                                                showDialog(context: context, builder: (_) => AlertDialog(
                                                                    title: Text("Link"),
                                                                    content: Text(text),
                                                                ));
                                                            }
                                                        },
                                                        maxLines: 10,
                                                        overFlowTextSpan: OverFlowTextSpan(
                                                                text: "查看全文",
                                                                style: TextStyle(
                                                                    color: Color(0xFFE5322D),
                                                                    fontSize: 16.0,
                                                                ),
                                                                background: Theme.of(context).cardColor
                                                        ),
                                                        specialTextSpanBuilder: StackSpecialTextSpanBuilder(),
                                                    )
                                                ],
                                            ),
                                        ),
                                    ),
                                ]),
                            ],
                        ),
                    ],
                ),
                elevation: 0,
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.zero),
            ),
        );
    }

    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            home: Scaffold(
                appBar: AppBar(
                    title: Text("反正是有问题的测试DEMO"),
                ),
                body: ListView(
                    children: <Widget>[
                        card(), card(),
                    ],
                ),
            ),
        );
    }
}

换行存在问题

存在的问题是一行空间还没有占满就换行了
这是布局代码
`///第一行
Widget oneRow() {
final List textSpanList = List();
final positionNameSpan = TextSpan(
text: "服务端C++架构师服务端C++架构师",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: ZPMColors.ZP_28,
),
);
textSpanList.add(positionNameSpan);

if (this.position.chatWindow > 0) {
  final youliaoSpan = ImageSpan(
    AssetImage(
        "lib/src/screens/positionGroup/resources/icon_pos_youliao.png"),
    imageWidth: 44,
    imageHeight: 19,
    fit: BoxFit.contain,
    margin: const EdgeInsets.fromLTRB(6, 2, 0, 2)
  );
  textSpanList.add(youliaoSpan);
}

if (this.position.positionSourceType == 2) {
  final netApplySpan = ImageSpan(
    AssetImage(
        "lib/src/screens/positionGroup/resources/icon_pos_netapply.png"),
    imageWidth: 26,
    imageHeight: 14,
    fit: BoxFit.contain,
    margin: const EdgeInsets.fromLTRB(6, 2, 0, 4)
  );
  textSpanList.add(netApplySpan);
}

return ExtendedText.rich(
  TextSpan(children: textSpanList),
  maxLines: 2,
  textAlign: TextAlign.left,
  overflow: ExtendedTextOverflow.ellipsis,
);

}`
屏幕快照 2019-06-18 下午5 30 52

是否支持长按开始选择文字?

selectionEnabled开启后,需要双击才能选择文字。
这个控件是否支持长按开始选择文字?长按可能更符合用户的操作习惯

关于富文本嵌套的问题

eg: #皮卡丘[emoji]#
请问关于这种嵌套的富文本,我应该如何处理,才可以即让“皮卡丘”文字变色,又可以让[emoji]显示表情?

Throwable: 'package:extended_text/src/extended_render_paragraph.dart': Failed assertion: line 810 pos 9: 'textPainter.width <= rect.width': is not true.

使用的版本:0.6.6

复现步骤:

换
换
换

使用部件显示上面的文本,然后限制3行,

ExtendedText(
              ““”换
换
换
”““,
              style: AppTextStyles.feedContentStyle,
              maxLines: 3,
              overflow: TextOverflow.ellipsis,
              overFlowTextSpan:
                  OverFlowTextSpan( children: [
                TextSpan(
                    text: '\u2026',
                    children: [
                      TextSpan(
                          text: '全文',
                          style: AppTextStyles.feedContentStyle
                              .copyWith(color: Colors.blue))
                    ],
                    style: AppTextStyles.feedContentStyle.copyWith(
                      fontSize: getSp(28)
                    )),
              ]),
            )

EndFlag 不支持多个字符

如果我需要把 {{@ 和 '}}' 作为 开始和结束符,现在则只会反最后一个字符当作识别符。

Test {{@extended_text}}  Custom SpecialText.

global position of special texts

Hi
ExtendedText is an awesome widget!
How can I find global position of special texts on screen.
I want to see if a link in text is visible and if yes where it is on page?

Long press on iOS for Extended Text Selection

Hi - is there a way to implement long press for the Selection Menu on iOS? Currently I have long press on Android but need to double click for iOS which users are not discovering.
Thanks for the great plugin by the way!

HTML 转 TEXT.RICH 无法实现选中

大神 你好,我现在又一个需求,就是后台传递过的是一段HTML标签,我通过其他轮子转成了Text.rich 但是怎么用上你的这个轮子呢? 望回复,拜托了!!

自定义的emoji,没有自动换行

如果一行中的末尾连续出现emoji, 自动换行失败。

text_issue

  1. extended_text: ^0.6.6
  2. flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, v1.7.8+hotfix.3, on Microsoft Windows [Version 10.0.17134.950], locale zh-CN)

[√] Android toolchain - develop for Android devices (Android SDK version 28.0.3)

[√] Android Studio (version 3.4)

[√] Connected device (1 available)                                                                                                                                                                                                                                  • No issues found!

iOS assertion: 'value.isFinite' with overflow text span

This case only happened on iOS.

tried to set a non-finite rect.
'package:flutter/src/semantics/semantics.dart':
Failed assertion: line 1175 pos 12: 'value.isFinite'


Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md

When the exception was thrown, this was the stack: 
#2      SemanticsNode.rect= (package:flutter/src/semantics/semantics.dart:1175:12)
#3      ExtendedRenderParagraph.assembleSemanticsNode (package:extended_text/src/extended_render_paragraph.dart:707:14)
...

Demo is here.

import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:extended_text/extended_text.dart';
import 'package:extended_text_library/extended_text_library.dart';

class TestPage extends StatefulWidget {
  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State<TestPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: ListView(
        children: <Widget>[
          for (int i = 0; i < 10; i++)
            ExtendedText(
              """#帮帮酱#今天又是充满活力的一天
失物招领&寻物启事
①水院丢失无印良品笔盒
②招领廖锦强校园卡<M 151314>@廖锦强</M>
③寻找眼镜 汉水306
④招领航海学院徐欣的规范汉字证书<M 180321>@徐欣</M>
⑤软件1991陈昊丢失校园卡
⑥古龙大礼堂对面自行车丢失
⑦寻找校园卡19级陈雨蝶<M 198738>@陈雨蝶</M>
⑧禹州408杯子失物招领
⑨文津101捡到beats耳机""",
              style: TextStyle(fontSize: 14.0),
              onSpecialTextTap: null,
              maxLines: 8,
              overFlowTextSpan: OverFlowTextSpan(
                children: <TextSpan>[
                  TextSpan(text: " ... "),
                  TextSpan(
                    text: "全文",
                    style: TextStyle(
                      color: Colors.red,
                    ),
                  ),
                ],
              ),
              specialTextSpanBuilder: StackSpecialTextSpanBuilder(),
            ),
        ],
      ),
    );
  }
}

class MentionText extends SpecialText {
  static const String startKey = "<M";
  static const String endKey = "<\/M>";
  final RegExp mTagStartReg = RegExp(r"<M?\w+.*?\/?>");
  final RegExp mTagEndReg = RegExp(r"<\/M?\w+.*?\/?>");
  final int start;
  final BuilderType type;

  MentionText(TextStyle textStyle, SpecialTextGestureTapCallback onTap,
      {this.start, this.type})
      : super(startKey, endKey, textStyle, onTap: onTap);

  @override
  bool isEnd(String value) {
    return (getContent() + value).endsWith(endKey);
  }

  int getUidFromContent(content) {
    Iterable<Match> matches = mTagStartReg.allMatches(content);
    String result;
    for (Match m in matches) result = m.group(0);
    return int.parse(result.substring(3, result.length - 1));
  }

  String removeUidFromContent(content) {
    content = content.replaceAllMapped(mTagStartReg, (match) => "");
    content = content.replaceAllMapped(mTagEndReg, (match) => "");
    return content;
  }

  @override
  InlineSpan finishText() {
    String mentionOriginalText = toString();
    String mentionText = removeUidFromContent(mentionOriginalText);
    mentionOriginalText =
    "${mentionOriginalText.substring(0, mentionOriginalText.length - MentionText.endKey.length)}>";

    if (type == BuilderType.extendedTextField) {
      return SpecialTextSpan(
        text: mentionText,
        actualText: mentionOriginalText,
        start: start,
        deleteAll: true,
        style: textStyle?.copyWith(color: Colors.blue),
      );
    } else {
      int uid = getUidFromContent(mentionOriginalText);
      return TextSpan(
        text: mentionText,
        style: textStyle?.copyWith(color: Colors.blue),
        recognizer: TapGestureRecognizer()
          ..onTap = () {
            Map<String, dynamic> data = {'content': mentionText, 'uid': uid};
            if (onTap != null) onTap(data);
          },
      );
    }
  }
}

class PoundText extends SpecialText {
  static const String flag = "#";
  final int start;
  final BuilderType type;
  PoundText(TextStyle textStyle, SpecialTextGestureTapCallback onTap,
      {this.start, this.type})
      : super(flag, flag, textStyle, onTap: onTap);

  @override
  InlineSpan finishText() {
    final String poundText = getContent();
    if (type == BuilderType.extendedTextField) {
      return SpecialTextSpan(
        text: "#$poundText#",
        actualText: "#$poundText#",
        start: start,
        deleteAll: false,
        style: textStyle?.copyWith(color: Colors.orangeAccent),
      );
    } else {
      return TextSpan(
        text: "#$poundText#",
        style: textStyle?.copyWith(color: Colors.orangeAccent),
        recognizer: TapGestureRecognizer()
          ..onTap = () {
            Map<String, dynamic> data = {'content': toString()};
            if (onTap != null) onTap(data);
          },
      );
    }
  }
}

class StackSpecialTextSpanBuilder extends SpecialTextSpanBuilder {
  final BuilderType type;
  StackSpecialTextSpanBuilder({this.type: BuilderType.extendedText});

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

    if (isStart(flag, MentionText.startKey)) {
      return MentionText(textStyle, onTap, type: BuilderType.extendedText);
    } else if (isStart(flag, PoundText.flag)) {
      return PoundText(textStyle, onTap, type: BuilderType.extendedText);
    }
    return null;
  }
}

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

    if (isStart(flag, MentionText.startKey)) {
      return MentionText(
        textStyle, onTap,
        start: index -
            (MentionText.startKey.length -
                1), // Using minus to keep position correct.
        type: BuilderType.extendedTextField,
      );
    } else if (isStart(flag, PoundText.flag)) {
      return PoundText(textStyle, onTap,
          start: index, type: BuilderType.extendedTextField);
    }
    return null;
  }
}

enum BuilderType { extendedText, extendedTextField }

selectionEnabled is not working on iOS, but working on Android

Am I missing something or selectionEnabled is not working on iOS:

ExtendedText("Hi tehere 1234 5 5525234 523 45 42 5 5 2 254 5",style:getTextStyle(),selectionEnabled: true,),

Selecting text has no effect and sometimes throws this error:

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building _OverlayEntry-[LabeledGlobalKey<_OverlayEntryState>#65417](dirty, dependencies: [MediaQuery, _LocalizationsScope-[GlobalKey#2396b]], state: _OverlayEntryState#9e58c):
The getter 'cutButtonLabel' was called on null.
Receiver: null
Tried calling: cutButtonLabel

The relevant error-causing widget was:
MaterialApp file:///Users/abd/Programming/gits/p1/lib/generals/general_widget.dart:54:12
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 ExtendedCupertinoTextSelectionControls.buildToolbar (package:extended_text_library/src/selection/cupertino_text_selection_controls.dart:387:44)
#2 ExtendedTextSelectionOverlay._buildToolbar (package:extended_text_library/src/selection/extended_text_selection_overlay.dart:329:34)
#3 _OverlayEntryState.build (package:flutter/src/widgets/overlay.dart:169:25)
#4 StatefulElement.build (package:flutter/src/widgets/framework.dart:4334:27)

this widget makes gestureDetector don't work

  1. this widget makes gestureDetector don't work. Like when I want to longPress on a selectable text by gestureDetector, it won't work.

Looking forward to you reply. Thanks in advance.

ExtendedText绑定onTap后,点击specialText也会触发onTap事件

flutter doctor --verbose

[✓] Flutter (Channel stable, v1.12.13+hotfix.7, on Mac OS X 10.15.3 19D76, locale zh-Hans-CN)
    • Flutter version 1.12.13+hotfix.7 at /Users/lfh/Library/Flutter
    • Framework revision 9f5ff2306b (4 周前), 2020-01-26 22:38:26 -0800
    • Engine revision a67792536c
    • Dart version 2.7.0

[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
    • Android SDK at /Users/lfh/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-29, build-tools 29.0.2
    • ANDROID_HOME = /Users/lfh/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.3.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.3.1, Build version 11C504
    • CocoaPods version 1.8.4

[✓] Android Studio (version 3.5)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 43.0.1
    • Dart plugin version 191.8593
    • Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)

[✓] VS Code (version 1.42.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.8.1

[✓] Connected device (1 available)
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)

• No issues found!
Process finished with exit code 0

代码:

...
SingleChildScrollView(
  child: Container(
    padding: EdgeInsets.all(8.0),
    child: ExtendedText(
      '测试文本,@张三,$李四',
      onSpecialTextTap: (dynamic parameter) {
        // 点击特殊文本,弹出bottom sheet
        print('special text tap');
      },
      specialTextSpanBuilder:
          ContentSpecialTextSpanBuilder(showBackground: true),
      selectionEnabled: true,
      textSelectionControls: _materialContextTextSelectionControls,
      onTap: () {
        // 显示工具栏
        print('tap');
      },
    ),
  ),
),
...

点击特殊文本的时候,两个tap都触发了:

I/flutter ( 6643): special text tap
I/flutter ( 6643): tap

请问:有办法能实现点击特殊文本只触发onSpecialTextTap事件吗?

Building AOT snapshot in release mode (ios-release)... Compiler message: lib/special_text/emoji_text.dart:22:14: Error: A value of type 'ImageSpan' can't be assigned to a variable of type 'TextSpan'.

  • 'ImageSpan' is from 'package:extended_text_library/src/image_span.dart'
    ('file://Development/flutter/.pub-cache/hosted/pub.dartlang.org/extended_text_library-0.
    4.2/lib/src/image_span.dart').

    • 'TextSpan' is from 'package:flutter/src/painting/text_span.dart'
      ('file:/Development/flutter/packages/flutter/lib/src/painting/text_span.dart').

    Try changing the type of the left hand side, or casting the right hand side to 'TextSpan'.

       return ImageSpan(AssetImage(EmojiUitl.instance.emojiMap[key]),
    

Click outside of extended text raises exception

The following assertion was thrown while handling a gesture:
Looking up a deactivated widget's ancestor is unsafe.

At this point the state of the widget's element tree is no longer stable.

To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.

When the exception was thrown, this was the stack:
#0 Element._debugCheckStateIsActiveForAncestorLookup. (package:flutter/src/widgets/framework.dart:3508:9)
#1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3522:6)
#2 Element.findAncestorStateOfType (package:flutter/src/widgets/framework.dart:3641:12)
#3 Overlay.of (package:flutter/src/widgets/overlay.dart:237:41)
#4 ExtendedTextSelectionOverlay.showToolbar (package:extended_text_library/src/selection/extended_text_selection_overlay.dart:193:13)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#af7cb
debugOwner: GestureDetector
state: ready
won arena
finalPosition: Offset(38.8, 23.6)
button: 1
sent tap down

I/flutter (10005): 'package:flutter/src/services/text_editing.dart': Failed assertion: line 24 pos 15: 'start != null I/flutter (10005): && start >= -1': is not true.

I/flutter (10005): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (10005): The following assertion was thrown during paint():
I/flutter (10005): 'package:flutter/src/services/text_editing.dart': Failed assertion: line 24 pos 15: 'start != null
I/flutter (10005): && start >= -1': is not true.
I/flutter (10005):
I/flutter (10005): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter (10005): more information in this error message to help you determine and fix the underlying cause.
I/flutter (10005): In either case, please report this assertion by filing a bug on GitHub:
I/flutter (10005): https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter (10005):
I/flutter (10005): When the exception was thrown, this was the stack:
I/flutter (10005): #2 new TextRange
package:flutter/…/services/text_editing.dart:24
I/flutter (10005): #3 new TextSelection
package:flutter/…/services/text_editing.dart:107
I/flutter (10005): #4 ExtendedRenderEditable._getCaretOffset
package:extended_text_field/src/extended_render_editable.dart:1720
I/flutter (10005): #5 ExtendedRenderEditable._updateSelectionExtentsVisibility
package:extended_text_field/src/extended_render_editable.dart:284
I/flutter (10005): #6 ExtendedRenderEditable._paintContents
package:extended_text_field/src/extended_render_editable.dart:1884
I/flutter (10005): #7 ExtendedRenderEditable.paint
package:extended_text_field/src/extended_render_editable.dart:1968
I/flutter (10005): #8 RenderObject._paintWithContext
package:flutter/…/rendering/object.dart:2089
I/flutter (10005): #9 PaintingContext.paintChild
package:flutter/…/rendering/object.dart:172
I/flutter (10005): #10 _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint
package:flutter/…/rendering/proxy_box.dart:123
I/flutter (10005): #11 RenderObject._paintWithContext
package:flutter/…/rendering/object.dart:2089
I/flutter (10005): #12 PaintingContext.paintChild
package:flutter/…/rendering/object.dart:172
I/flutter (10005): #13 _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint
package:flutter/…/rendering/proxy_box.dart:123
I/flutter (10005): #14 PaintingContext.pushLayer
package:flutter/…/rendering/object.dart:369
I/flutter (10005): #15 RenderLeaderLayer.paint
package:flutter/…/rendering/proxy_box.dart:4624
I/flutter (10005): #16 RenderObject._paintWithContext
package:flutter/…/rendering/object.dart:2089
I/flutter (10005): #17 PaintingContext.paintChild
package:flutter/…/rendering/object.dart:172
I/flutter (10005): #18 _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint
package:flutter/…/rendering/proxy_box.dart:123
I/flutter (10005): #19 RenderObject._paintWithContext
package:flutter/…/rendering/object.dart:2089
I/flutter (10005): #20 PaintingContext.paintChild
package:flutter/…/rendering/object.dart:172
I/flutter (10005): #21 _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint
package:flutter/…/rendering/proxy_box.dart:123
I/flutter (10005): #22 RenderObject._paintWithContext
package:flutter/…/rendering/object.dart:2089
I/flutter (10005): #23 PaintingContext.paintChild
package:flutter/…/rendering/object.dart:172
I/flutter (10005): #24 _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint
package:flutter/…/rendering/proxy_box.dart:123

can't reponse tap gesture

@override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(10),
      padding: EdgeInsets.all(10),
      color: Colors.white,
      child: StreamBuilder<bool>(
        stream: _cellBloc.isSpread,
        initialData: false,
        builder: (context, snapshot) {
          return snapshot.data ? buildSpreadCell() : buildFoldCell();
        },
      ),
    );
  }

  Widget buildFoldCell() {
    return ExtendedText(
      's我觉得搜jog就搜集公司偶的机构就搜到结构将搜集的工具OS 是的解耦就搜集滚动事件酸豆角构建搜的感觉是家大公鸡搜到过建瓯市就酸豆角构建搜见到过',
      style: TextStyle(fontSize: 16),
      maxLines: 2,
      overFlowTextSpan: OverFlowTextSpan(
        children: [
          TextSpan(
              text: "\u2026 展开",
              style: TextStyle(color: Color(0xff9c9c9c), fontSize: 14),
              recognizer: TapGestureRecognizer()
                ..onTap = () {
                  debugPrint('点击');
                  _mainBloc.addToFolds(widget.data);
                })
        ],
        background: Colors.white,
      ),
    );
  }
 ....

when i tap the textspan, the console log

flutter: Another exception was thrown: NoSuchMethodError: The getter 'dx' was called on null.

使用新版本的包,在rich里放置widgetSpan和overflowWidget导致显示异常

在0.7.1的版本下是可以使用的
但更新到了4.0之后显示出现了异常

异常图片

代码段如下

Scaffold(
    appBar: AppBar(title: Text('widget.title')),
      body: ExtendedText.rich(
        TextSpan(
          children: [
            for (var item in List.generate(100, (index) => index))
              WidgetSpan(
                child: Container(
                  child: Text("汉字              "),
                  color: Colors.grey,
                ),
              ),
          ],
        ),
        maxLines: 1,
        overflow: TextOverflow.visible,
        // overflowWidget: TextOverflowWidget(child: Text('查看更多')),
      ),
    );

Text failed to display when switch between brightness

Step to Reproduce

Place a ExtendedText with some content, then switch between brightness, the error will occurred.

Minimum code to reproduce

import 'package:flutter/material.dart';
import 'package:extended_text/extended_text.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool darkMode = false;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        brightness: darkMode ? Brightness.dark : Brightness.light,
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ExtendedText("This is just a testing paragraph..."),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.brightness_4),
          onPressed: () {
            setState(() {
              darkMode = !darkMode;
            });
          },
        ),
      ),
    );
  }
}

Logs

Error Log

════════ Exception caught by rendering library ═════════════════════════════════════════════════════
The following assertion was thrown during paint():
'package:flutter/src/painting/text_painter.dart': Failed assertion: line 768 pos 12: '!_needsLayout': is not true.


Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md

The relevant error-causing widget was: 
  ExtendedText file:///Users/alex/Documents/FlutterProjects/flutter_app/lib/main.dart:26:13
When the exception was thrown, this was the stack: 
#2      TextPainter._computeCaretMetrics (package:flutter/src/painting/text_painter.dart:768:12)
#3      TextPainter.getOffsetForCaret (package:flutter/src/painting/text_painter.dart:742:5)
#4      ExtendedRenderParagraph.getOffsetForCaret (package:extended_text/src/extended_render_paragraph.dart:514:25)
#5      ExtendedRenderParagraph._paintSpecialTextChildren (package:extended_text/src/extended_render_paragraph.dart:758:30)
#6      ExtendedRenderParagraph._paintSpecialText (package:extended_text/src/extended_render_paragraph.dart:750:5)
...
The following RenderObject was being processed when the exception was fired: ExtendedRenderParagraph#e6b66 relayoutBoundary=up2
...  parentData: offset=Offset(0.0, 399.0); flex=null; fit=null (can use size)
...  constraints: BoxConstraints(0.0<=w<=375.0, 0.0<=h<=Infinity)
...  semantics node: SemanticsNode#3
...    Rect.fromLTRB(0.0, 399.0, 179.0, 413.0)
...    label: "This is just a testing paragraph..."
...    textDirection: ltr
...  size: Size(179.0, 14.0)
...  textAlign: start
...  textDirection: ltr
...  softWrap: wrapping at box width
...  overflow: clip
...  textScaleFactor: 0.8
...  locale: en_US
...  maxLines: unlimited
  text: TextSpan
    debugLabel: lerp((englishLike body1 2014).merge(whiteCupertino body1) ⎯0.1→ (lerp(englishLike body1 20140.1→ englishLike body1 2014)).merge(lerp(whiteCupertino body1 ⎯0.1→ blackCupertino body1)))
    inherit: false
    color: Color(0xfefdfdfd)
    family: .SF UI Text
    size: 14.0
    weight: 400
    baseline: alphabetic
    decoration: TextDecoration.none
    "This is just a testing paragraph..."
RenderObject: ExtendedRenderParagraph#e6b66 relayoutBoundary=up2
  parentData: offset=Offset(0.0, 399.0); flex=null; fit=null (can use size)
  constraints: BoxConstraints(0.0<=w<=375.0, 0.0<=h<=Infinity)
  semantics node: SemanticsNode#3
    Rect.fromLTRB(0.0, 399.0, 179.0, 413.0)
    label: "This is just a testing paragraph..."
    textDirection: ltr
  size: Size(179.0, 14.0)
  textAlign: start
  textDirection: ltr
  softWrap: wrapping at box width
  overflow: clip
  textScaleFactor: 0.8
  locale: en_US
  maxLines: unlimited
  text: TextSpan
    debugLabel: lerp((englishLike body1 2014).merge(whiteCupertino body1) ⎯0.1→ (lerp(englishLike body1 20140.1→ englishLike body1 2014)).merge(lerp(whiteCupertino body1 ⎯0.1→ blackCupertino body1)))
    inherit: false
    color: Color(0xfefdfdfd)
    family: .SF UI Text
    size: 14.0
    weight: 400
    baseline: alphabetic
    decoration: TextDecoration.none
    "This is just a testing paragraph..."
════════════════════════════════════════════════════════════════════════════════════════════════════

flutter doctor

[✓] Flutter (Channel beta, v1.12.13+hotfix.7-pre.1, on Mac OS X 10.15.1 19B88, locale zh-Hans-CN)

ExtendedText长按选择问题

Android手机上设置ThemeData的platform为iOS,则长按选择无法生效

app.dart

class App extends StatelessWidget {
  const App({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      onGenerateRoute: AppRouter.onGenerateRoute,
      onUnknownRoute: AppRouter.onUnknownRoute,
      builder: (BuildContext context, Widget child) {
        /// 禁用系统字体控制
        return MediaQuery(
          data: MediaQuery.of(context).copyWith(
            textScaleFactor: 1.0,
            boldText: false,
          ),
          child: child,
        );
      },
      onGenerateTitle: (BuildContext context) {
        return 'RouterKit';
      },
      theme: ThemeData.light().copyWith(
        platform: TargetPlatform.android,
      ),
    );
  }
}

demo.dart

                         Container(
                            alignment: Alignment.center,
                            padding: typeset.padding,
                            child: ExtendedText.rich(
                              TextSpan(
                                children: <InlineSpan>[
                                  TextSpan(text: model.article?.title ?? ''),
                                  TextSpan(text: model.article?.title ?? ''),
                                  TextSpan(text: model.article?.title ?? ''),
                                  TextSpan(text: model.article?.title ?? ''),
                                  TextSpan(text: model.article?.title ?? ''),
                                  TextSpan(text: model.article?.title ?? ''),
                                  TextSpan(text: model.article?.title ?? ''),
//                                  WidgetSpan(
//                                    child: Container(
//                                      width: 100,
//                                      height: 100,
//                                      color: Colors.red,
//                                    ),
//                                  ),
                                ],
                              ),
                              style: typeset.resolveTextStyle(zhHansCN),
                              strutStyle: typeset.strutStyle,
                              textAlign: typeset.textAlign,
                              textDirection: typeset.textDirection,
                              textScaleFactor: typeset.textScaleFactor,
                              locale: zhHansCN,
                              selectionEnabled: true,
                              textSelectionControls: extendedCupertinoTextSelectionControls,
//                              textSelectionControls: extendedMaterialTextSelectionControls,
                            ),
                          );

Failed assertion: 'textPainter.width >= lastChild.size.width': is not true.

I have been getting the following error recently, but it is happening only if the content is more than the maxLines and the last line's text is more than certain character count. Am on extended_text version 4.0.0
Please help me figure out a solution for this. Not sure what am doing wrong. Thanks in advance!

Another exception was thrown: 'package:extended_text/src/extended_render_paragraph.dart': Failed assertion: line 946 pos 14: 'textPainter.width >= lastChild.size.width': is not true.

Am using a package for some highlighting.
The following is my widget code:

final content = ParsedText(
  alignment: TextAlign.start,
  text: contentText,
  style: contentStyle,
  overflow: TextOverflow.ellipsis,
  maxLines: 3
  parse: [
    MatchText(
      type: ParsedType.CUSTOM,
      pattern: Hashtag.REGEX,
      style: TextStyle(color: Colors.blueAccent),
      onTap: null,
    ),
    MatchText(
      type: ParsedType.URL,
      style: TextStyle(color: Colors.blueAccent),
      onTap: null,
    ),
  ],
  overflowWidget: TextOverflowWidget(
    child: RichText(
      text: TextSpan(
        text: ' \u2026',
        children: <TextSpan>[
          TextSpan(
            text: "see more",
            recognizer: TapGestureRecognizer()..onTap = null,
          ),
        ],
        style: TextStyle(
          color: Colors.blueAccent,
          fontSize: 12.0,
        ),
      ),
    ),
  ),
);
flutter doctor -v
[✓] Flutter (Channel stable, 1.20.2, on Mac OS X 10.15.6 19G2021, locale en-IN)
    • Flutter version 1.20.2 at /Users/dheeraj2dj/flutter
    • Framework revision bbfbf1770c (2 weeks ago), 2020-08-13 08:33:09 -0700
    • Engine revision 9d5b21729f
    • Dart version 2.9.1


[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
    • Android SDK at /Users/dheeraj2dj/Library/Android/sdk
    • Platform android-30, build-tools 29.0.2
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.6, Build version 11E708
    • CocoaPods version 1.9.3

[✓] Android Studio (version 4.0)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 48.1.2
    • Dart plugin version 193.7361
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)

[!] IntelliJ IDEA Community Edition (version 2020.2.1)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • For information about installing plugins, see
      https://flutter.dev/intellij-setup/#installing-the-plugins

[✓] VS Code (version 1.36.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.4.1

[✓] Connected device (1 available)
    • xxxxxxxxxxxxxxx

! Doctor found issues in 1 category.

Get selected text

I am using extended_text, it's marvelous!
How can I get selected text and copy it into a string, I need to search a document for selected text.

ExtendedText加了GlobalKey后无法选中内容,望大佬解决

GlobalKey repaintKey = GlobalKey();
Container(
key: repaintKey, // 如果加入key,就无法选择文本
width: 280,
height: 350,
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 20),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.red, width: 2.0),
),
child:ExtendedText(
'abcdefg',
selectionEnabled: true,
onTap: () {
print(1111);
},
),
)

如果text的文本是带回车换行的字符串,大概率会出现报错

如果后台放回的content的格式是如下这种
image

ExtendedText(
                      content,
                      maxLines: 3,
                      style: TvStyle.tv_size_28_color_333333,
                      overFlowTextSpan: OverFlowTextSpan(
                        children: <TextSpan>[
                          TextSpan(text: '  \u2026  '),
                          TextSpan(
                            text: "全文",
                            style: TvStyle.tv_size_28_color_5765AD,
                          )
                        ],
                        background: Theme.of(context).canvasColor,
                      ),
                    ),

报错信息:

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.