extended textRelated articles

  • Flutter RichText supports image display and custom image effects
  • Flutter RichText supports custom text overflow effects
  • Flutter RichText supports custom text backgrounds
  • Flutter RichText supports special text effects
  • Flutter RichText supports text selection

I’ve introduced that beforeExtended Text’s image capabilitiesToday, we are going to talk about product design

Product said, that text overflow dot dot after I add a chicken leg, think what ah, is to add a “full text”, click after jump to the full text.

Like this one down here

First, I looked at the source of Text and found this… It’s written to death and passed to TextPainter

const String _kEllipsis = '\u2026';
Copy the code

Then look inside, is the engine painting code. I can’t see it. I was too weak. Searched on Google next, discovery also has to ask this question 26748, above also said. You need to copy the source code and change _kEllipsis to what you want, but.. This is a string. What about the blue, for example? What about clicks?

After a lot of thought, a word is painting, painting freely on the Canvas.

First, I define a TextSpan for user-defined text overflow effects

 overFlowTextSpan: OverFlowTextSpan(children: <TextSpan>[
                      TextSpan(text: ' \u2026 '),
                      TextSpan(
                          text: "more detail", style: TextStyle( color: Colors.blue, ), recognizer: TapGestureRecognizer() .. onTap = () { launch("https://github.com/fluttercandies/extended_text");
                            })
                    ], background: Theme.of(context).canvasColor),
Copy the code

Then we go straight to the paint method of the ExtendedRenderParagraph

@override
  void paint(PaintingContext context, Offset offset) {
    _paintSpecialText(context, offset);
    _paint(context, offset);
    _paintTextOverflow(context, offset);
  }
Copy the code

This effect definitely needs to be painted after the word, and then magic change

void _paintTextOverflow(PaintingContext context, Offset offset) {
    if(_hasVisualOverflow && overFlowTextSpan ! =null) {
      final Canvas canvas = context.canvas;

      ///We will move the canvas, so rect top left should be (0,0)
      final Rect rect = Offset(0.0.0.0) & size;
      var textPainter = overFlowTextSpan.layout(_textPainter);
      assert(textPainter.width <= rect.width,);
     
  }
Copy the code

First of all, we need to layout our overFlowTextSpan. If your definition is too long, it has exceeded one line, so sorry

The old action moves the canvas to the upper left corner of the entire text and calculates the nearest TextPosition based on the upper left corner of the overFlowTextSpanOffset.

      canvas.save();

      ///move to extended text
      canvas.translate(offset.dx, offset.dy);

      final Offset overFlowTextSpanOffset = Offset(
          rect.width - textPainter.width, rect.height - textPainter.height);

      ///find TextPosition near overflow
      TextPosition overflowOffset =
          getPositionForOffset(overFlowTextSpanOffset);
Copy the code

Then use this to find the top-left of the most recent text, so that you don’t cut half or incomplete text.

 ///find overflow TextPosition that not clip the original text
      Offset finalOverflowOffset = _findFinalOverflowOffset(
          rect, rect.width - textPainter.width, overflowOffset.offset);

 Offset _findFinalOverflowOffset(Rect rect, double x, int endTextOffset) {
    Offset endOffset = getOffsetForCaret(
      TextPosition(offset: endTextOffset, affinity: TextAffinity.upstream),
      rect,
    );
    //overflow
    if (endOffset == null|| (endTextOffset ! =0 && endOffset == Offset.zero)) {
      return _findFinalOverflowOffset(rect, x, endTextOffset - 1);
    }

    if (endOffset.dx > x) {
      return _findFinalOverflowOffset(rect, x, endTextOffset - 1);
    }
    return endOffset;
  } 
Copy the code

In this way, we can figure out where we need to draw the OverFlowTextSpan text and find a way to clear or block the text below the OverFlowTextSpan.

The first attempt was to use blendmode. clear to clear the text in the specified area, but failed. I don’t know why, I see others write like this, can clear the content on the Canvas, if any brothers know, please do tell me, thank you very much.

      ///why BlendMode.clear not clear the text
// canvas.saveLayer(overFlowTextSpanRect, Paint());
// canvas.drawRect(
// overFlowTextSpanRect,
// Paint()
/ /.. blendMode = BlendMode.clear);
// canvas.restore();
Copy the code

So you can only draw a Canvas like color to cover the text. The default is

Theme.of(context).canvasColor
Copy the code

And then we’ll draw the OverFlowTextSpan

 textPainter.paint(
          canvas, Offset(finalOverflowOffset.dx, overFlowTextSpanOffset.dy));
Copy the code

And finally, we’re going to handle the click event, save the point that the textPainter drew (relative to the overall system coordinates)

overFlowTextSpan.textPainterHelper.saveOffset(Offset(
          offset.dx + finalOverflowOffset.dx,
          offset.dy + overFlowTextSpanOffset.dy));
Copy the code

In the handleEvent method, we add the following code that, if we find a corresponding TextSpan registered with the recognizer, we fire it and return (because the overFlowTextSpan is one layer above the original word).

if(overFlowTextSpan ! =null) {
      final TextPosition position =
          overFlowTextSpan.textPainterHelper.getPositionForOffset(offset);
      final TextSpan span =
          overFlowTextSpan.textPainterHelper.getSpanForPosition(position);

      if(span? .recognizer ! =null) {
        span.recognizer.addPointer(event);
        return; }}Copy the code

_offset is the point we just kept relative to the whole system. We need to subtract _offset from the passed Offset so that the overFlowTextSpan starts with (0,0) relative to itself. Finally, use this TextPosition to find the corresponding TextSpan, and you’re done.

 ///method for [OverFlowTextSpan]
  ///offset int coordinate system
  Offset _offset;
  void saveOffset(Offset offset) {
    _offset = offset;
  }

  ///method for [OverFlowTextSpan]
  TextPosition getPositionForOffset(Offset offset) {
    return painter.getPositionForOffset(offset - _offset);
  }

  ///method for [OverFlowTextSpan]
  TextSpan getSpanForPosition(TextPosition position) {
    return painter.text.getSpanForPosition(position);
  }
Copy the code

With the exception of clearing (overwriting) the Text, this is probably the perfect solution, and I look forward to seeing more ideas on how to improve Extended Text

Finally put onExtended_TextIf there’s something you don’t understand, please let me know. Welcome aboardFlutter CandiesTogether to make cute little Flutter candiesQQ group: 181398081