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

news2024/11/18 7:49:49

问题描述

 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.enforced
  final 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;
      }
    }
  }

 //截断字符串
  @visibleForTesting
  static 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判断返回的是什么。
  @override
  TextEditingValue 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);

  @visibleForTesting
  static 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就是显示在输入框上的旧值加你新输入的字符串
  @override
  TextEditingValue 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.coloradmin.cn/o/1799626.html

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

相关文章

nginx配置WebSocket参数wss连接

目录 一、原文连接 二、 配置参数 三、实践 四、重启nginx 五、连接websocket 一、原文连接 nginx配置websocket支持wss-腾讯云开发者社区-腾讯云 二、 配置参数 map $http_upgrade $connection_upgrade { default upgrade; close; } upstream websocket { se…

PostgreSQL基础(九):PostgreSQL的事务介绍

文章目录 PostgreSQL的事务介绍 一、什么是ACID&#xff08;常识&#xff09; 二、事务的基本使用 三、保存点&#xff08;了解&#xff09; PostgreSQL的事务介绍 一、什么是ACID&#xff08;常识&#xff09; 在日常操作中&#xff0c;对于一组相关操作&#xff0c;通常…

基于t-SNE的泰坦尼克号数据集降维

目录 1. 作者介绍2. 算法介绍2.1 t-SNE介绍2.2.SNE基本原理2.3.拥挤问题2.4.t-SNE基本原理2.5.t-SNE算法过程 3. 泰坦尼克号数据集降维实验3.1.数据集介绍3.2 任务介绍3.3 代码实现3.4 实验结果 参考连接 1. 作者介绍 刘方星&#xff0c;男&#xff0c;西安工程大学电子信息学…

vue3 监听器,组合式API的watch用法

watch函数 在组合式 API 中&#xff0c;我们可以使用 watch 函数在每次响应式状态发生变化时触发回调函数 watch(ref,callback&#xff08;newValue,oldValue&#xff09;&#xff0c;option:{}) ref:被监听的响应式量&#xff0c;可以是一个 ref (包括计算属性)、一个响应式…

大型零售企业总部到分公司数据发放,有没有更优化的方案?

大型零售企业在市场经济中扮演重要角色&#xff0c;是保证基础商品生产、流通和供给的重要一环。随着企业发展&#xff0c;很多大型零售企业都会在全国、乃至全球各地开设分公司&#xff0c;用以降低生产和运营成本&#xff0c;更好地提供本地化服务。 为了保证总部与分公司间信…

【C++ | 拷贝构造函数】一文了解C++的 拷贝(复制)构造函数

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-06-07 2…

Linux.软件操作

1.yum 命令 要连网 2.systemctl 命令控制软件的启动和关闭 3.ln 创建软连接 使用cat来找本体&#xff0c;看看链接生不生效 4.date 命令查看系统时间 格式化的时候可以用双引号把他们引出来 -d 对时间进行修改 修改时区 自动校准 手动校准 5.ifconfig 查看本机的ip地址 6.h…

SOA主要协议和规范

Web服务作为实现SOA中服务的最主要手段。首先来了解Web Service相关的标准。它们大多以“WS-”作为名字的前缀&#xff0c;所以统称“WS-*”。Web服务最基本的协议包括UDDI、WSDL和SOAP&#xff0c;通过它们&#xff0c;可以提供直接而又简单的Web Service支持&#xff0c;如图…

《机器学习特征提取》

书籍&#xff1a;Building Feature Extraction with Machine Learning: Geospatial Applications 作者&#xff1a;Bharath.H. Aithal&#xff0c;Prakash P.S. 出版&#xff1a;CRC Press 书籍下载-《机器学习特征提取》这是一本面向专业人士和研究生的实用指南&#xff0c…

Docker|了解容器镜像层(1)

引言 容器非常神奇。它们允许简单的进程表现得像虚拟机。在这种优雅的底层是一组模式和实践&#xff0c;最终使一切运作起来。在设计的根本是层。层是存储和分发容器化文件系统内容的基本方式。这种设计既出人意料地简单&#xff0c;同时又非常强大。在今天的帖子[1]中&#xf…

PS初级|写在纸上的字怎么抠成透明背景?

前言 上一次咱们讲了很多很多很多的抠图教程&#xff0c;这次继续。。。最近有小伙伴问我&#xff1a;如果是写在纸上的字&#xff0c;要怎么把它抠成透明背景。 这个其实很简单&#xff0c;直接来说就是选择通道来抠。但有一点要注意的是&#xff0c;写在纸上的字&#xff0…

elementui Menu 二级菜单 min-width修改无效

原因&#xff1a;可能是生成的二级菜单样式里面没有带特定的hash属性 而vue代码里面样式里带了 scoped生成的样式有改样式选择器 从而无法成功选择 解决&#xff1a;让样式可以全局选择 不带属性选择器 单文件组件 CSS 功能 | Vue.js :global(.el-menu--vertical .el-menu--p…

【Web API DOM11】节点操作

一&#xff1a;DOM节点 1 什么是DOM节点 DOM树里每一个内容都称为节点 2 DOM节点分类 元素节点 属性节点&#xff1a;a标签的href、img标签的src等 文本节点&#xff1a;标签中的文字 上图为整个DOM树&#xff0c;每个标签、以及标签属性、文本内容构成了DOM树 二&#…

码蹄集部分题目(2024OJ赛18期;并查集+ST表+贪心)

1&#x1f40b;&#x1f40b;史莱姆融合&#xff08;钻石&#xff1b;并查集&#xff09; 时间限制&#xff1a;1秒 占用内存&#xff1a;128M &#x1f41f;题目描述 &#x1f41f;题目思路 这道题目使用并查集&#xff0c;同一集合的所有元素的最顶上的祖父节点是统一的。…

[NOVATEK] NT96580行车记录仪功能学习笔记

一、u-Boot升级灯 运行u-Boot程序时LED灯闪烁,找到运行过程中一直在运行的函数在里面进行LED引脚电平的翻转 宏定义 Z:\SunFan\AHD580\pip\na51055_PIP\BSP\u-boot\include\configs\nvt-na51055-evb.h Z:\SunFan\AHD580\pip\na51055_PIP\BSP\u-boot\drivers\mtd\nvt_flash_…

【BOM02】本地存储

一&#xff1a;什么是本地存储 数据存储在用户浏览器中&#xff0c;用户设置、读取方便&#xff0c;同时页面刷新时不会丢失数据。存储在浏览器中数据约5M&#xff0c;分为sessionStorage和localStorage两种存储方式 二&#xff1a;localStorage存储 作用 将数据永久存储在…

idm2024最新完美破解版免费下载 idm绿色直装版注册机免费分享 idm永久激活码工具

IDM 2024破解版重新开发了调度程序和MMS协议支持、重新设计和增强的下载引擎、与所有最新浏览器的独特高级集成、改进的工具栏以及大量其他改进和新功能&#xff0c;这一全新的更新&#xff0c;使得IDM下载器更加完美。值得一提的是&#xff0c;它可以借助油猴浏览器的脚本&…

linux系统——route路由命令

route路由对linux内的ip路由表进行操作 计算机间的数据通信是通过网络来实现的&#xff0c;路由就是从源主机到目标主机的转发过程 路由分为静态路由与动态路由&#xff0c;linux中的均为静态路由&#xff0c;动态路由由交换机路由器自动分配规则而来

2022 hnust 湖科大 javaweb课设 数据库课设 报告+源代码+流程图文件+课设指导书+附赠数据库课堂实验指导书

2022 hnust 湖科大 javaweb课设 数据库课设 报告源代码流程图文件课设指导书附赠数据库课堂实验指导书 描述 湖南科技大学大二下学期先后开展java web和数据库课程设计&#xff0c;两个课设项目可以通用&#xff0c;老师一般会允许自拟选题&#xff0c;所以在此统一打包&…

关于使用南墙waf防护halo网站主页请求404报错的解决方案

文章目录 环境说明问题展示原因探究解决方法 环境说明 在1panel应用商店&#xff0c;部署南墙waf(docker版)halo(2.16.1社区版)注意部署过程中注意uuwaf必须勾选允许外部访问&#xff0c;halo可以不勾选[这里为了证明确实是南墙waf的原因&#xff0c;选择勾选] 问题展示 使…