当前位置: 首页 > news >正文

模板支架图片开源seo软件

模板支架图片,开源seo软件,centos部署wordpress,网站添加二级域名flutter聊天界面-TextField输入框实现功能等匹配正则表达式展示高亮功能 一、简要描述 描述: 最近有位朋友讨论的时候,提到了输入框的高亮展示。在flutter TextField中需要插入特殊样式的标签,比如:“请 张三 回答一下”&#x…

flutter聊天界面-TextField输入框实现@功能等匹配正则表达式展示高亮功能

一、简要描述

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

效果图如下

在这里插入图片描述
视频效果

flutter聊天界面-TextField输入框实现@功能

昨天整理的文中,简单实用TextEditingController的buildTextSpan时候,直接修改代码

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

会出现光标输入的问题,这里修改了一下,完善了一下。

大家可以使用rich_text_controller来实现,查看rich_text_controller源码可以看到,RichTextController继承TextEditingController,重写了buildTextSpan。经过我在iPhone上测试,当输入@汉字的时候,对中文兼容会有问题,这里做一下修改完善修改。

二、TextEditingController的buildTextSpan方法

在TextEditingController中buildTextSpan方法中,我们可以看到,该方法中的代码
在这里插入图片描述

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)),],);
  • composingStyle未输入完成的字的样式,可以自己做下修改。

  • value.composing.textBefore:当前输入前面的字。

  • value.composing.textAfter:当前输入后面的字。

当输入过程中,我们将value.composing.textBefore,value.composing.textAfter匹配高亮即可

代码如下

value.composing.textBefore

TextSpan(style: style, children: buildRegExpSpan(context: context, text: value.composing.textBefore(value.text))),

value.composing.textAfter

TextSpan(style: style, children: buildRegExpSpan(context: context, text: value.composing.textAfter(value.text))),

匹配正则表达式

List<TextSpan> buildRegExpSpan({required BuildContext context,TextStyle? style,required String? text}) {List<TextSpan> children = [];if (!(text != null && text.isNotEmpty)) {return children;}final matches = <String>{};List<Map<String, List<int>>> matchIndex = [];// Validating with REGEXRegExp? allRegex;allRegex = patternMatchMap != null? RegExp(patternMatchMap?.keys.map((e) => e.pattern).join('|') ?? "",caseSensitive: regExpCaseSensitive,dotAll: regExpDotAll,multiLine: regExpMultiLine,unicode: regExpUnicode): null;// Validating with StringsRegExp? stringRegex;stringRegex = stringMatchMap != null? RegExp(r'\b' + stringMatchMap!.keys.join('|').toString() + r'+\$',caseSensitive: regExpCaseSensitive,dotAll: regExpDotAll,multiLine: regExpMultiLine,unicode: regExpUnicode): null;text.splitMapJoin(stringMatchMap == null ? allRegex! : stringRegex!,onNonMatch: (String span) {if (stringMatchMap != null &&children.isNotEmpty &&stringMatchMap!.keys.contains("${children.last.text}$span")) {final String? ks =stringMatchMap!["${children.last.text}$span"] != null? stringMatchMap?.entries.lastWhere((element) {return element.key.allMatches("${children.last.text}$span").isNotEmpty;}).key: '';children.add(TextSpan(text: span, style: stringMatchMap![ks!]));return span.toString();} else {children.add(TextSpan(text: span, style: style));return span.toString();}},onMatch: (Match m) {matches.add(m[0]!);final RegExp? k = patternMatchMap?.entries.firstWhere((element) {return element.key.allMatches(m[0]!).isNotEmpty;}).key;final String? ks = stringMatchMap?[m[0]] != null? stringMatchMap?.entries.firstWhere((element) {return element.key.allMatches(m[0]!).isNotEmpty;}).key: '';if (deleteOnBack!) {if ((isBack(text!, _lastValue) && m.end == selection.baseOffset)) {WidgetsBinding.instance.addPostFrameCallback((_) {children.removeWhere((element) => element.text! == text);text = text!.replaceRange(m.start, m.end, "");selection = selection.copyWith(baseOffset: m.end - (m.end - m.start),extentOffset: m.end - (m.end - m.start),);});} else {children.add(TextSpan(text: m[0],style: stringMatchMap == null? patternMatchMap![k]: stringMatchMap![ks],),);}} else {children.add(TextSpan(text: m[0],style: stringMatchMap == null? patternMatchMap![k]: stringMatchMap![ks],),);}final resultMatchIndex = matchValueIndex(m);if (resultMatchIndex != null && onMatchIndex != null) {matchIndex.add(resultMatchIndex);onMatchIndex!(matchIndex);}return (onMatch(List<String>.unmodifiable(matches)) ?? '');},);return children;}

这里使用的是rich_text_controller中的代码,做了相应的修改,输入@张三正则表达式正常高亮显示了。

整个text_field_controller代码如下

import 'package:flutter/material.dart';class TextFieldController extends TextEditingController {final Map<RegExp, TextStyle>? patternMatchMap;final Map<String, TextStyle>? stringMatchMap;final Function(List<String> match) onMatch;final Function(List<Map<String, List<int>>>)? onMatchIndex;final bool? deleteOnBack;String _lastValue = "";/// controls the caseSensitive property of the full [RegExp] used to pattern matchfinal bool regExpCaseSensitive;/// controls the dotAll property of the full [RegExp] used to pattern matchfinal bool regExpDotAll;/// controls the multiLine property of the full [RegExp] used to pattern matchfinal bool regExpMultiLine;/// controls the unicode property of the full [RegExp] used to pattern matchfinal bool regExpUnicode;bool isBack(String current, String last) {return current.length < last.length;}TextFieldController({String? text,this.patternMatchMap,this.stringMatchMap,required this.onMatch,this.onMatchIndex,this.deleteOnBack = false,this.regExpCaseSensitive = true,this.regExpDotAll = false,this.regExpMultiLine = false,this.regExpUnicode = false}): assert((patternMatchMap != null && stringMatchMap == null) ||(patternMatchMap == null && stringMatchMap != null)),super(text: text);/// Setting this will notify all the listeners of this [TextEditingController]/// that they need to update (it calls [notifyListeners]).set text(String newText) {value = value.copyWith(text: newText,selection: const TextSelection.collapsed(offset: -1),composing: TextRange.empty,);}/// Builds [TextSpan] from current editing value.TextSpan 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;if (composingRegionOutOfRange) {List<TextSpan> children = [];final matches = <String>{};List<Map<String, List<int>>> matchIndex = [];// Validating with REGEXRegExp? allRegex;allRegex = patternMatchMap != null? RegExp(patternMatchMap?.keys.map((e) => e.pattern).join('|') ?? "",caseSensitive: regExpCaseSensitive,dotAll: regExpDotAll,multiLine: regExpMultiLine,unicode: regExpUnicode): null;// Validating with StringsRegExp? stringRegex;stringRegex = stringMatchMap != null? RegExp(r'\b' + stringMatchMap!.keys.join('|').toString() + r'+\$',caseSensitive: regExpCaseSensitive,dotAll: regExpDotAll,multiLine: regExpMultiLine,unicode: regExpUnicode): null;text.splitMapJoin(stringMatchMap == null ? allRegex! : stringRegex!,onNonMatch: (String span) {if (stringMatchMap != null &&children.isNotEmpty &&stringMatchMap!.keys.contains("${children.last.text}$span")) {final String? ks =stringMatchMap!["${children.last.text}$span"] != null? stringMatchMap?.entries.lastWhere((element) {return element.key.allMatches("${children.last.text}$span").isNotEmpty;}).key: '';children.add(TextSpan(text: span, style: stringMatchMap![ks!]));return span.toString();} else {children.add(TextSpan(text: span, style: style));return span.toString();}},onMatch: (Match m) {matches.add(m[0]!);final RegExp? k = patternMatchMap?.entries.firstWhere((element) {return element.key.allMatches(m[0]!).isNotEmpty;}).key;final String? ks = stringMatchMap?[m[0]] != null? stringMatchMap?.entries.firstWhere((element) {return element.key.allMatches(m[0]!).isNotEmpty;}).key: '';if (deleteOnBack!) {if ((isBack(text, _lastValue) && m.end == selection.baseOffset)) {WidgetsBinding.instance.addPostFrameCallback((_) {children.removeWhere((element) => element.text! == text);text = text.replaceRange(m.start, m.end, "");selection = selection.copyWith(baseOffset: m.end - (m.end - m.start),extentOffset: m.end - (m.end - m.start),);});} else {children.add(TextSpan(text: m[0],style: stringMatchMap == null? patternMatchMap![k]: stringMatchMap![ks],),);}} else {children.add(TextSpan(text: m[0],style: stringMatchMap == null? patternMatchMap![k]: stringMatchMap![ks],),);}final resultMatchIndex = matchValueIndex(m);if (resultMatchIndex != null && onMatchIndex != null) {matchIndex.add(resultMatchIndex);onMatchIndex!(matchIndex);}return (onMatch(List<String>.unmodifiable(matches)) ?? '');},);_lastValue = text;return TextSpan(style: style, children: children);}final TextStyle composingStyle = style?.merge(const TextStyle(decoration: TextDecoration.underline))?? const TextStyle(decoration: TextDecoration.underline);return TextSpan(children: <TextSpan>[TextSpan(style: style, children: buildRegExpSpan(context: context, text: value.composing.textBefore(value.text))),TextSpan(style: composingStyle,text: value.composing.textInside(value.text),),TextSpan(style: style, children: buildRegExpSpan(context: context, text: value.composing.textAfter(value.text))),],);}Map<String, List<int>>? matchValueIndex(Match match) {final matchValue = match[0]?.replaceFirstMapped('#', (match) => '');if (matchValue != null) {final firstMatchChar = match.start + 1;final lastMatchChar = match.end - 1;final compactMatch = {matchValue: [firstMatchChar, lastMatchChar]};return compactMatch;}return null;}List<TextSpan> buildRegExpSpan({required BuildContext context,TextStyle? style,required String? text}) {List<TextSpan> children = [];if (!(text != null && text.isNotEmpty)) {return children;}final matches = <String>{};List<Map<String, List<int>>> matchIndex = [];// Validating with REGEXRegExp? allRegex;allRegex = patternMatchMap != null? RegExp(patternMatchMap?.keys.map((e) => e.pattern).join('|') ?? "",caseSensitive: regExpCaseSensitive,dotAll: regExpDotAll,multiLine: regExpMultiLine,unicode: regExpUnicode): null;// Validating with StringsRegExp? stringRegex;stringRegex = stringMatchMap != null? RegExp(r'\b' + stringMatchMap!.keys.join('|').toString() + r'+\$',caseSensitive: regExpCaseSensitive,dotAll: regExpDotAll,multiLine: regExpMultiLine,unicode: regExpUnicode): null;text.splitMapJoin(stringMatchMap == null ? allRegex! : stringRegex!,onNonMatch: (String span) {if (stringMatchMap != null &&children.isNotEmpty &&stringMatchMap!.keys.contains("${children.last.text}$span")) {final String? ks =stringMatchMap!["${children.last.text}$span"] != null? stringMatchMap?.entries.lastWhere((element) {return element.key.allMatches("${children.last.text}$span").isNotEmpty;}).key: '';children.add(TextSpan(text: span, style: stringMatchMap![ks!]));return span.toString();} else {children.add(TextSpan(text: span, style: style));return span.toString();}},onMatch: (Match m) {matches.add(m[0]!);final RegExp? k = patternMatchMap?.entries.firstWhere((element) {return element.key.allMatches(m[0]!).isNotEmpty;}).key;final String? ks = stringMatchMap?[m[0]] != null? stringMatchMap?.entries.firstWhere((element) {return element.key.allMatches(m[0]!).isNotEmpty;}).key: '';if (deleteOnBack!) {if ((isBack(text!, _lastValue) && m.end == selection.baseOffset)) {WidgetsBinding.instance.addPostFrameCallback((_) {children.removeWhere((element) => element.text! == text);text = text!.replaceRange(m.start, m.end, "");selection = selection.copyWith(baseOffset: m.end - (m.end - m.start),extentOffset: m.end - (m.end - m.start),);});} else {children.add(TextSpan(text: m[0],style: stringMatchMap == null? patternMatchMap![k]: stringMatchMap![ks],),);}} else {children.add(TextSpan(text: m[0],style: stringMatchMap == null? patternMatchMap![k]: stringMatchMap![ks],),);}final resultMatchIndex = matchValueIndex(m);if (resultMatchIndex != null && onMatchIndex != null) {matchIndex.add(resultMatchIndex);onMatchIndex!(matchIndex);}return (onMatch(List<String>.unmodifiable(matches)) ?? '');},);return children;}
}

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

三、使用TextFieldController测试@张三 高亮

调整好TextFieldController后,我这里测试@张三 高亮

我们进行初始化TextFieldController

// Add a controllerlate TextFieldController _controller;void initState() {// TODO: implement initState_controller = TextFieldController(patternMatchMap: {////* Returns every Hashtag with red color//RegExp(r"@[^\s]+\s?"):TextStyle(color:Colors.green),////* Returns every Hashtag with red color//RegExp(r"\B#[a-zA-Z0-9]+\b"):TextStyle(color:Colors.red),////* Returns every Mention with blue color and bold style.//RegExp(r"\B@[a-zA-Z0-9]+\b"):TextStyle(fontWeight: FontWeight.w800 ,color:Colors.blue,),////* Returns every word after '!' with yellow color and italic style.//RegExp(r"\B![a-zA-Z0-9]+\b"):TextStyle(color:Colors.yellow, fontStyle:FontStyle.italic),// add as many expressions as you need!},//* starting v1.2.0// Now you have the option to add string Matching!// stringMatchMap: {//   "String1":TextStyle(color: Colors.red),//   "String2":TextStyle(color: Colors.yellow),// },//! Assertion: Only one of the two matching options can be given at a time!//* starting v1.1.0//* Now you have an onMatch callback that gives you access to a List<String>//* which contains all matched stringsonMatch: (List<String> matches){// Do something with matches.//! P.S// as long as you're typing, the controller will keep updating the list.},deleteOnBack: true,// You can control the [RegExp] options used:regExpUnicode: true,);super.initState();}

在TextField中使用TextFieldController。具体代码如下

TextField(minLines: 1,maxLines: null,keyboardType: TextInputType.multiline,textAlignVertical: TextAlignVertical.center,autofocus: true,focusNode: editFocusNode,controller: _controller,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中正常了。

使用TextEditingController的buildTextSpan,可以查看:https://blog.csdn.net/gloryFlow/article/details/132889374
完善TextField输入框匹配正则表达式高亮显示,可以查看:https://blog.csdn.net/gloryFlow/article/details/132899084

四、小结

flutter聊天界面-TextField输入框buildTextSpan实现@功能展示高亮功能。自定义修改TextEditingController。
内容较多,描述可能不准确,请见谅。

本文地址:https://blog.csdn.net/gloryFlow/article/details/132899084

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

http://www.dt0577.cn/news/25975.html

相关文章:

  • 长沙o2o网站制作公司怎么自己找外贸订单
  • 建宣传网站苏州seo培训
  • 百度搜索不到asp做的网站新东方考研培训机构官网
  • 厦门的网站建设公司新产品怎样推广
  • 美国网站后缀是什么网站排名优化服务
  • 东莞网站制作搭建竞价托管开户
  • 不知道是谁做的网站 输入学号百度seo教程网
  • 响应式网站 手机版广州四楚seo顾问
  • wordpress seo插件中文版方法seo
  • 深圳网站建设公司报价单网络推广怎么赚钱
  • 网站上做时时彩代理赚钱吗杭州网站优化公司
  • 电子商务网站建设管理论文5188大数据官网
  • wordpress限制访问点击seo软件
  • 网站推广的主要方法有哪些?公众号代运营
  • 易站通这个网站怎么做百度收录网站
  • 响应式网站导航怎么做游戏推广引流
  • ps做网站首页效果图今天今日头条新闻
  • 一个网站开发语言公司怎么在百度上推广
  • 自己做微信优惠券需要网站十大场景营销案例
  • 对口网站怎么做凡科建站
  • wordpress中文名注册网站关键词优化应该怎么做
  • 学子网站建设关键seo排名点击软件
  • 闵行网站建设多久能见效果在哪里找软件开发公司
  • wordpress官方响应式主题关键词优化按天计费
  • 哪个网站可以做分期漳州网络推广
  • 珠海有什么网站google权重查询
  • 国家知识产权局seo排名需要多少钱
  • 网站开发存在的问题百度关键字优化价格
  • 有做挂名法人和股东的网站吗吉林网络公司
  • 东莞市专注网站建设公司常见的网络营销方式有哪些