preface

Having nothing to do, I continue to learn. This time, I have realized a function of expanding and folding multi-line text. The relevant code has been uploaded to Github, and the component source code.

  • useLayoutBuilderTo implement control lazy loading
  • useTextPainterDetermines whether the text content exceeds the specified number of lines
    • More than
      • byTextPainterThe built-in method calculates how many lines of text to intercept before
      • Concatenate captured text with expand/display (concatenate with TextSpan)
    • If not, the original text is displayed

Implementation steps

Use LayoutBuilder to realize control lazy loading

return LayoutBuilder(builder: (context, constraints) {
}
Copy the code

Use TextPainter to determine if the text content exceeds the specified number of lines

TextPainter _textPainter = TextPainter( maxLines: widget.maxLines, textScaleFactor: MediaQuery.textScaleFactorOf(context), locale: Localizations.localeOf(context), textAlign: TextAlign.start, text: TextSpan( text: widget.text, style: widget.textStyle, ), textDirection: Directionality.of(context)) .. layout( minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);Copy the code

TextPainter’s built-in method calculates how many lines of text to intercept

final textSize = _textPainter.size; final position = _textPainter.getPositionForOffset(Offset( textSize.width - _textPainter.width, textSize.height, )); // Default endOffset = position. offset-1; But this can sometimes lead to two word wrap, so it will be a reduction of the 1 final endOffset = _textPainter. GetOffsetBefore (position. The offset - 1);Copy the code

Concatenate captured text with expand/display (concatenate with TextSpan)

Return RichText(overflow: textoverflow. clip, text: TextSpan(// cut 0-endoffset string, and expand/collapse text:! _isExpand ? widget.text : widget.text.substring(0, endOffset), style: TextStyle(color: Colors.black), children: [ TextSpan( text: _isExpand ? widget.shrinkText : widget.expandText, style: TextStyle(color: Colors.lightBlueAccent), recognizer: TapGestureRecognizer()..onTap = () {setState(() {_isExpand =!_isExpand;});})]);Copy the code

The complete component code is shown below

import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; class CommonRichText extends StatefulWidget { final String text; final int maxLines; final int minLines; final TextStyle? textStyle; final String shrinkText; final String expandText; final Function? onShrink; final Function? onExpand; CommonRichText({this.text = ", this.maxlines = 4, this.minlines = 1, this.textstyle, this.shrinkText = 'expand ', This.expandtext = 'shrink ', this.onexpand, this.onexpand}); _RichTextState createState() => _RichTextState(); } class _RichTextState extends State<CommonRichText> { bool _isExpand = true; @override void initState() { // TODO: implement initState super.initState(); } @override void didUpdateWidget(covariant CommonRichText oldWidget) { // TODO: implement didUpdateWidget super.didUpdateWidget(oldWidget); } @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { TextPainter _textPainter = TextPainter( maxLines: widget.maxLines, textScaleFactor: MediaQuery.textScaleFactorOf(context), locale: Localizations.localeOf(context), textAlign: TextAlign.start, text: TextSpan( text: widget.text, style: widget.textStyle, ), textDirection: Directionality.of(context)) .. layout( minWidth: constraints.minWidth, maxWidth: constraints.maxWidth); / / determine whether has exceeded maximum number of lines if (_textPainter. DidExceedMaxLines) {final textSize = _textPainter. Size; final position = _textPainter.getPositionForOffset(Offset( textSize.width - _textPainter.width, textSize.height, )); // Default endOffset = position. offset-1; But this can sometimes lead to two word wrap, so it will be a reduction of the 1 final endOffset = _textPainter. GetOffsetBefore (position. The offset - 1); Return RichText(overflow: textoverflow. clip, text: TextSpan(// cut 0-endoffset string, and expand/collapse text:! _isExpand ? widget.text : widget.text.substring(0, endOffset), style: TextStyle(color: Colors.black), children: [ TextSpan( text: _isExpand ? widget.shrinkText : widget.expandText, style: TextStyle(color: Colors.lightBlueAccent), recognizer: TapGestureRecognizer()..onTap = () {setState(() {_isExpand =!_isExpand;});})]); } else { return Text.rich(TextSpan(text: widget.text)); }}); }}Copy the code

The interface display is as follows