美文网首页
Flutter 重复造轮子 (9) Ellipsis 省略文字组

Flutter 重复造轮子 (9) Ellipsis 省略文字组

作者: 半城半离人 | 来源:发表于2023-09-22 08:48 被阅读0次

    详细可以访问仓库 HcUi: 重复创造Flutter 的轮子 在原有组件上拓展 展现出新的特性 (gitee.com)

    介绍

    对长文本进行省略展示,支持展开/收起,对省略位置可以自定义


    c2b2bc33-ed77-4ad3-b50d-51d003bf5c52.gif

    代码演示

    基础用法

          HcTextEllipsis(
                content:"生态文明建设是关系中华民族永续发展的根本大计。总书记指出,要把建设美丽中国摆在强国建设、民族复兴的突出位置,以高品质生态环境支撑高质量发展,加快推进人与自然和谐共生的现代化。各地坚持以习近平生态文明思想为指引,牢固树立和践行绿水青山就是金山银山的理念,以更高站位、更宽视野、更大力度,全面推进美丽中国建设。"
              ),
    

    最大显示行数

              HcTextEllipsis(
                maxLines: 2,
                content:"生态文明建设是关系中华民族永续发展的根本大计。总书记指出,要把建设美丽中国摆在强国建设、民族复兴的突出位置,以高品质生态环境支撑高质量发展,加快推进人与自然和谐共生的现代化。各地坚持以习近平生态文明思想为指引,牢固树立和践行绿水青山就是金山银山的理念,以更高站位、更宽视野、更大力度,全面推进美丽中国建设。"
              ),
    

    省略符位置

     HcTextEllipsis(
                maxLines: 2,
                position: HcTextEllipsisPosition.center,
                content:"生态文明建设是关系中华民族永续发展的根本大计。总书记指出,要把建设美丽中国摆在强国建设、民族复兴的突出位置,以高品质生态环境支撑高质量发展,加快推进人与自然和谐共生的现代化。各地坚持以习近平生态文明思想为指引,牢固树立和践行绿水青山就是金山银山的理念,以更高站位、更宽视野、更大力度,全面推进美丽中国建设。"
              ),
    

    替换省略符

              HcTextEllipsis(
                maxLines: 2,
                ellipsisDots: "~~~",
                position: HcTextEllipsisPosition.center,
                content:"生态文明建设是关系中华民族永续发展的根本大计。总书记指出,要把建设美丽中国摆在强国建设、民族复兴的突出位置,以高品质生态环境支撑高质量发展,加快推进人与自然和谐共生的现代化。各地坚持以习近平生态文明思想为指引,牢固树立和践行绿水青山就是金山银山的理念,以更高站位、更宽视野、更大力度,全面推进美丽中国建设。"
              ),
    

    展开按钮单独显示

              HcTextEllipsis(
                maxLines: 2,
                ellipsisDots: "~~~",
                separateShow: false,
                position: HcTextEllipsisPosition.center,
                content:"生态文明建设是关系中华民族永续发展的根本大计。总书记指出,要把建设美丽中国摆在强国建设、民族复兴的突出位置,以高品质生态环境支撑高质量发展,加快推进人与自然和谐共生的现代化。各地坚持以习近平生态文明思想为指引,牢固树立和践行绿水青山就是金山银山的理念,以更高站位、更宽视野、更大力度,全面推进美丽中国建设。"
              ),
    

    点击按钮的回调

           HcTextEllipsis(
                  maxLines: 5,
                  ellipsisDots: "...",
                  separateShow: false,
                  showEllipsisBtn: true,
                  position: HcTextEllipsisPosition.center,
                  callback: (bool state) {
                    //设定回调后手动控制是否展开 返回true为展开 false为不展开
                    return Future.value(!state);
                  },
                  content:
                      "生态文明建设是关系中华民族永续发展的根本大计。总书记指出,要把建设美丽中国摆在强国建设、民族复兴的突出位置,以高品质生态环境支撑高质量发展,加快推进人与自然和谐共生的现代化。各地坚持以习近平生态文明思想为指引,牢固树立和践行绿水青山就是金山银山的理念,以更高站位、更宽视野、更大力度,全面推进美丽中国建设。"),
    

    API

    props

    参数 说明 类型 默认值 是否必填
    maxLines 需要展示的行数 int 1 true
    content 文字内容 String - true
    expandText 展开按钮的文字内容 String 展开 true
    collapseText 收起按钮的文字内容 String 收起 true
    ellipsisDots 省略符 String ... true
    position 分隔符的位置 HcTextEllipsisPosition end true
    isExpand 默认是否展开 bool false true
    showEllipsisBtn 是否展示更多的按钮 bool false true
    separateShow 按钮是否单独展示 bool false true
    contentStyle 文字的字体样式 TextStyle? - false
    btnTextStyle 按钮的文字样式 TextStyle? - false
    callback 点击的时候回调 HcTextEllipsisCallback - false

    HcTextEllipsisPosition

    展示省略符号位置

    参数名 说明
    start 开头
    center 中部
    end 结尾

    Function

    方法名 说明 参数 返回类型
    HcTextEllipsisCallback 点击按钮后的回调 Function(bool) Future<bool?>

    项目源码

    enum HcTextEllipsisPosition { start, center, end }
    
    typedef HcTextEllipsisCallback = Future<bool?> Function(bool);
    
    class HcTextEllipsis extends StatefulWidget {
      //需要展示的行数
      final int maxLines;
    
      //文字内容
      final String content;
    
      //展开的文字
      final String expandText;
    
      //折叠的文字
      final String collapseText;
    
      ///分隔符
      final String ellipsisDots;
    
      /// 文本的样式
      final TextStyle contentStyle;
    
      //按钮的 文字样式
      final TextStyle btnTextStyle;
    
      //点击按钮的回调
      final HcTextEllipsisCallback? callback;
    
      //分割符的位置
      final HcTextEllipsisPosition position;
    
      //是否展开
      final bool isExpand;
    
      //是否单独显示
      final bool separateShow;
    
      //是否展示拓展按钮
      final bool showEllipsisBtn;
    
      const HcTextEllipsis(
          {Key? key,
          this.isExpand = false,
          this.maxLines = 1,
          required this.content,
          this.expandText = "展开",
          this.collapseText = "收起",
          this.ellipsisDots = "...",
          this.position = HcTextEllipsisPosition.end,
          this.contentStyle = const TextStyle(color: Colors.black, fontSize: 16),
          this.callback,
          this.btnTextStyle = const TextStyle(
              color: Colors.blue, fontSize: 14, fontWeight: FontWeight.w500),
          this.separateShow = false,
          this.showEllipsisBtn = true})
          : super(key: key);
    
      @override
      State<HcTextEllipsis> createState() => _HcTextEllipsisState();
    }
    
    class _HcTextEllipsisState extends State<HcTextEllipsis> {
      //是否展开
      bool _isExpand = false;
    
      //组件的大小
      Size _viewSize = const Size(0, 0);
    
      //按钮组件的宽度
      double btnWidth = 0.0;
    
      //省略符号的宽度
      double dotWidth = 0.0;
    
      //文字高度
      double fontHeight = 0.0;
    
      //ellipsisLineWidth;
      double ellipsisLineWidth = 0.0;
    
      @override
      void initState() {
        super.initState();
        _isExpand = widget.isExpand;
      }
    
      @override
      void didUpdateWidget(covariant HcTextEllipsis oldWidget) {
        super.didUpdateWidget(oldWidget);
        _isExpand = widget.isExpand;
      }
    
      @override
      Widget build(BuildContext context) {
        return LayoutBuilder(builder: (context, size) {
          //按钮的宽度
          btnWidth =
              HcStringUtil.boundingTextSize(widget.expandText, widget.btnTextStyle)
                  .width;
          //文字的宽度与高度
          Size fontSize = HcStringUtil.boundingTextSize(
              widget.ellipsisDots, widget.contentStyle);
          dotWidth = fontSize.width;
          fontHeight = fontSize.height;
          //组件最大长度
          _viewSize = Size(size.maxWidth, size.maxHeight);
          //获取painter信息
          TextPainter painter = HcStringUtil.getTextInfo(
              widget.content, widget.contentStyle,
              maxLines: widget.maxLines, width: _viewSize.width);
    
          // 如果不满一行直接显示
          if (!painter.didExceedMaxLines) {
            return Text(widget.content, style: widget.contentStyle);
          }
          String result = "";
          String content = widget.content;
          switch (widget.position) {
            case HcTextEllipsisPosition.start:
              result = _buildEllipsisStartText(content, widget.maxLines);
              break;
            case HcTextEllipsisPosition.center:
              if (widget.maxLines == 1) {
                ellipsisLineWidth = _viewSize.width -
                    (!widget.separateShow && widget.showEllipsisBtn
                        ? btnWidth
                        : 0) -
                    dotWidth;
                int start = HcStringUtil.getTextInfo(content, widget.contentStyle,
                        width: (ellipsisLineWidth ~/ 2).toDouble())
                    .getPositionForOffset(
                        Offset((ellipsisLineWidth ~/ 2).toDouble(), 0))
                    .offset;
                result = content.substring(0, start);
                result += widget.ellipsisDots;
                content = HcStringUtil.reverseString(content);
                int end = HcStringUtil.getTextInfo(content, widget.contentStyle,
                        width:
                            ellipsisLineWidth - (ellipsisLineWidth ~/ 2).toDouble())
                    .getPositionForOffset(Offset(
                        ellipsisLineWidth - (ellipsisLineWidth ~/ 2).toDouble(), 0))
                    .offset;
                result += HcStringUtil.reverseString(content.substring(0, end));
              } else {
                double halfLine = (widget.maxLines ~/ 2).toDouble();
                result += _buildEllipsisEndText(content, widget.maxLines - halfLine,
                    showEllipsis: false);
                result += _buildEllipsisStartText(content, halfLine);
              }
              break;
            case HcTextEllipsisPosition.end:
              result = _buildEllipsisEndText(content, widget.maxLines);
              break;
          }
    
          return RichText(
            text: TextSpan(
                text: _isExpand ? widget.content : result,
                style: widget.contentStyle,
                children: [if (widget.showEllipsisBtn) _ellipsisBtn()]),
          );
        });
      }
    
      InlineSpan _ellipsisBtn() {
        return TextSpan(
            text: _isExpand ? widget.collapseText : widget.expandText,
            style: widget.btnTextStyle,
            recognizer: TapGestureRecognizer()
              ..onTap = () async {
                bool isExpand = !_isExpand;
                if (widget.callback != null) {
                  try {
                    isExpand = await widget.callback!.call(_isExpand) ?? isExpand;
                  } finally {}
                }
                setState(() {
                  _isExpand = isExpand;
                });
              });
      }
    
      String _buildEllipsisStartText(content, maxLines) {
        String result = "";
        // 反转文字截取最后
        content = HcStringUtil.reverseString(content);
        // 获取文字展示宽度
        ellipsisLineWidth = _viewSize.width -
            (!widget.separateShow && widget.showEllipsisBtn ? btnWidth : 0) -
            (widget.maxLines == 1 ? dotWidth : 0);
        // 获取阶段位置
        int lastLineIndex = HcStringUtil.getTextInfo(content, widget.contentStyle,
                width: _viewSize.width)
            .getPositionForOffset(Offset(ellipsisLineWidth, 0))
            .offset;
        // 判断阶段位置展示的文字是否超过最大限度
        double resultWidth = HcStringUtil.boundingTextSize(
                content.substring(0, lastLineIndex), widget.contentStyle)
            .width;
        // 修正最大截取距离
        lastLineIndex =
            resultWidth > ellipsisLineWidth ? lastLineIndex - 1 : lastLineIndex;
        String temp = content.substring(0, lastLineIndex);
        if (maxLines > 1) {
          content = content.substring(lastLineIndex);
          if (maxLines > 2) {
            int centerIndex = HcStringUtil.getTextInfo(content, widget.contentStyle,
                    width: _viewSize.width)
                .getPositionForOffset(
                    Offset(_viewSize.width, fontHeight * (maxLines - 3)))
                .offset;
            temp += content.substring(0, centerIndex);
    
            content = content.substring(centerIndex);
          }
          int firstIndex = HcStringUtil.getTextInfo(content, widget.contentStyle,
                  width: _viewSize.width - dotWidth)
              .getPositionForOffset(Offset(_viewSize.width - dotWidth, 0))
              .offset;
          temp += content.substring(0, firstIndex);
        }
    
        result = widget.ellipsisDots;
        result += HcStringUtil.reverseString(temp);
        return result;
      }
    
      String _buildEllipsisEndText(content, maxLines, {bool showEllipsis = true}) {
        String result = "";
        if (maxLines > 1) {
          int position = HcStringUtil.getTextInfo(content, widget.contentStyle,
                  width: _viewSize.width)
              .getPositionForOffset(
                  Offset(_viewSize.width, fontHeight * (maxLines - 2)))
              .offset;
          result = content.substring(0, position);
          content = content.substring(position);
        }
        ellipsisLineWidth = _viewSize.width -
            (showEllipsis && (!widget.separateShow && widget.showEllipsisBtn)
                ? btnWidth
                : 0) -
            dotWidth;
        int position = HcStringUtil.getTextInfo(content, widget.contentStyle,
                width: ellipsisLineWidth)
            .getPositionForOffset(Offset(ellipsisLineWidth, 0))
            .offset;
        result += content.substring(0, position);
        if (showEllipsis) {
          result += widget.ellipsisDots;
        }
        return result;
      }
    }
     static Size boundingTextSize(String text, TextStyle style,
          {int maxLines = 2 ^ 31,
          double maxWidth = double.infinity,
          textDirection = TextDirection.ltr}) {
        if (text.isEmpty) {
          return Size.zero;
        }
        final TextPainter textPainter = TextPainter(
            textDirection: textDirection,
            text: TextSpan(text: text, style: style),
            maxLines: maxLines)
          ..layout(maxWidth: maxWidth);
        return textPainter.size;
      }
    
      /// 获取文字信息
      static TextPainter getTextInfo(String text, TextStyle style,
          {int maxLines = 2 ^ 23,
          double width = double.infinity,
          String? ellipsis,
          textDirection = TextDirection.ltr}) {
        TextSpan span = TextSpan(text: text, style: style);
        return TextPainter(
          text: span,
          maxLines: maxLines,
          ellipsis: ellipsis,
          textDirection: textDirection,
        )..layout(maxWidth: width);
      }
    
      ///翻转文字
      static String reverseString(String str) {
        return str.split("").reversed.join();
      }
    

    相关文章

      网友评论

          本文标题:Flutter 重复造轮子 (9) Ellipsis 省略文字组

          本文链接:https://www.haomeiwen.com/subject/nzltvdtx.html