前言

最近有个需求是人工语音播放时文本能随语音朗读时像歌词滚动的效果.

原本第一考虑的时能随时间字体渐变成更改后的颜色, 有比较流畅的走马灯效果. 但最终实践了几次后发现要能够逐字逐行渐变有一些麻烦, 不好实现.

所以转而变为将字体直接将字体高亮, 一段文本区分成两个部分, 一个部分是高亮文本, 也就是已朗读的部分, 一个部分是剩下未朗读的非高亮文本. 通过时时渲染页面就能达成滚动高亮的效果.

功能实现

因为在Text中会存在两段文本, 所以就不能单只用Text组件, 而改用Text.rich. 通过textSpan生成一个数组然后放到text.rich中. 所以本文需要处理, 而不是自己一个个拼接, 所以需要先有一个解析的类来负责处理.

需要在项目中加入第三方插件 string_scanner 用于扫描文本.

class StringParser {
  // 导入的文本
  final String content;
  // 高亮部分尾部索引, 也就是两段的区分位置
  final int endIndex;

  StringParser({required this.content, required this.endIndex});

  late StringScanner _scanner;

  // 解析函数
  InlineSpan parser() {
    _scanner = StringScanner(content);

    parseContent();

    final List<InlineSpan> spans = [];

    int currentPosition = 0;

    // 需要高亮的部分
    spans.add(TextSpan(style: _spans.style, text: _spans.text(content)));
    currentPosition = _spans.end;

    // 未高亮的部分
    if (currentPosition != content.length) {
      spans.add(
          TextSpan(text: content.substring(currentPosition, content.length)));
    }

    return TextSpan(style: TextStyleSupport.defaultStyle, children: spans);
  }

  late SpanBean _spans;

  // 解析需要变成高亮的字符
  void parseContent() {
    int startIndex = 0;

    _spans = SpanBean(startIndex, endIndex);

    if (!_scanner.isDone) {
      _scanner.position  ;
    }
  }
}

之后需要定义一个高亮的数据类型, 用于方便修改之后想要高亮的文本样式和默认样式.

class SpanBean {
  SpanBean(this.start, this.end);

  final int start;
  final int end;

  String text(String src) {
    return src.substring(start, end);
  }

  TextStyle get style => TextStyleSupport.highLightStyle;
}

class TextStyleSupport {
  static const defaultStyle = TextStyle(color: Colors.black, fontSize: 36);
  static const highLightStyle = TextStyle(color: Colors.green, fontSize: 36);
}

至此文本高亮和非高亮处理完成, 只需要在文件中导入后使用.

滚动效果则需要实现一个play函数里通过 Future.delayed来控制延时递归执行.

_starPlay(flag) {
	// flag用于判断是 执行还是暂停
    if (this.endIndex == content.length   1 || !flag) {
      return;
    }

    parser = StringParser(content: content, endIndex: this.endIndex  );
    span = parser.parser();

    setState(() {});
    Future.delayed(Duration(milliseconds: 100)).then((value) {
      _starPlay(this.flag);
    });
  }

最终在文件里的代码则是

import 'package:flutter/material.dart';
import 'string_parser.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late InlineSpan span;

  final String content =
      """一点不错,”狐狸说。“对我来说,你还只是一个小男孩,就像其他千万个小男孩一样。我不需要你。你也同样用不着我。对你来说,我也不过是一只狐狸,和其他千万只狐狸一样。但是,如果你驯服了我,我们就互相不可缺少了。对我来说,你就是世界上唯一的了;我对你来说,也是世界上唯一的了。""";

  late StringParser parser;
  int endIndex = 0;
  bool flag = true;

  @override
  void initState() {
    super.initState();
    parser = StringParser(content: content, endIndex: endIndex);
    span = parser.parser();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("滚动高亮文本"),
          actions: [
            ElevatedButton(
                onPressed: () {
                  this.flag = true;
                  _starPlay(flag);
                  print('开始');
                },
                child: Text("开始")),
            ElevatedButton(
                onPressed: () {
                  this.flag = false;
                  // _starPlay(flag);
                  print('暂停');
                },
                child: Text("暂停"))
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Text.rich(span),
        ));
  }

  _starPlay(flag) {
    if (this.endIndex == content.length   1 || !flag) {
      return;
    }

    parser = StringParser(content: content, endIndex: this.endIndex  );
    span = parser.parser();

    setState(() {});
    Future.delayed(Duration(milliseconds: 100)).then((value) {
      _starPlay(this.flag);
    });
  }
}

实现效果:

到此这篇关于Flutter实现文本滚动高亮效果的示例讲解的文章就介绍到这了,更多相关Flutter文本高亮内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Flutter实现文本滚动高亮效果的示例讲解的更多相关文章

  1. UIKit框架-基础控件Swift版本: 3.UILabel方法/属性详解

    前面我们讲解了UI基础控件的UIButton,现在让我们继续往下讲:1.UILabel的状态内容的显示模式内容的位置字体样式文字排序2.常用属性:以下就是我们在实际开发中最常用到的UILabel的属性:3.实现代码由于前面已经把如何创建工程介绍了,这里就不多做第二次介绍了,下面让我们直接来看代码:实现方法:最终的效果:没有高亮时的状态高亮时的状态:好了,这次我们就讲到这里,下次我们继续~~

  2. UIKit框架-基础控件Swift版本: 4.UIImageView方法/属性详解

    前面我把UIButton和UILabel的常用属性讲完了,现在让我们来看看第三个基础控件:1.UIImageView的状态图片的展示状态2.UIImageView的常用属性3.常用的方法4.代码实现在viewDedload中实现:最终效果图:这里有几个动画属性暂时不讲先,等到后面的综合使用时再一起说,这里涉及到的UIImage知识点,下一篇文章里会有讲解.好了,这次就讲到这里,下次我们继续~

  3. Swift UIImageView 构造方法

    UIImageView有三个构造方法init我们可能发现UIImageView并没有这个构造方法,其实他是继承父类UIView的方法init(image:UIImage!)这个比较简单直接看代码init(image:UIImage!)苹果开发群:414319235欢迎加入欢迎讨论问题

  4. swift控件之旅之UIButton

    如:然后在方法中可以获得按钮对象了:运行结果:

  5. Flutter中文教程-Cookbook

    Flutter中文网的Cookbook中包含了在编写Flutter应用程序时常见问题及示例。设计基础使用主题共享颜色和字体样式Images显示来自网上的图片用占位符淡入图片使用缓存图Lists创建一个基本list创建一个水平list使用长列表创建不同类型子项的List创建一个gridList处理手势处理点击添加Material触摸水波效果实现滑动关闭导航导航到新页面并返回给新页面传值从新页面返回数据给上一个页面网络从网上获取数据进行认证请求使用WebSockets

  6. android-studio – 未配置Dart SDK

    Initializinggradle…

  7. 安卓 – 从一个扑动的应用程序拨打电话

    或者有更好的选择从我的应用程序拨打电话?

  8. android – 如何在Flutter中添加Webview?

    我知道可以将WebView添加为整页,但找不到任何示例代码.我假设你可以使用PageView作为它的基础,但不知道如何调用本机androidWebView并将其添加到PageView.谁能指出我正确的方向?

  9. android – 如何将消息从Flutter传递给Native?

    如果需要与特定的API/硬件组件进行交互,您如何将Flutter的信息传递回Android/Native代码?是否有任何事件频道可以通过其他方式发送信息或类似于回调?

  10. android – 如何在Flutter App中处理onPause / onResume?

    我是否过于复杂的事情?即使我的用例似乎不需要它,我仍然想知道:如何自己处理onPause/onResume事件?

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部