Flutter: TextEditingValue的实现

文章目录

    • TextEditingValue
    • 一、fromJSON
    • 二、text、selection、composing、empty
    • 三、isComposingRangeValid
    • 四、replaced

TextEditingValue

/// The current text, selection, and composing state for editing a run of text.

class TextEditingValue {const TextEditingValue({this.text = '',this.selection = const TextSelection.collapsed(offset: -1),this.composing = TextRange.empty,});

创建用于编辑文本段落的信息。
选择范围和正在编辑的范围必须位于文本之内。这一点在构建时不会被检查,
必须由调用者确保。
[selection] 的默认值是 TextSelection.collapsed(offset: -1)。
这表示没有任何选择。

一、fromJSON

class TextEditingValue {// ... 省略大量代码factory TextEditingValue.fromJSON(Map<String, dynamic> encoded) {final String text = encoded['text'] as String;final TextSelection selection = TextSelection(baseOffset: encoded['selectionBase'] as int? ?? -1,extentOffset: encoded['selectionExtent'] as int? ?? -1,affinity: _toTextAffinity(encoded['selectionAffinity'] as String?) ?? TextAffinity.downstream,isDirectional: encoded['selectionIsDirectional'] as bool? ?? false,);final TextRange composing = TextRange(start: encoded['composingBase'] as int? ?? -1,end: encoded['composingExtent'] as int? ?? -1,);assert(_textRangeIsValid(selection, text));assert(_textRangeIsValid(composing, text));return TextEditingValue(text: text,selection: selection,composing: composing,);}

从 JSON 对象创建此类的实例。
final String text = encoded[‘text’] as String; : JSON 中提取文本内容
TextSelection :从 JSON 中提取选择范围信息并创建 TextSelection 对象
baseOffset: encoded['selectionBase'] as int? ?? -1, // 选择起始位置,默认为 -1
extentOffset: encoded['selectionExtent'] as int? ?? -1, // 选择结束位置,默认为 -1
affinity: _toTextAffinity(encoded['selectionAffinity'] as String?) ?? TextAffinity.downstream, // 选择的光标方向,默认为下游
isDirectional: encoded['selectionIsDirectional'] as bool? ?? false,// 选择是否为方向性选择,默认为 false

TextRange : 从 JSON 中提取正在编辑的范围信息并创建 TextRange 对象
final TextRange composing = TextRange(
start: encoded['composingBase'] as int? ?? -1, // 编辑范围起始位置,默认为 -1
end: encoded['composingExtent'] as int? ?? -1, // 编辑范围结束位置,默认为 -1
);
断言检查选择范围和编辑范围是否在文本范围内,如果_textRangeIsValid(selection, text)为false,assert(false)就抛出错误,不再往下执行。assert(true)不会抛出任何错误
assert(_textRangeIsValid(selection, text));
assert(_textRangeIsValid(composing, text));
如果检查没问题,就返回赋值后的TextEditingValue。
return TextEditingValue(text: text, selection: selection, composing: composing);

在Flutter(以及Dart)中,assert语句用于在调试模式下进行条件检查。如果条件为true,assert语句不会产生任何影响;它只是用来进行调试时的条件验证,并不会影响程序的运行流程。如果条件为false,则会抛出一个AssertionError。

二、text、selection、composing、empty

class TextEditingValue {// ... 省略大量代码/// text 是当前正在编辑的文本。final String text;/// selection是当前选中的文本范围。/// 当 [selection] 是一个 [TextSelection],并且其 baseOffset 和 extentOffset 相同且为非负数时,/// [selection] 属性表示光标的位置。/// 如果当前 [selection] 的 baseOffset 或 extentOffset 为负数,/// 则表示当前文本没有选中内容或光标位置,大多数依赖于当前选择的文本编辑操作/// (例如,在光标位置插入字符)将不会执行任何操作。
final TextSelection selection;/// 正在被组合输入的文本范围。/// 组合输入区域由输入法(IME)创建,用于指示某个范围内的文本是临时的。/// 例如,Android Gboard 应用的英文键盘会将光标下的当前单词放入组合输入区域,/// 以指示该单词可能会受到自动更正或预测更改的影响。/// 组合输入区域还可用于执行多阶段输入,这通常由为拼音键盘设计的输入法使用,/// 用于输入表意符号。例如,许多中日韩(CJK)键盘要求用户输入拉丁字母序列,/// 然后将其转换为 CJK 字符。在 iOS 上,默认的软件键盘没有专门的视图来显示未完成的拉丁字母序列,/// 因此它直接显示在文本字段中,位于组合输入区域内。/// 组合输入区域通常只应由输入法或用户通过与输入法的交互来更改。/// 如果此属性表示的文本范围是 [TextRange.empty],则表示当前没有文本正在被组合输入。final TextRange composing;/// 一个表示空字符串、没有选择且没有组合输入范围的值。static const TextEditingValue empty = TextEditingValue();
}

text: 当前编辑的文字
selection: 当前选中文字的范围
composing:正在被组合输入的文本范围。例如拼音输入法,在输入字母出现候选字还未确认选择的阶段,composing就为true,当确认选中后,composing就为false。下图就是composing: true的示例。
在这里插入图片描述
empty: 一个表示空字符串、没有选择且没有组合输入范围的值
使用:_textEditController.value = TextEditingVlaue.empty; 给一个输入赋值一个空值。

class TextEditingValue {// ... 省略大量代码TextEditingValue copyWith({String? text,TextSelection? selection,TextRange? composing,}) {return TextEditingValue(text: text ?? this.text,selection: selection ?? this.selection,composing: composing ?? this.composing,);}
}

创建一个当前 TextEditingValue 的副本,并允许部分字段被替换为新值。
在需要修改 TextEditingValue部分属性时,避免直接修改原对象,而是返回一个新的对象。这是不可变对象设计的常见模式。

参数:text、selection 和 composing 都是可选参数。如果调用者提供了新值,则使用新值;否则,保留当前对象的值。

空值合并运算符 (??):用于判断参数是否为 null。如果为 null,则使用当前对象的对应值。

返回新对象:通过 TextEditingValue 构造函数创建一个新对象,确保原对象不会被修改。

text: text ?? this.text, 如果提供了新的文本,则使用新文本,否则保留原文本
selection: selection ?? this.selection,如果提供了新的选择范围,则使用新范围,否则保留原范围
composing: composing ?? this.composing, 如果提供了新的组合输入范围,则使用新范围,否则保留原范围

三、isComposingRangeValid

class TextEditingValue {// ... 省略大量代码bool get isComposingRangeValid => composing.isValid && composing.isNormalized && composing.end <= text.length;
}

isComposingRangeValid :通过get 关键字,给 isComposingRangeValid 提供了一个只读的访问方式,使外部能直接读取isComposingRangeValid 值,而无法直接修改它。
用于判断 composing 范围是否text的有效范围。
当且仅当 composing 范围是规范化的、其起始位置大于或等于 0,并且其结束位置小于或等于 [text] 的长度时,返回 true
如果此属性为 false,而 [composing] 范围的 isValid 为 true,通常表示当前 [composing] 范围由于编程错误而无效。
composing.isValid:检查 composing 范围是否有效(即 start 和 end 都是非负数)。
composing.isNormalized:检查 composing 范围是否规范化(即 start <= end)。
composing.end <= text.length:检查 composing 的结束位置是否不超过文本的长度。

返回值:只有当所有条件都满足时,返回 true,否则返回 false。
composing 是 TextRange,在TextRange中有以下定义:

class TextRange {// ... 省略大量代码/// 范围的起始位置/// 如果 [start] 和 [end] 都为 -1,则表示一个空的文本范围final int start;/// 范围结束位置的下一个索引/// 如果 [start] 和 [end] 都为 -1,则表示一个空的文本范围final int end;/// 判断该范围是否表示文本中的有效位置bool get isValid => start >= 0 && end >= 0;/// 判断该范围是否为空(但仍可能位于文本中),未选中一段范围的文本。bool get isCollapsed => start == end;/// 判断该范围的起始位置是否在结束位置之前。bool get isNormalized => end >= start;
}

isCollapsed: true时,光标未选中范围,例如在“本”“。”前的光标,如下图:
在这里插入图片描述

四、replaced

class TextEditingValue {// ... 省略大量代码TextEditingValue replaced(TextRange replacementRange, String replacementString) {if (!replacementRange.isValid) {return this;}final String newText = text.replaceRange(replacementRange.start, replacementRange.end, replacementString);if (replacementRange.end - replacementRange.start == replacementString.length) {return copyWith(text: newText);}int adjustIndex(int originalIndex) {// The length added by adding the replacementString.final int replacedLength = originalIndex <= replacementRange.start && originalIndex < replacementRange.end ? 0 : replacementString.length;// The length removed by removing the replacementRange.final int removedLength = originalIndex.clamp(replacementRange.start, replacementRange.end) - replacementRange.start;return originalIndex + replacedLength - removedLength;}final TextSelection adjustedSelection = TextSelection(baseOffset: adjustIndex(selection.baseOffset),extentOffset: adjustIndex(selection.extentOffset),);final TextRange adjustedComposing = TextRange(start: adjustIndex(composing.start),end: adjustIndex(composing.end),);assert(_textRangeIsValid(adjustedSelection, newText));assert(_textRangeIsValid(adjustedComposing, newText));return TextEditingValue(text: newText,selection: adjustedSelection,composing: adjustedComposing,);}
}
  1. if (!replacementRange.isValid) { return this; }
    检查 replacementRange 是否有效:
    如果 replacementRange 无效(如 start 或 end 为负数),则直接返回当前对象,不做任何操作。

  2. final String newText = text.replaceRange(replacementRange.start, replacementRange.end, replacementString);
    替换文本:
    使用 text.replaceRange 方法将 replacementRange 指定的文本替换为 replacementString,生成新的文本 newText。

  3. 处理特殊情况:
    if (replacementRange.end - replacementRange.start == replacementString.length) { return copyWith(text: newText); }
    如果替换前后的文本长度相同(即 replacementRange.end - replacementRange.start == replacementString.length),则直接调用 copyWith 方法返回新的 TextEditingValue,因为选择范围和组合输入范围不需要调整。

  4. int adjustIndex(int originalIndex) {}
    调整索引:
    定义 adjustIndex 方法,用于计算替换后 selection 和 composing 范围的新位置。
    final int replacedLength = originalIndex <= replacementRange.start && originalIndex < replacementRange.end ? 0 : replacementString.length;
    replacedLength:如果原始索引在替换范围内,则不增加长度;否则,增加 replacementString 的长度。
    final int removedLength = originalIndex.clamp(replacementRange.start, replacementRange.end) - replacementRange.start;
    removedLength:计算被替换文本的长度。
    调整公式:originalIndex + replacedLength - removedLength。当前索引 + 替换文本的长度 - 被替换删除的文本的长度 = 最终光标的索引下标

clamp 是 Dart 中用于限制一个数值在指定范围内的工具方法。它的定义如下:
int clamp(int lowerLimit, int upperLimit);
功能:将当前值限制在 lowerLimit 和 upperLimit 之间。
如果当前值小于 lowerLimit,则返回 lowerLimit
如果当前值大于 upperLimit,则返回 upperLimit
当前值处于lowerLimit~upperLimit之间,返回当前值
上述的originalIndex.clamp(replacementRange.start, replacementRange.end),表示返回替换文案在长度范围内的合法索引。

  1. final TextSelection adjustedSelection = TextSelection( baseOffset: adjustIndex(selection.baseOffset), extentOffset: adjustIndex(selection.extentOffset), );
    通过adjustIndex计算 selection 和 composing 的最终索引范围,并通过TextSelection重新赋值。

  2. 断言检查:_textRangeIsValid(adjustedSelection, newText) 是否为true
    使用 assert 检查调整后的 selection 和 composing 范围是否有效。

  3. 返回新对象
    返回一个新的 TextEditingValue,包含替换后的文本、调整后的选择范围和组合输入范围。

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

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

相关文章

Web开发:ORM框架之使用Freesql的导航属性

一、什么时候用导航属性 看数据库表的对应关系&#xff0c;一对多的时候用比较好&#xff0c;不用多写一个联表实体&#xff0c;而且查询高效 二、为实体配置导航属性 1.给关系是一的父表实体加上&#xff1a; [FreeSql.DataAnnotations.Navigate(nameof(子表.子表关联字段))]…

RAG(检索增强生成)原理、实现与评测方法探讨

RAG是什么&#xff1f; 看一下RAG的英文全称&#xff1a;Retrieval-Augmented Generation&#xff0c;建索、增强、生成&#xff1b;一句话串起来就是通过检索增强模型的生成&#xff0c;是的&#xff0c;这就是RAG。 RAG怎么做&#xff1f; 目前比较通用的套路是这样的&#x…

【嵌入式Linux应用开发基础】网络编程(4):UDP协议

目录 一、UDP 协议概述 二、UDP 协议特点 三、UDP协议的字段格式 四、UDP协议的数据传输过程 五、嵌入式UDP编程核心API 六、UDP 在嵌入式 Linux 中的编程实现 6.1 UDP 服务器代码示例 6.2 UDP 客户端代码示例 七、UDP 协议的应用场景 八、UDP 协议的优缺点 8.1 优点…

视频字幕识别和翻译

下载的视频很多不是汉语的&#xff0c;我们需要用剪映将语音识别出来作为字幕压制到视频中去。 剪映6.0以后语音识别需要收费&#xff0c;但是低版本还是没有问题。 如果想要非汉语字幕转成中文&#xff0c;剪映低版本不提供这样功能。但是&#xff0c;用剪映导出识别字幕&am…

小迪安全-24天-文件管理,显示上传,黑白名单,访问控制

上节课回顾&#xff0c;token问题 没有更新token值&#xff0c;造成了复用 加上这段代码就好了&#xff0c;就不会复用了 文件管理-文件上传 upload.html文件&#xff0c;找ai生成就行 uoload.php接受文件上传的信息 这里在写个临时文件存储换个地方 因为上面临时文件存在c盘…

单入单出队列性能优化(Lock-Free)

摘要&#xff1a;文中首先介绍了有锁线程安全循环队列的基本实现&#xff0c;然后探讨了使用原子变量实现 Lock-Free 队列的优势&#xff0c;能够减少线程之间的数据竞争。接着&#xff0c;介绍了数据对齐的策略&#xff0c;以降低伪共享的概率&#xff0c;随后引入了索引缓存来…

java项目之网络游戏交易系统源码(ssm+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的网络游戏交易系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 本网络游戏交易系统分为管理员…

PyTorch 源码学习:GPU 内存管理之深入分析 CUDACachingAllocator

因引入 expandable_segments 机制&#xff0c;PyTorch 2.1.0 版本发生了较大变化。本文关注的是 PyTorch 原生的 GPU 内存管理机制&#xff0c;故研究的 PyTorch 版本为 2.0.0。代码地址&#xff1a; c10/cuda/CUDACachingAllocator.hc10/cuda/CUDACachingAllocator.cpp 更多内…

【PromptCoder】使用 package.json 生成 cursorrules

【PromptCoder】使用 package.json 生成 cursorrules 在当今快节奏的开发世界中&#xff0c;效率和准确性至关重要。开发者们不断寻找能够优化工作流程、帮助他们更快编写高质量代码的工具。Cursor 作为一款 AI 驱动的代码编辑器&#xff0c;正在彻底改变我们的编程方式。但如…

学习路程五 向量数据库Milvus操作

前序 前面安装好了docker且成功拉取Milvus镜像&#xff0c;启动。通过python成功连接上了数据。接下来就继续更多Milvus的操作 在开始之前&#xff0c;先来简单了解一下向量数据库内一些东西的基本概念 概念描述数据库&#xff08;Database&#xff09;类似与MySQL的database…

SpringBoot 热部署

1、添加 DevTools 依赖 <!-- 热部署依赖 --> <dependency> <groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId> </dependency>2、在IDEA的菜单栏中依次选择“File”→“Settings”&#x…

SOC-ATF 安全启动BL1流程分析(1)

一、ATF 源码下载链接 1. ARM Trusted Firmware (ATF) 官方 GitHub 仓库 GitHub 地址: https://github.com/ARM-software/arm-trusted-firmware 这是 ATF 的官方源码仓库&#xff0c;包含最新的代码、文档和示例。 下载方式&#xff1a; 使用 Git 克隆仓库&#xff1a; git…

汽车无钥匙进入一键启动操作正确步骤

汽车智能无钥匙进入和一键启动的技术在近年来比较成熟&#xff0c;不同车型的操作步骤可能略有不同&#xff0c;但基本的流程应该是通用的&#xff0c;不会因为时间变化而有大的改变。 移动管家汽车一键启动无钥匙进入系统通常是通过携带钥匙靠近车辆&#xff0c;然后触摸门把…

excel单、双字节字符转换函数(中英文输入法符号转换)

在Excel中通常使用函数WIDECHAR和ASC来实现单、双字节字符之间的转换。其中 WIDECHAR函数将所有的字符转换为双字节&#xff0c;ASC函数将所有的字符转换为单字节 首先来解释一下单双字节的含义。单字节一般对应英文输入法的输入&#xff0c;如英文字母&#xff0c;英文输入法…

IP----访问服务器流程

这只是IP的其中一块内容-访问服务器流程&#xff0c;IP还有更多内容可以查看IP专栏&#xff0c;前一段学习内容为IA内容&#xff0c;还有更多内容可以查看IA专栏&#xff0c;可通过以下路径查看IA-----配置NAT-CSDN博客CSDN,欢迎指正 1.访问服务器流程 1.分层 1.更利于标准化…

Ubutu部署WordPress

前言 什么是word press WordPress是一种使用PHP语言开发的建站系统&#xff0c;用户可以在支持PHP和MySQL数据库的服务器上架设WordPress。它是一个开源的内容管理系统&#xff08;CMS&#xff09;&#xff0c;允许用户构建动态网站和博客。现在的WordPress已经强大到几乎可以…

LangChain构建行业知识库实践:从架构设计到生产部署全指南

文章目录 引言:行业知识库的进化挑战一、系统架构设计1.1 核心组件拓扑1.2 模块化设计原则二、关键技术实现2.1 文档预处理流水线2.2 混合检索增强三、领域适配优化3.1 医学知识图谱融合3.2 检索结果重排序算法四、生产环境部署4.1 性能优化方案4.2 安全防护体系五、评估与调优…

Lua的table(表)

Lua表的基本概念 Lua中的表&#xff08;table&#xff09;是一种多功能数据结构&#xff0c;可以用作数组、字典、集合等。表是Lua中唯一的数据结构机制&#xff0c;其他数据结构如数组、列表、队列等都可以通过表来实现。 表的实现 Lua的表由两部分组成&#xff1a; 数组部分…

应对现代生活的健康养生指南

在科技飞速发展的现代社会&#xff0c;人们的生活方式发生了巨大改变&#xff0c;随之而来的是一系列健康问题。快节奏的生活、高强度的工作以及电子产品的过度使用&#xff0c;让我们的身体承受着前所未有的压力。因此&#xff0c;掌握正确的健康养生方法迫在眉睫。 针对久坐不…

使用DeepSeek/chatgpt等AI工具辅助网络协议流量数据包分析

随着deepseek,chatgpt等大模型的能力越来越强大&#xff0c;本文将介绍一下deepseek等LLM在分数流量数据包这方面的能力。为需要借助LLM等大模型辅助分析流量数据包的同学提供参考&#xff0c;也了解一下目前是否有必要继续学习wireshark工具以及复杂的协议知识。 pcap格式 目…