Flutter:简单搞一个内容高亮

举报
二流小码农 发表于 2023/08/14 18:48:36 2023/08/14
【摘要】 在Flutter当中,实现起来可以说是无比的简单,毕竟原生的组件都给我们提供了,那就是富文本组件RichText。

内容高亮并不陌生,特别是在搜索内容页面,可以说四处可见,就拿掘金这个应用而言,针对某一个关键字,我们搜索之后,与关键字相同的内容,则会高亮展示,如下图所示:


如上的效果,在Flutter当中,实现起来可以说是无比的简单,毕竟原生的组件都给我们提供了,那就是富文本组件RichText。


针对今天的内容,简单的列一个大纲,主要内容如下:


1、案例简单效果

2、认识RichText

3、文本的高亮实现逻辑

4、高亮组件源码


一、案例简单效果

1、简单的内容高亮展示

2、列表形式内容展示



二、认识RichText

要实现高亮效果,那么我们必须了解富文本组件RichText,话又说回来,什么是富文本呢?简单来说,它是一种特殊的文本格式,比普通文本更加丰富多彩,可以包含各种字体、颜色、大小等元素,使文本更加生动、有趣,比如我们常见的阅读协议等场景,均可采用富文本形式,这是原生的文本无法实现的效果。


初识构造


构造属性需要注意的是,这里的text,和文本Text中的text是不一样的,文本Text指的是字符串,这里的text指的是InlineSpan,当然了InlineSpan是抽象基类,一般我们使用TextSpan。


RichText({
    super.key,
    required this.text,
    this.textAlign = TextAlign.start,
    this.textDirection,
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
    this.textScaleFactor = 1.0,
    this.maxLines,
    this.locale,
    this.strutStyle,
    this.textWidthBasis = TextWidthBasis.parent,
    this.textHeightBehavior,
    this.selectionRegistrar,
    this.selectionColor,
  }) : assert(text != null),
       assert(textAlign != null),
       assert(softWrap != null),
       assert(overflow != null),
       assert(textScaleFactor != null),
       assert(maxLines == null || maxLines > 0),
       assert(textWidthBasis != null),
       assert(selectionRegistrar == null || selectionColor != null),
       super(children: _extractChildren(text));


常见构造属性概述:


const TextSpan({
    this.text,
    this.children,
    super.style,
    this.recognizer,
    MouseCursor? mouseCursor,
    this.onEnter,
    this.onExit,
    this.semanticsLabel,
    this.locale,
    this.spellOut,
  }) : mouseCursor = mouseCursor ??
         (recognizer == null ? MouseCursor.defer : SystemMouseCursors.click),
       assert(!(text == null && semanticsLabel != null));


属性

类型

概述

textAlign

TextAlign

文本对齐方式

TextAlign.left
TextAlign.right
TextAlign.cente
TextAlign.justify
TextAlign.start
TextAlign.end

textDirection

TextDirection

文本的方向

TextDirection.ltr
TextDirection.rtl

overflow

TextOverflow

文字溢出的处理方式
TextOverflow.clip:剪切溢出的文本填满容器。
TextOverflow.fade:将溢出的文本淡化为透明。
TextOverflow.ellipsis:使用省略号表示文本已溢出。
TextOverflow.visible:呈现容器外溢出的文本

maxLines

int

最大行数

textWidthBasis

TextWidthBasis

文本的宽度

TextWidthBasis.parent

TextWidthBasis.longestLine


TextSpan常见属性


属性

说明

text

String类型的文本

children

子组件

style

TextStyle类型的文本样式可以设置文字的大小、颜色、样式等

recognizer

指定手势交互
recognizer: TapGestureRecognizer()..onTap = () {},可以监听点击事件


简单案例:


RichText(
            text: const TextSpan(children: [
              TextSpan(text: "床前明月光,", style: TextStyle(color: Colors.black)),
              TextSpan(text: "疑是地上霜。", style: TextStyle(color: Colors.red)),
              TextSpan(text: "举头望明月,", style: TextStyle(color: Colors.blueAccent)),
              TextSpan(text: "低头思故乡。", style: TextStyle(color: Colors.tealAccent))
            ])


效果:



当然了,除了上述写法之外,也可以使用Text.rich来实现,代码如下:


 const Text.rich(TextSpan(children: [
            TextSpan(text: "床前明月光,", style: TextStyle(color: Colors.black)),
            TextSpan(text: "疑是地上霜。", style: TextStyle(color: Colors.red)),
            TextSpan(text: "举头望明月,", style: TextStyle(color: Colors.blueAccent)),
            TextSpan(text: "低头思故乡。", style: TextStyle(color: Colors.tealAccent))
          ]))


三、文本的高亮实现逻辑


RichText可以实现一个富文本展示,那么如何利用这个组件实现某个内容高亮展示呢?首先,我们要明白,高亮的内容是不固定的,一段内容的每个字符都有可能会高亮,所以针对TextSpan,我们就需要动态的创建,然后动态的改变其样式。

这里的动态也是十分的简单,无非就是字符串的截取,分别是开头、结尾、和中间三种情况进行截取,如下图所示。


当然了,需要注意,有可能要搜索的这个内容,在整个内容中是多处存在的,这个时候,针对以上的逻辑,就需要遍历循环了,直至找到最后一个搜索的内容。



主要的逻辑如下:


 //搜索内容为空
    if (_searchContent == "") {
      return Text(
        _content,
        style: _ordinaryStyle,
      );
    }
    List<TextSpan> richList = [];
    int start = 0;
    int end;

    //遍历,进行多处高亮
    while ((end = _content.indexOf(_searchContent, start)) != -1) {
      //如果搜索内容在开头位置,直接高亮,此处不执行
      if (end != 0) {
        richList.add(TextSpan(
            text: _content.substring(start, end), style: _ordinaryStyle));
      }
      //高亮内容
      richList.add(TextSpan(text: _searchContent, style: _highlightStyle));
      //赋值索引
      start = end + _searchContent.length;
    }
    //搜索内容只有在开头或者中间位置,才执行
    if (start != _content.length) {
      richList.add(TextSpan(
          text: _content.substring(start, _content.length),
          style: _ordinaryStyle));
    }

    return RichText(
      text: TextSpan(children: richList),
    );


四、高亮组件源码


源码很简单,可以结合列表组件或者单独使用,当然了,有一些特殊需求,文字加大或者改变背景等需求,都可以进行扩展。


class TextHighlight extends StatelessWidget {
  final TextStyle _ordinaryStyle; //普通的样式
  final TextStyle _highlightStyle; //高亮的样式
  final String _content; //文本内容
  final String _searchContent; //搜索的内容

  const TextHighlight(this._content, this._searchContent, this._ordinaryStyle,
      this._highlightStyle,
      {super.key});

  @override
  Widget build(BuildContext context) {
    //搜索内容为空
    if (_searchContent == "") {
      return Text(
        _content,
        style: _ordinaryStyle,
      );
    }
    List<TextSpan> richList = [];
    int start = 0;
    int end;

    //遍历,进行多处高亮
    while ((end = _content.indexOf(_searchContent, start)) != -1) {
      //如果搜索内容在开头位置,直接高亮,此处不执行
      if (end != 0) {
        richList.add(TextSpan(
            text: _content.substring(start, end), style: _ordinaryStyle));
      }
      //高亮内容
      richList.add(TextSpan(text: _searchContent, style: _highlightStyle));
      //赋值索引
      start = end + _searchContent.length;
    }
    //搜索内容只有在开头或者中间位置,才执行
    if (start != _content.length) {
      richList.add(TextSpan(
          text: _content.substring(start, _content.length),
          style: _ordinaryStyle));
    }

    return RichText(
      text: TextSpan(children: richList),
    );
  }
}


案例Demo很是简单,上边是搜索框,下面是展示的内容,这里就不贴了,高亮组件已经给大家提供了,大家可以直接复制使用。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。