写点什么

Flutter 自定义 ACEFoldTextView 折叠文本

作者:阿策小和尚
  • 2021 年 12 月 10 日
  • 本文字数:2520 字

    阅读完需:约 8 分钟

Flutter 自定义 ACEFoldTextView 折叠文本

    小菜在学习 Flutter 过程中,有特别需求是对于文本过长的内容需要展示固定行数,而在文本右下角有提示用户点击展开和收起;小菜尝试自定义一个可折叠收缩的 ACEFoldTextView


ACEFoldTextView

    小菜首先简单梳理了一下设计流程,如下图所示;


  • 当文本内容所占据行数小于等于限制的最大行数时,默认展示整个文本内容,不会有【展开/收起】;

  • 当文本内容所占据行数大于限制的最大行数时,默认展示最大行数内容,并在右下角显示【展开】提示;

  • 点击【展开】区域时,当文本内容最后一行内容与【展开】区域占据内容宽度只和小于最大宽度时,默认展示【收起】;

  • 点击【展开】区域时,当文本内容最后一行内容与【展开】区域占据内容宽度只和大于等于最大宽度时,【收起】区域换行展示;


1. 透明渐变【展开/收起】

    小菜整体通过 Stack 层级嵌套方式在右下角显示可点击的【展开/收起】文本区,为了提高显示效果,并防止完全遮挡内容文本,小菜尝试了两种方式来实现颜色透明度渐变;

1.1 ShaderMask 着色器

    小菜之前有重点介绍过 ShaderMask 着色器,可以对子 Widget 进行颜色处理,包括遮罩层特效展示;小菜设置了一个 LinearGradient 线性渐变,但 ShaderMask 是对整个子 Widget 遮罩层生效,可能会影响 Text 文本显示效果,需要 Stack 层级使用;


_transparentWid02() => ShaderMask(    shaderCallback: (bounds) => LinearGradient(          colors: [_bgColor.withOpacity(0.0), _bgColor],        ).createShader(bounds),    child: Container(        alignment: Alignment.centerRight,        color: Colors.white,        width: _kMoreWidth,        child: Text((_temLines > _maxLines) ? '展开' : '收起',            style: TextStyle(color: Theme.of(context).accentColor, fontSize: widget.textStyle?.fontSize ?? 14.0))));
复制代码



1.2 Container BoxDecoration

    第二种就是常用的 Container 配合设置 BoxDecoration 设置线性渐变色;该方式使用更为便捷;


_transparentWid01() => Container(    alignment: Alignment.centerRight,    decoration: BoxDecoration(        gradient: LinearGradient(            colors: [_bgColor.withOpacity(0.0), _bgColor],            end: FractionalOffset(0.5, 0.5))),    width: _kMoreWidth,    child: Text((_temLines > _maxLines) ? '展开' : '收起',        style: TextStyle(color: Theme.of(context).accentColor, fontSize: widget.textStyle?.fontSize ?? 14.0)));
复制代码



2. Text 文本内容折叠

    小菜想实现文本折叠,首先需要预先得知 Text 文本在范围内占据的行数,一般都需要通过 TextPainter 等方式获取;小菜尝试了两种方式进行判断;

2.1 TextPainter.didExceedMaxLines

    小菜之前也有简单了解过 TextPainterTextSpan 的应用,主要用于文本的绘制,当设置 maxLines 之后,可以通过 didExceedMaxLines 判断文本内容是否已经超行;小菜之后会对 TextPainter 再深入研究一下;


_checkOverMaxLines01(maxLines, maxWidth) {  final textSpan = TextSpan(text: _textStr, style: widget.textStyle);  final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr, maxLines: maxLines);  textPainter.layout(maxWidth: widget.maxWidth ?? MediaQuery.of(context).size.width);  return textPainter.didExceedMaxLines;}
复制代码

2.2 LineMetrics

    didExceedMaxLines 可以直接获取文本内容是否超行,但无法获取每行文本信息等;于是小菜尝试了 computeLineMetrics() 方式获取 LineMetrics 基线度量;可以获取每行内容所占据的宽高等;


    当然 LineMetrics 也无法获取每行文本内容,以及在两种文本对齐方式共用时有注意事项,小菜之后会进一步研究;


    Tips: 在使用 computeLineMetrics() 获取 LineMetrics 信息时,需要注意 TextPainter 必须设置好 textDirection 文本对齐方式,以及在 layout 布局之后才可以获取;


_checkOverMaxLines02(maxWidth) {  final textSpan = TextSpan(text: _textStr, style: widget.textStyle);  final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr);  textPainter.layout(maxWidth: widget.maxWidth ?? MediaQuery.of(context).size.width);  _lines = textPainter.computeLineMetrics();  return _lines;}
复制代码

3. ACEFoldTextView

    有了前面两步的基础,小菜将其结合起来,生成自定义 ACEFoldTextView;通过 LinearBuilder 约束子 Text 延迟加载;通过 LineMetrics 获取最后一行文本长度,与默认【展开】所在 Widget 计算总和,之后判断是否占据超过限制最大宽度;当超过最大宽度时,小菜将文本添加一个 \n 强制换行;


return LayoutBuilder(builder: (context, size) {  _isOverFlow = _checkOverMaxLines01(_maxLines, widget.maxWidth);  _temLines = _checkOverMaxLines02(widget.maxWidth)?.length;  return (_temLines <= _maxLines)      ? _itemText() : Stack(children: <Widget>[_itemText(), _moreText()]);});
_moreText() => Positioned( bottom: 0, right: 0, child: GestureDetector( child: _transparentWid02(), onTap: () => setState(() { if (_temLines > _maxLines) { if (_lines.last.width + _kMoreWidth >= widget.maxWidth) { _maxLines = _temLines + 1; _textStr = '${widget.text}\n'; } else { _maxLines = _temLines; } } else if (_temLines == _maxLines) { _maxLines = widget.maxLines; } })));
复制代码




    ACEFoldTextView 案例源码


    小菜对 ACEFoldTextView 的绘制到此位置,其中涉及到 TextPainter 内容较浅显,小菜之后会进一步学习研究;如有错误,请多多指导!

【签约作者第二季】


来源: 阿策小和尚

发布于: 9 小时前阅读数: 6
用户头像

还未添加个人签名 2021.05.13 加入

Android / Flutter 小菜鸟~

评论

发布
暂无评论
Flutter 自定义 ACEFoldTextView 折叠文本