flutter文本展开收起

文章介绍了一个在Flutter中自定义的ExpandableText类,该类扩展了StatefulWidget,用于创建一个可以展开和收缩的文本控件。它接受文本、最大行数、样式和是否展开等参数,内部使用TextPainter来检测文本是否超过最大行数。当文本超出限制时,提供‘查看全部’和‘收起’的选项,点击切换显示状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import 'package:atui/jade/utils/JadeColors.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
/*
* 展开、收起的文本
* */

class ExpandableText extends StatefulWidget {
  final String text;

  final int maxLines;

  final TextStyle style;

  final bool expand;

  final String expandText;

  final TextStyle expandTextStyle;

  final bool removeExpandAction;

  final bool keepShowExpandText;

  final paragraphsWidth;

  final Function expandCallback;

  const ExpandableText(
      {Key key,
      this.text,
      this.maxLines,
      this.style,
      this.expand,
      this.expandText,
      this.expandTextStyle,
      this.removeExpandAction,
      this.keepShowExpandText,
      this.paragraphsWidth,
      this.expandCallback})
      : super(key: key);

  
  State<StatefulWidget> createState() {
    return _ExpandableTextState(text, maxLines, style, expand, expandText,expandTextStyle,removeExpandAction,keepShowExpandText,paragraphsWidth,expandCallback);
  }
}

class _ExpandableTextState extends State<ExpandableText> {
  final String text;//文本内容
  final int maxLines;//最大行数
  final TextStyle style;//文本样式
  bool expand; //是否展开
  String expandText; //展开、收起按钮文本
  TextStyle expandTextStyle;//展开、收起按钮文本样式
  final bool removeExpandAction; //是否一处展开、收起按钮的点击响应
  final bool keepShowExpandText; //是否在未超过设置的最大行数时,显示拼接的展开、收起按钮
  final paragraphsWidth; //自然段样式时,句头占位的宽度
  final Function expandCallback; //点击扩展文字按钮回调出去

  _ExpandableTextState(this.text, this.maxLines, this.style, this.expand, this.expandText, this.expandTextStyle,
      this.removeExpandAction, this.keepShowExpandText, this.paragraphsWidth,this.expandCallback) {
    if (expand == null) {
      expand = false;
    }
  }

  
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, size) {
      final span = TextSpan(text: text ?? '', style: style);

      //通过TextPainter进行布局
      final tp = TextPainter(
          text: span, maxLines: maxLines, textDirection: TextDirection.ltr);
      tp.layout(maxWidth: size.maxWidth);

      //判断是否超过最大行数
      if (tp.didExceedMaxLines) {
        //获取最后一行的位置
        final position = tp.getPositionForOffset(Offset(
          size.maxWidth - tp.width / maxLines, //size.maxWidth最右,减去tp.width / maxLines为省略号和拼接的文本按钮预留距离
          tp.size.height,
        ));

         //final endOffset = tp.getOffsetBefore(position.offset);
        final endOffset = tp.getOffsetAfter(position.offset);
        return RichText(
            overflow: TextOverflow.clip,
          text: TextSpan(
            children: [
              WidgetSpan(child: SizedBox(width: paragraphsWidth ?? 0)),
              TextSpan(
                text: expand?text
                    : text.substring(0, endOffset)+'...',
                style: TextStyle(
                    fontSize: style != null ? style.fontSize : null,
                    color: style != null ? style.color : Colors.black,
                    shadows: style != null ? style.shadows : [Shadow(
                      blurRadius: 0.0,
                      color: Colors.black,
                      offset: Offset(0.0, 0.0),
                    )],
                  height: style.height != null ? style.height : 1.0
                ),
              ),
              TextSpan(
                text: expand ? '收起' : expandText??'查看全部',
                style: TextStyle(
                    fontSize: style != null ? style.fontSize : null,
                    color: expandTextStyle != null ? expandTextStyle.color : JadeColors.blue_2,
                  shadows: expandTextStyle != null ? expandTextStyle.shadows : [
                    Shadow(
                      blurRadius: 0.0,
                      color: JadeColors.blue_2,
                      offset: Offset(0.0, 0.0),
                    )
                  ],
                  decoration: TextDecoration.underline
                ),
                recognizer: TapGestureRecognizer()
                  ..onTap = () {
                    if(expandCallback != null){
                      expandCallback();
                    }
                    //是取消点击扩展文字按钮显示全文的响应操作
                    if(removeExpandAction == true){
                      return;
                    }
                    setState(() {
                      expand = !expand;
                    });
                  },
              )
            ])
        );
      } else {
        if(keepShowExpandText == true && text != null && text.isNotEmpty){
          return Text.rich(TextSpan(
              children: [
                TextSpan(
                    text: text,style: style
                ),
                TextSpan(
                    text: expandText,style: expandTextStyle
                )
              ]
          ));
        }
        return Text(text ??  '', style: style);
      }
    });
  }
}

使用

String _content = "这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容这里是文本内容";
ExpandableText(
  text: _content ,
  maxLines: 4,
  style:TextStyle(fontSize: setFontSize(30)),
  ),
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值