Flutter 137: 图解自定义 ACEFoldTextView 折叠文本

简介: 0 基础学习 Flutter,第一百三十七步:尝试构建一个简单的可折叠文本!

    小菜在学习 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 内容较浅显,小菜之后会进一步学习研究;如有错误,请多多指导!

来源: 阿策小和尚
目录
相关文章
|
1月前
|
UED 开发者 容器
Flutter&鸿蒙next 的 Sliver 实现自定义滚动效果
Flutter 提供了强大的滚动组件,如 ListView 和 GridView,但当需要更复杂的滚动效果时,Sliver 组件是一个强大的工具。本文介绍了如何使用 Sliver 实现自定义滚动效果,包括 SliverAppBar、SliverList 等常用组件的使用方法,以及通过 CustomScrollView 组合多个 Sliver 组件实现复杂布局的示例。通过具体代码示例,展示了如何实现带有可伸缩 AppBar 和可滚动列表的页面。
110 1
|
1月前
Flutter 自定义组件继承与调用的高级使用方式
本文深入探讨了 Flutter 中自定义组件的高级使用方式,包括创建基本自定义组件、继承现有组件、使用 Mixins 和组合模式等。通过这些方法,您可以构建灵活、可重用且易于维护的 UI 组件,从而提升开发效率和代码质量。
131 1
|
1月前
|
前端开发 开发者
深入探索 Flutter 鸿蒙版的画笔使用与高级自定义动画
本文深入探讨了 Flutter 中的绘图功能,重点介绍了 CustomPainter 和 Canvas 的使用方法。通过示例代码,详细讲解了如何绘制自定义图形、设置 Paint 对象的属性以及实现高级自定义动画。内容涵盖基本绘图、动画基础、渐变动画和路径动画,帮助读者掌握 Flutter 绘图与动画的核心技巧。
80 1
|
1月前
|
Dart UED 开发者
Flutter&鸿蒙next中的按钮封装:自定义样式与交互
在Flutter应用开发中,按钮是用户界面的重要组成部分。Flutter提供了多种内置按钮组件,但有时这些样式无法满足特定设计需求。因此,封装一个自定义按钮组件变得尤为重要。自定义按钮组件可以确保应用中所有按钮的一致性、可维护性和可扩展性,同时提供更高的灵活性,支持自定义颜色、形状和点击事件。本文介绍了如何创建一个名为CustomButton的自定义按钮组件,并详细说明了其样式、形状、颜色和点击事件的处理方法。
86 1
|
1月前
|
Dart 搜索推荐 API
Flutter & 鸿蒙next版本:自定义对话框与表单验证的动态反馈与错误处理
在现代移动应用开发中,用户体验至关重要。本文探讨了如何在 Flutter 与鸿蒙操作系统(HarmonyOS)中创建自定义对话框,并结合表单验证实现动态反馈与错误处理,提升用户体验。通过自定义对话框和表单验证,开发者可以提供更加丰富和友好的交互体验,同时利用鸿蒙next版本拓展应用的受众范围。
84 1
|
3月前
|
前端开发 搜索推荐
Flutter中自定义气泡框效果的实现
Flutter中自定义气泡框效果的实现
114 3
|
4月前
|
前端开发
Flutter快速实现自定义折线图,支持数据改变过渡动画
Flutter快速实现自定义折线图,支持数据改变过渡动画
109 4
Flutter快速实现自定义折线图,支持数据改变过渡动画
|
4月前
|
开发者 监控 开发工具
如何将JSF应用送上云端?揭秘在Google Cloud Platform上部署JSF应用的神秘步骤
【8月更文挑战第31天】本文详细介绍如何在Google Cloud Platform (GCP) 上部署JavaServer Faces (JSF) 应用。首先,确保已准备好JSF应用并通过Maven构建WAR包。接着,使用Google Cloud SDK登录并配置GCP环境。然后,创建`app.yaml`文件以配置Google App Engine,并使用`gcloud app deploy`命令完成部署。最后,通过`gcloud app browse`访问应用,并利用GCP的监控和日志服务进行管理和故障排查。整个过程简单高效,帮助开发者轻松部署和管理JSF应用。
64 0
|
4月前
|
开发者 容器 Java
Azure云之旅:JSF应用的神秘部署指南,揭开云原生的新篇章!
【8月更文挑战第31天】本文探讨了如何在Azure上部署JavaServer Faces (JSF) 应用,充分发挥其界面构建能力和云平台优势,实现高效安全的Web应用。Azure提供的多种服务如App Service、Kubernetes Service (AKS) 和DevOps简化了部署流程,并支持应用全生命周期管理。文章详细介绍了使用Azure Spring Cloud和App Service部署JSF应用的具体步骤,帮助开发者更好地利用Azure的强大功能。无论是在微服务架构下还是传统环境中,Azure都能为JSF应用提供全面支持,助力开发者拓展技术视野与实践机会。
21 0
|
4月前
|
开发框架 API 开发者
Flutter表单控件深度解析:从基本构建到高级自定义,全方位打造既美观又实用的移动端数据输入体验,让应用交互更上一层楼
【8月更文挑战第31天】在构建美观且功能强大的移动应用时,表单是不可或缺的部分。Flutter 作为热门的跨平台开发框架,提供了丰富的表单控件和 API,使开发者能轻松创建高质量表单。本文通过问题解答形式,深入解读 Flutter 表单控件,并通过具体示例代码展示如何构建优秀的移动应用表单。涵盖创建基本表单、处理表单提交、自定义控件样式、焦点管理和异步验证等内容,适合各水平开发者学习和参考。
111 0