flutter聊天界面-TextField输入框buildTextSpan实现@功能展示高亮功能

flutter聊天界面-TextField输入框buildTextSpan实现@功能展示高亮功能

最近有位朋友讨论的时候,提到了输入框的高亮展示。在flutter TextField中需要插入特殊样式的标签,比如:“请 @张三 回答一下”,这一串字符在TextField中输入,当输入@时 弹出好友列表选择,然后将 “@张三”高亮显示在TextField中。

https://blog.csdn.net/gloryFlow/article/details/132889374

效果图如下

在这里插入图片描述

一、TextEditingController中的buildTextSpan

在TextField中,我们找到TextEditingController的buildTextSpan方法。

buildTextSpan功能如下:根据当前编辑值生成[TextSpan],默认情况下,使组成范围内的文本显示为带下划线。继承可以重写此方法以自定义文本的外观。

我这里继承TextEditingController,重写buildTextSpan方法,

List<InlineSpan> textSpans = RichTextHelper.getRichText(value.text);if (composingRegionOutOfRange) {return TextSpan(style: style, children: textSpans);}

完整代码如下

import 'package:flutter/material.dart';
import 'package:flutter_lab/rich_text_helper.dart';class TextFieldController extends TextEditingController {/// Builds [TextSpan] from current editing value.////// By default makes text in composing range appear as underlined. Descendants/// can override this method to customize appearance of text.@overrideTextSpan buildTextSpan({required BuildContext context,TextStyle? style,required bool withComposing}) {assert(!value.composing.isValid ||!withComposing ||value.isComposingRangeValid);// If the composing range is out of range for the current text, ignore it to// preserve the tree integrity, otherwise in release mode a RangeError will// be thrown and this EditableText will be built with a broken subtree.final bool composingRegionOutOfRange =!value.isComposingRangeValid || !withComposing;print("--- composingRegionOutOfRange:${composingRegionOutOfRange},withComposing:${withComposing},value.isComposingRangeValid:${value.isComposingRangeValid}");List<InlineSpan> textSpans = RichTextHelper.getRichText(value.text);if (composingRegionOutOfRange) {return TextSpan(style: style, children: textSpans);}print("+++ composingRegionOutOfRange:${composingRegionOutOfRange}");final TextStyle composingStyle =style?.merge(const TextStyle(decoration: TextDecoration.underline)) ??const TextStyle(decoration: TextDecoration.underline);return TextSpan(style: style,children: <TextSpan>[TextSpan(text: value.composing.textBefore(value.text)),TextSpan(style: composingStyle,text: value.composing.textInside(value.text),),TextSpan(text: value.composing.textAfter(value.text)),],);}
}

二、设置@高亮

实现将 “@张三”高亮显示在TextField中,需要正则表达是,匹配到@功能正则表达式:String regexStr =
r"@[@]*[@ ]+[^@]* ";

使用RegExp

String regexStr =r"@[^@]*[^@ ]+[^@]* ";RegExp exp = RegExp('$regexStr');

具体代码如下

import 'package:flutter/material.dart';class RichTextHelper {//图文混排static getRichText(String text) {List<InlineSpan> textSpans = [];String regexStr =r"@[^@]*[^@ ]+[^@]* ";RegExp exp = RegExp('$regexStr');//正则表达式是否在字符串[input]中有匹配。if (exp.hasMatch(text)) {Iterable<RegExpMatch> matches = exp.allMatches(text);int index = 0;int count = 0;for (var matche in matches) {count++;String c = text.substring(matche.start, matche.end);//匹配到的东西,如表情在首位if (index == matche.start) {index = matche.end;}//匹配到的东西,如表情不在首位else if (index < matche.start) {String leftStr = text.substring(index, matche.start);index = matche.end;textSpans.add(TextSpan(text: spaceWord(leftStr),style: getDefaultTextStyle(),),);}//匹配到的网址if (RegExp(regexStr).hasMatch(c)) {textSpans.add(TextSpan(text: spaceWord(c),style:TextStyle(color: Colors.blueAccent, fontSize: 16),),);}//是否是最后一个表情,并且后面是否有字符串if (matches.length == count && text.length > index) {String rightStr = text.substring(index, text.length);textSpans.add(TextSpan(text: spaceWord(rightStr),style: getDefaultTextStyle(),),);}}} else {textSpans.add(TextSpan(text: spaceWord(text),style: getDefaultTextStyle(),),);}return textSpans;}static TextStyle getDefaultTextStyle() {return TextStyle(fontSize: 16,fontWeight: FontWeight.w400,fontStyle: FontStyle.normal,color: Colors.black87,decoration: TextDecoration.none,);}static String spaceWord(String text) {if (text.isEmpty) return text;String spaceWord = '';for (var element in text.runes) {spaceWord += String.fromCharCode(element);spaceWord += '\u200B';}return spaceWord;}
}

三、创建TextField

创建需要显示的TextField,设置输入框的onTap、onChanged、focusNode、TextEditingController等

代码如下

// 输入框
class InputTextField extends StatefulWidget {const InputTextField({Key? key,this.inputOnTap,this.inputOnChanged,this.inputOnSubmitted,this.inputOnEditingCompleted,this.autofocus = false,required this.textEditingController,}) : super(key: key);final inputOnTap;final inputOnChanged;final inputOnSubmitted;final inputOnEditingCompleted;final bool autofocus;final TextEditingController textEditingController;@overrideState<InputTextField> createState() => _InputTextFieldState();
}class _InputTextFieldState extends State<InputTextField> {FocusNode editFocusNode = FocusNode();@overridevoid initState() {// TODO: implement initStatesuper.initState();}//获取焦点void getFocusFunction(BuildContext context) {FocusScope.of(context).requestFocus(editFocusNode);}//失去焦点void unFocusFunction() {editFocusNode.unfocus();}@overridevoid dispose() {// TODO: implement disposeeditFocusNode.unfocus();editFocusNode.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Container(padding: EdgeInsets.only(left: 10.0,top: 5.0,bottom: 5.0,),constraints: BoxConstraints(minHeight: 40.0,maxHeight: 120.0,),child: TextField(onTap: () {widget.inputOnTap();},onChanged: (string) {widget.inputOnChanged(string);},onEditingComplete: () {widget.inputOnEditingCompleted();},onSubmitted: (string) {widget.inputOnSubmitted(string);},minLines: 1,maxLines: null,keyboardType: TextInputType.multiline,textAlignVertical: TextAlignVertical.center,autofocus: widget.autofocus,focusNode: editFocusNode,controller: widget.textEditingController,textInputAction: TextInputAction.send,decoration: InputDecoration(contentPadding: EdgeInsets.symmetric(vertical: 10, horizontal: 8.0),filled: true,isCollapsed: true,floatingLabelBehavior: FloatingLabelBehavior.never,hintText: "说点什么吧~",hintStyle: TextStyle(fontSize: 14,fontWeight: FontWeight.w400,fontStyle: FontStyle.normal,color: ColorUtil.hexColor(0xACACAC),decoration: TextDecoration.none,),enabledBorder: OutlineInputBorder(/*边角*/borderRadius: const BorderRadius.all(Radius.circular(5.0), //边角为30),borderSide: BorderSide(color: ColorUtil.hexColor(0xf7f7f7), //边框颜色为绿色width: 1, //边线宽度为1),),focusedBorder: OutlineInputBorder(borderRadius: const BorderRadius.all(Radius.circular(5.0), //边角为30),borderSide: BorderSide(color: ColorUtil.hexColor(0xECECEC), //边框颜色为绿色width: 1, //宽度为1),),),),);}
}

四、TextField赋text演示

最后我们可以在输入框TextField设置文本

TextFieldController textEditingController = TextFieldController();textEditingController.text = "你好@张三 欢迎,哈哈,haha";

完整代码如下

class TextFieldRich extends StatefulWidget {const TextFieldRich({super.key});@overrideState<TextFieldRich> createState() => _TextFieldRichState();
}class _TextFieldRichState extends State<TextFieldRich> {TextFieldController textEditingController = TextFieldController();@overridevoid initState() {// TODO: implement initStatesuper.initState();textEditingController.text = "你好@张三 欢迎,哈哈,haha";}@overridevoid dispose() {// TODO: implement disposesuper.dispose();}@overrideWidget build(BuildContext context) {Size scrSize = MediaQuery.of(context).size;return Scaffold(appBar: AppBar(// Here we take the value from the MyHomePage object that was created by// the App.build method, and use it to set our appbar title.title: Text('TextField测试页面'),),body: Container(width: scrSize.width,height: scrSize.height,color: Colors.greenAccent,alignment: Alignment.center,padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0),child: InputTextField(textEditingController: textEditingController),),);}
}

至此可以看到效果图中@张三 高亮显示了。

五、小结

flutter聊天界面-TextField输入框buildTextSpan实现@功能展示高亮功能。该示例中,光标会有问题,暂时没做修改,后续抽空修改。

https://blog.csdn.net/gloryFlow/article/details/132889374

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/81034.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【PowerQuery】Excel的PowerQuery按需刷新

将数据通过PowerQuery 导入进来后,这里将进行数据分组运算,最终的数据计算结果将保存在Excel 表格中,图为销售统计结果。 在Excel中,如果我们希望进行销售统计的手动更新可以使用几种不同的方法来进行刷新: 刷新单一数据连接如果仅仅需要刷新单一数据连接的话我们可以通过…

DETR:End-to-End Object Detection with Transformers

代码&#xff1a;https://github.com/HuKai97/detr-annotations 论文&#xff1a;https://arxiv.org/pdf/2005.12872.pdf 参考视频&#xff1a;DETR 论文精读【论文精读】_哔哩哔哩_bilibili 团队&#xff1a;Meta AI 摘要 DETR 做目标检测任务既不需要proposal&#xff0…

每日一题~中序后序遍历构造二叉树

原题链接&#xff1a;106. 从中序与后序遍历序列构造二叉树 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 思路分析&#xff1a; 后序遍历分析图 中序遍历分析图 不难看出后序遍历的结果中的最后一个元素就是根节点&#xff0c;倒数第二个元素则是根节点的…

VUE build:gulp打包:测试、正式环境

目录 项目结构 Gulp VUE使用Gulp Vue安装Gulp Vue定义Gulp.js package.json build文件夹 config文件夹 static-config文件夹 项目结构 Gulp Gulp是一个自动化构建工具&#xff0c;可以帮助前端开发者通过自动化任务来管理工作流程。Gulp使用Node.js的代码编写&#xff…

【牛客网】BC146 添加逗号

一.题目描述 牛客网题目链接:添加逗号_牛客题霸_牛客网 描述: 对于一个较大的整数 N(1<N<2,000,000,000) 比如 980364535&#xff0c;我们常常需要一位一位数这个数字是几位数&#xff0c;但是如果在这 个数字每三位加一个逗号&#xff0c;它会变得更加易于朗读。 因此&a…

linux查看进程对应的线程(数)

首先&#xff0c;top或ps查看进程列表&#xff0c;确定要查看的进程pid&#xff0c;如下面40698 查看进程的线程情况 查看进程&#xff1a;top -p 40698 查看线程&#xff1a;top -p 40698 -d 3 -H 其中-d是刷新频率 可看到此进程共211个线程&#xff0c;运行中的是211个。…

虹科案例 | Zuellig Pharma和ELPRO通过符合GDP标准的温度监测和高效的温度数据管理为未来发展奠定基础

在本案例研究中&#xff0c;您将了解Zuellig Pharma 实施了温度监测解决方案&#xff0c;以一致的数据结构获取各国和各种运输方式的数据; 通过将温度数据上传到其数据库管理系统&#xff0c;显著提高了其效率; 并建立了为未来管理决策提供数据增值使用的基础。 项目合作伙伴 …

使用终端MobaXterm连接Centos

1. 下载MobaXterm 官网&#xff1a; https://mobaxterm.mobatek.net/download.html 2. MobaXterm连接Linux 1 、查看刚才安装的 Linux 的 IP 地址 2、连接 3. Linux自带了JRE 由于javac指令不能运行&#xff0c;所以Linux只自带了JRE&#xff01;&#xff01;&#xff01;

Vue 2 组件间的通信方式总结

引言 组件间的关系有父子关系、兄弟关系、祖孙关系和远亲关系。 不同的关系间&#xff0c;组件的通信有不同的方式。 一、prop 和 $emit prop向下传递&#xff0c;emit向上传递。 父组件使用 prop 向子组件传递信息。 ParentComponent.vue <template><div><…

VSCODE 使用技巧

vscode批量去掉代码中空行的方法 1、在vscode中使用ctrl f组合快捷键打开替换窗口. 2、输入下面的正则表达式 ^\s*(?\r?$)\n https://mp.weixin.qq.com/s/ZKV2sZWszxBLNTNLEWhsng 你的代码够安全吗&#xff1f;推荐5个VS Code代码安全插件 VSCode&#xff1a;人生苦短&…

C++项目实战——基于多设计模式下的同步异步日志系统-⑤-实用工具类设计

文章目录 专栏导读获取系统时间time介绍 getTime函数设计判断文件是否存在stat介绍exists函数设计 获取文件所在路径find_last_of介绍path函数设计 创建文件所在目录mkdir介绍find_first_of介绍函数createDirectory设计 实用工具类整理 专栏导读 &#x1f338;作者简介&#xf…

VS code 下 makefile 【缺少分隔符 停下来】 报错解决方法

首先来看报错的makefile源码 再来看报错的信息&#xff1a; 第5行缺少分隔符&#xff0c;其实不止是第5行&#xff0c;只要是前面需要加tab留白的行都会报这个错误&#xff0c;比如说第7行第11行 编译的时候&#xff0c;前面的留白必须是按tab键生成的 但是&#xff01;&…

【JavaSE笔记】抽象类与接口

一、抽象类 1、概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这样的类就是抽象类。 package demo2…

Json-Jackson和FastJson

狂神&#xff1a; 测试Jackson 纯Java解决日期格式化 设置ObjectMapper FastJson&#xff1a; 知乎&#xff1a;Jackson使用指南 1、常见配置 方式一&#xff1a;yml配置 spring.jackson.date-format指定日期格式&#xff0c;比如yyyy-MM-dd HH:mm:ss&#xff0c;或者具体的…

ArmSoM-W3之RK3588 Debian11详解

1. 简介 RK3588从入门到精通Debian 是⼀种完全⾃由开放并⼴泛⽤于各种设备的 Linux 操作系统。Rockchip在官⽅Debian发⾏版的基础上构建和适配了相关硬件功能 2. 环境介绍 硬件环境&#xff1a; ArmSoM-W3 RK3588开发板 软件版本&#xff1a; OS&#xff1a;ArmSoM-W3 Debia…

WPF 如何让xmal的属性换行显示 格式化

WPF 如何让UI的xmal 按照下面的格式化显示 首先格式化显示在VS中的快捷键是 Ctrl &#xff2b;D 然后需要配置&#xff0c;工具 选项 -文本编辑器 -xmal -格式化-间距 更改成如下就可以了

RK3588 点亮imx586摄像头

一.硬件原理图 mipi摄像头硬件确认点&#xff1a; 1.供电&#xff1a;5V&#xff0c;2.8V&#xff0c;1.2V&#xff0c;1.8V&#xff0c;reset脚&#xff08;硬拉3.3&#xff0c;上电的时候从低到高&#xff09;&#xff0c;pwron脚外接 3.3V。 2,时钟&#xff1a;MCLKOUT是2…

xen-gic初始化流程

xen-gic初始化流程 调试平台使用的是gic-600&#xff0c;建议参考下面的文档来阅读代码&#xff0c;搞清楚相关寄存器的功能。 《corelink_gic600_generic_interrupt_controller_technical_reference_manual_100336_0106_00_en》 《IHI0069H_gic_architecture_specification》…

linux万字图文学习进程信号

1. 信号概念 信号是进程之间事件异步通知的一种方式&#xff0c;属于软中断。 1.1 linux中我们常用Ctrlc来杀死一个前台进程 1. Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。2. S…

简单记录一下Splunk ES 升级

1: 背景: 现在有些app 产品对splunk ES (enterprise security) 的版本有要求,这个就要求splunk ES 随着Splunk enterprise 也一起升级,下面先列一下各个版本的兼容: Splunk products version compatibility matrix - Splunk Documentation 下面列出的8.2.11 的版本: 2:…