【Flutter】 TextField限制长度时, 第三方手写输入法、ios原始拼音输入法输入被吞问题

问题描述

 TextField限制长度时, 当你的输入字符长度已经到了最大值-1时,使用第三方手写输入法或者ios原生拼音输入法输入liang(什么拼音都行,这里只是举例),输到i那么li都会消失。

原因分析

这是因为第三方手写输入法或者ios原生拼音输入法,虽然还没选中哪个汉子,但是输入的拼音字母已经显示在输入框了,那么这个字符串就会算作已经输入了,再计算TextField字符串长度的时候会加上未选中的拼音,跟最大长度作对比,字符数比最大值还要大的时候就会因为TextField的判定机制使未选中的字符消失。

解决方法

TextField除了使用maxLength设置最大长度外,还可以使用inputFormatters限制:

            TextField(controller: _controller,inputFormatters: [LengthLimitingTextInputFormatter(maxLength),],......

源码分析

LengthLimitingTextInputFormatter是flutter原生的类,代码如下:

class LengthLimitingTextInputFormatter extends TextInputFormatter {LengthLimitingTextInputFormatter(this.maxLength, {this.maxLengthEnforcement,}) : assert(maxLength == null || maxLength == -1 || maxLength > 0);final int? maxLength;  //传入的最大长度//确定应如何强制执行 maxLength 限制,默认为 MaxLengthEnforcement.enforcedfinal MaxLengthEnforcement? maxLengthEnforcement; //获取默认的maxLengthEnforcement,总共有三种,//有兴趣可以看文章最后或者看源码注释研究一下,这个问题的解决方法不需要这个方法static MaxLengthEnforcement getDefaultMaxLengthEnforcement([TargetPlatform? platform,]) {if (kIsWeb) {return MaxLengthEnforcement.truncateAfterCompositionEnds;} else {switch (platform ?? defaultTargetPlatform) {case TargetPlatform.android:case TargetPlatform.windows:return MaxLengthEnforcement.enforced;case TargetPlatform.iOS:case TargetPlatform.macOS:case TargetPlatform.linux:case TargetPlatform.fuchsia:return MaxLengthEnforcement.truncateAfterCompositionEnds;}}}//截断字符串@visibleForTestingstatic TextEditingValue truncate(TextEditingValue value, int maxLength) {final CharacterRange iterator = CharacterRange(value.text);if (value.text.characters.length > maxLength) {iterator.expandNext(maxLength);}final String truncated = iterator.current;return TextEditingValue(text: truncated,selection: value.selection.copyWith(baseOffset: math.min(value.selection.start, truncated.length),extentOffset: math.min(value.selection.end, truncated.length),),composing: !value.composing.isCollapsed && truncated.length > value.composing.start? TextRange(start: value.composing.start,end: math.min(value.composing.end, truncated.length),): TextRange.empty,);}//顾名思义,更新字符串,每次输入的值发生改变时都会调用。//根据字符串最大值以及maxLengthEnforcement判断返回的是什么。@overrideTextEditingValue formatEditUpdate(TextEditingValue oldValue,TextEditingValue newValue,) {final int? maxLength = this.maxLength;if (maxLength == null ||maxLength == -1 ||newValue.text.characters.length <= maxLength) {return newValue;}assert(maxLength > 0);switch (maxLengthEnforcement ?? getDefaultMaxLengthEnforcement()) {case MaxLengthEnforcement.none:return newValue;case MaxLengthEnforcement.enforced:if (oldValue.text.characters.length == maxLength && oldValue.selection.isCollapsed) {return oldValue;}return truncate(newValue, maxLength);case MaxLengthEnforcement.truncateAfterCompositionEnds:if (oldValue.text.characters.length == maxLength &&!oldValue.composing.isValid) {return oldValue;}if (newValue.composing.isValid) {return newValue;}return truncate(newValue, maxLength);}}
}

输入框显示的是什么是formatEditUpdate可以决定的,使用这次的更改主要是formatEditUpdate。

解决代码 

参考LengthLimitingTextInputFormatter写一个新的TextInputFormatter。

这里只改了formatEditUpdate,以及不需要判定maxLengthEnforcement,所以把相关代码也删了。

class MyLengthLimitingTextInputFormatter extends TextInputFormatter{final int? maxLength;MyLengthLimitingTextInputFormatter(this.maxLength) : assert(maxLength == null || maxLength == -1 || maxLength > 0);@visibleForTestingstatic TextEditingValue truncate(TextEditingValue value, int maxLength) {final CharacterRange iterator = CharacterRange(value.text);if (StringUtil.getTextLength(value.text) > maxLength) {iterator.expandNext(maxLength);}final String truncated = iterator.current;return TextEditingValue(text: truncated,selection: value.selection.copyWith(baseOffset: math.min(value.selection.start, truncated.length),extentOffset: math.min(value.selection.end, truncated.length),),composing: !value.composing.isCollapsed && truncated.length > value.composing.start? TextRange(start: value.composing.start,end: math.min(value.composing.end, truncated.length),): TextRange.empty,);}//oldValue就是显示在输入框上的旧值,newValue就是显示在输入框上的旧值加你新输入的字符串@overrideTextEditingValue formatEditUpdate(TextEditingValue oldValue,TextEditingValue newValue,) {final int? maxLength = this.maxLength;//加上你新输入的字符串都没大于限制的最大值就直接在输入框显示新字符串if (maxLength == null ||maxLength == -1 ||newValue.text.characters.length <= maxLength) {return newValue;}assert(maxLength > 0);//已经达到最大值且新输入的字符串不是正在输入的状态//(真正输入存活状态:拼音拼出的字还没选中(下面会有下划线)或者手写的字还没在输入法选中(下面会有下划线);//存活状态就是反过来,选中了或者输入英文的时候就是打出的每个字母都直接到输入框,下面也不会有下划线) ,直接返回旧的值if (oldValue.text.characters.length == maxLength && !newValue.composing.isValid) {return oldValue;}//已经达到最大值且新输入的字符串是正在输入的状态,显示旧值+你新输入的值=newValue,//就是这句解决了拼音会被吞的问题if (oldValue.text.characters.length == maxLength && newValue.composing.isValid){return newValue;}// Enforced to return a truncated value.return truncate(newValue, maxLength);}}

使用

将LengthLimitingTextInputFormatter换成MyLengthLimitingTextInputFormatter就行。

            TextField(controller: _controller,inputFormatters: [MyLengthLimitingTextInputFormatter(maxLength),],......

缺陷

但是这样的解决方法还有一个问题:只要你英文打的够快,然后快速选择还在输入法上没消失的待选择的字母,它就能吞了前面的字。

附上这个问题没用到的:maxLengthEnforcement的区别

 源码:

enum MaxLengthEnforcement {/// No enforcement applied to the editing value. It's possible to exceed the/// max length.none,/// Keep the length of the text input from exceeding the max length even when/// the text has an unfinished composing region.enforced,/// Users can still input text if the current value is composing even after/// reaching the max length limit. After composing ends, the value will be/// truncated.truncateAfterCompositionEnds,
}

none:限制长度后还是可以随便输入 ,设了等于没设;

enforced:当字符长度达到限制长度时,正在输入的字符停止输入后会强制消失,例如输入英文字符停手后就会从输入法消失

truncateAfterCompositionEnds:当字符长度达到限制长度时,但正在输入的字符强制不会消失,也就是输入法里面预选的字不会消失

另一个方法

网上找到的另一个我还没验证的方法:

https://juejin.cn/post/6844904096407765005

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

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

相关文章

缓存方法返回值

1. 业务需求 前端用户查询数据时,数据查询缓慢耗费时间; 基于缓存中间件实现缓存方法返回值:实现流程用户第一次查询时在数据库查询,并将查询的返回值存储在缓存中间件中,在缓存有效期内前端用户再次查询时,从缓存中间件缓存获取 2. 基于Redis实现 参考1 2.1 简单实现 引入…

【Java面试】十四、LinkedList相关

文章目录 1、单向链表1.1 结构1.2 查询的时间复杂度1.3 插入删除的时间复杂度 2、双向链表2.1 时间复杂度 3、ArrayList和LinkedList的区别是什么 1、单向链表 1.1 结构 存储空间上&#xff0c;非连续链表的每个元素称结点Node每个结点包括两块&#xff1a;存储数据的数据域d…

C/C++ 进阶(5)二叉平衡搜索树(AVL树)

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;C 目录 一、概念 二、平衡因子 三、操作 插入 旋转 左单旋 右单旋 左右双旋 右左双旋 一、概念 二叉平衡搜索树又称AVL树&#xff0c;是一种特殊的二叉搜索树。一般的二叉搜索树在遇到数据有序时&…

路由策略简介

一、路由策略 1、定义: 路由策略(RoutingPolicy)作用于路由&#xff0c;主要实现了路由过滤和路由属性设置等功能&#xff0c;它通过改变路由属性(包括可达性)来改变网络流量所经过的路经。 2、目的 设备在发布、接收和引入路由信息时&#xff0c;根据实际组网需要实施一些策…

微服务开发与实战Day01 - MyBatisPlus

一、微服务 概念&#xff1a;微服务是一种软件架构风格&#xff0c;它是以专注于单一职责的很多小型项目为基础&#xff0c;组合除复杂的大型应用。 课程安排&#xff1a; https://www.bilibili.com/video/BV1S142197x7/?spm_id_from333.1007.top_right_bar_window_history.…

Vue3-Vite-ts 前端生成拓扑图vis,复制即用

完整代码&#xff0c;复制即可用&#xff0c;样式自调 试过 jointjs dagre-d3 vis&#xff0c;好用一点&#xff0c;可添加同层的双向箭头 方法1&#xff1a;Vis.js npm install vis-network <template><div id"mynetwork" class"myChart" :st…

通过DirectML和ONNXRuntime运行Phi-3模型

更多精彩内容&#xff0c;欢迎关注我的公众号“ONE生产力”&#xff01; 上篇我们讲到通过Intel Core Ultra系列处理器内置的NPU加速运行Phi-3模型&#xff0c;有朋友评论说他没有Intel处理器是否有什么办法加速Phi-3模型。通常&#xff0c;使用GPU特别是NVIDA的GPU加速AI模型…

混剪素材库有哪些?分享7个高质量混剪视频素材网站

作为自媒体创作者&#xff0c;我们经常需要高质量的混剪视频素材来吸引观众。今天&#xff0c;我将为大家介绍几个优质的视频素材网站&#xff0c;确保您的短视频制作既高效又充满创意。 蛙学府素材网 首推蛙学府素材网&#xff0c;这个平台真是创作者的福音。无论是短视频素材…

重磅消息! Stable Diffusion 3将于6月12日开源 2B 版本的模型,文中附候补注册链接。

在OpenAI发布Sora后&#xff0c;Stability AI也发布了其最新的模型Stabled Diffusion3, 之前的文章中已经和大家介绍过&#xff0c;感兴趣的小伙伴可以点击以下链接阅读。Sora是音视频方向&#xff0c;Stabled Diffusion3是图像生成方向&#xff0c;那么两者没有必然的联系&…

electron-Vue: Module parse failed: Unexpected character ‘ ‘

​ electron-Vue项目中&#xff0c;我自己写了一个node的C扩展&#xff08;xx.node&#xff09;&#xff0c;然后在.vue文件里import它&#xff0c;然后运行npm run electron:serve&#xff0c;报错如下: ​​ electron-Vue打包默认使用webpack&#xff0c;默认情况下webpack没…

盘点哪些企业容易被ddos攻击

DDoS&#xff08;分布式拒绝服务&#xff09;攻击已成为网络安全威胁中的重要一环。本文将探讨哪些类型的企业容易成为DDoS攻击的目标&#xff0c;并提出相应的防范策略&#xff0c;帮助企业更好地保护自身网络安全。 一、电子商务平台 电子商务平台作为线上交易和支付的重要场…

一文了解JVM(中)

HotSpot 虚拟机对象探秘 对象的创建 Header解释使用 new 关键字调用了构造函数使用 Class 的 newInstance 方法调用了构造函数使用 Constructor 类的newInstance 方法调用了构造函数使用 clone 方法没有调用构造函数使用反序列化没有调用构造函数说到对象的创建,首先让我们看…

6个迹象表明你的电脑电缆管理很糟糕,看下你有没有中招

​清理电脑内部的电缆可能看起来像是徒劳的忙碌。毕竟,如果一切都正常,为什么还要麻烦呢?好吧,我有六个很好的理由可以说服你打开你的机箱,修复你电脑里的混乱。 你很难打开侧板 如果你的电缆离侧板的边缘太近,你将无法毫不费力地将它们滑开。虽然这不是你每天都要做的…

数据结构——图论详细笔记

一 图论基本概念 Directed Acyclic Graph &#xff08;DAG&#xff09; 二 图的存储 ①邻接矩阵(适用于稠密图) ②邻接表(适用于稀疏图) 三、图的遍历 ①深度优先搜索 //(基于邻接表实现&#xff0c;以有向图为例) //DFS:Depth First Search 深度优先搜索 //1、访问起始顶点 …

为什么要开发盲盒小程序?商家企业的盈利方向?

近几年&#xff0c;盲盒已经成为了一种娱乐消费的流行趋势&#xff0c;受到了年轻人的喜欢&#xff0c;推动了盲盒经济的快速发展。在互联网的支持下&#xff0c;盲盒行业也获得了数字化发展&#xff0c;盲盒小程序为市场创新发展提供了重要动力。在当下小程序快速发展的时代&a…

【C++修行之道】类和对象(四)运算符重载

目录 一、 运算符重载 函数重载和运算符重载有什么关系&#xff1f; 二、.*运算符的作用 三、运算符重载的正常使用 四、重载成成员函数 五、赋值运算符重载 1.赋值运算符重载格式 传值返回和引用返回 有没有办法不生成拷贝&#xff1f; 2. 赋值运算符只能重载成类的…

MongoDB CRUD操作:可重试写入

MongoDB CRUD操作&#xff1a;可重试写入 文章目录 MongoDB CRUD操作&#xff1a;可重试写入使用的先决条件部署的限制支持的存储引擎3.6 MongoDB 驱动程序MongoDB 版本写确认 可重试写入和多文档事务启用可重试写入MongoDB驱动mongosh 可重试的写操作行为持续的网络错误故障切…

Linux如何远程连接服务器?

远程连接服务器是当代计算机技术中一个非常重要的功能&#xff0c;在各种领域都有广泛的应用。本文将重点介绍如何使用Linux系统进行远程连接服务器操作。 SSH协议 远程连接服务器最常用的方式是使用SSH&#xff08;Secure Shell&#xff09;协议。SSH是一种网络协议&#xff…

Java常规题技术分享

一、数组排序和添加成员 设计类Student和类StudentClass。 (1) 类Student有字符串属性name、double属性grade和int属性age 有带参数的构造方法&#xff0c;可设置三个属性的值 有各个属性的置取方法 (2)类StudentClass有Student数组属性stus存放班级成员&#xff0c;有int…

「不只是框架:Django REST framework的超能力大揭秘」

想要让你的API服务像五星级餐厅一样令人难忘吗&#xff1f;今天阿佑将为你揭晓&#xff01;从基础的RESTful原则到Django REST framework的高级特性&#xff0c;我们一步步带你走进API开发的后厨&#xff0c;展示如何准备食材&#xff08;数据模型&#xff09;、调制酱料&#…