【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;是一种特殊的二叉搜索树。一般的二叉搜索树在遇到数据有序时&…

@SuppressWarnings(“unchecked“)

SuppressWarnings("unchecked") 是 Java 编程语言中的一个注解&#xff0c;用于抑制编译器产生的类型检查警告。在某些情况下&#xff0c;程序员知道特定的代码块中可能会产生类型转换警告&#xff0c;但他们确信代码是安全的&#xff0c;因此使用这个注解来告诉编译…

路由策略简介

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

整合Spring Boot框架集成Knife4j

效率工具 推荐一个程序员常用的工具网站&#xff1a;程序员常用工具&#xff08;http://tools.cxyroad.com&#xff09;&#xff0c;有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具&#xff0c;效率加倍嘎嘎好用。 云服务器 云服务器限时免费领&#xff1a;轻量…

Nginx的配置文件-详细使用说明

Nginx的配置文件是Nginx服务器运行的核心,它决定了Nginx如何响应和处理各种请求。以下是对Nginx配置文件(通常名为nginx.conf)的详细解析,按照常见的结构和配置项进行分类: 1. 全局块 user:指定Nginx运行的用户和用户组。例如:user nginx;worker_processes:指定工作进…

FPGA复位专题---(41)复位管脚PR报错?

(41)复位管脚PR报错? 1 目录 (a)FPGA简介 (b)Verilog简介 (c)复位简介 (d)复位管脚PR报错? (e)结束 1 FPGA简介 (a)FPGA(Field Programmable Gate Array)是在PAL (可编程阵列逻辑)、GAL(通用阵列逻辑)等可编程器件的基础上进一步发展的产物。它是…

微服务开发与实战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模型…

react组件中的this

在React类组件中&#xff0c;如果你使用传统的函数声明方式定义一个方法( function xxx)&#xff0c;那么这个方法内部的 this 通常是 undefined。这是因为JavaScript函数中的 this 是在运行时基于函数是如何被调用的来绑定的&#xff0c;而不是在定义时。 例如&#xff1a; c…

混剪素材库有哪些?分享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没…

spark学习记录-spark基础概念

背景需求 公司有项目需要将大容量数据进行迁移&#xff0c;经过讨论&#xff0c;采用spark框架进行同步、转换、解析、入库。故此&#xff0c;这里学习spark的一些基本的概念知识。 Apache Spark 是一个开源的大数据处理框架&#xff0c;可以用于高效地处理和分析大规模的数据…

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

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

C语言题目要求实现方法总结(1-10)

目录 一、互换A, B的值 1. 1使用中间变量 1.2 使用异或^&#xff08;不允许创建中间变量&#xff09; 1.3 使用函数&#xff08;指针传参&#xff09; 二、 按降序输出A, B的值 2.1 直接实现 2.2 使用指针 三、 找出最大值 3.1 遍历数组 先输入再找&#xff08;常规&#xff09…

一文了解JVM(中)

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

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

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