全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2022-10-14_Flutter — 文本为什么可以被编辑?如何自定义编辑的行为?

您的位置:首页 >> 新闻 >> 行业资讯

Flutter — 文本为什么可以被编辑?如何自定义编辑的行为? ??本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究! 通过阅读本文,您将了解到 知道在Flutter中关于文本的整体逻辑;可编辑文本包含哪些内容;如何自定义可编辑行为;如何优雅的实现文本表单。前言:在上一篇文章中,我们讲解了Flutter文本的组成部分和Flutter 文本渲染到屏幕上的逻辑。文本的输出我们已经分析完成了,那么文本的输入又是怎么样的呢?在Flutter中,我们知道文本的输入可以通过TextField等组件将文字输入到App中,但是它背后的原理是什么呢,为什么可以编辑文本呢?在这一篇文章中,就让我们从Flutter的可编辑文本的实现原理,再到自定义可编辑的文本...希望能对你认识Flutter的文本编辑有所帮助。 注:本文的涉及较多文本编辑的核心逻辑,和大量的功能实践,建议收藏! TextField背后的存在在开始具体的分析前,大家可以先看下上面这张流程图,如果你和我一样,好奇Flutter的文本渲染和文本编辑之间有哪些联系,那么当你看完上图后会发现,从TextPainter开始就是相同的了,这也意味着,我们可以只分析TextPainter上层的部分。 组件层 每当我们想要在Flutter中进行文本的输入或者编辑时,我们通常会首先想到TextField这个组件,除了iOS和macOS外的系统都会使用它,它是属于Material库的一部分,和它对应的是Cupertino库中的CupertinoTextField。 2.png3.png除了这两个组件,大家可能还会想到TextFormField这个组件,但是它其实只是一个能帮助你更快速的实现一些类似保存逻辑的功能,它本质上还是TextField。 classTextFormFieldextendsFormFieldString{ TextFormField({}) returnUnmanagedRestorationScope( ... child:TextField( ..... ), }, } TextField和CupertinoTextField它们是有状态的组件。它们需要处理焦点、手势、鼠标悬停...等内容。但是无论是使用TextField还是CupertinoTextField最后都会创建EditableText。 来自TextField 类 - 材料库 - Dart API: EditableText,它是TextField核心的原始文本编辑控件。EditableText小部件很少直接使用,除非您正在实现完全不同的设计语言,例如 Cupertino。 当本文写到这里时,发现郭哥已经很详细的分析了TextField的内部原理,对于TextField的内部原理本文就不过多赘述了。 推荐阅读:Flutter 快速解析 TextField 的内部原理—@恋猫de小郭 如何自定义编辑的行为?自定义的编辑行为主要为下面几块部分 格式化输入框的数据自定义文本选中范围自定义光标位置通过TextInputFormatter格式化输入框的数据 在日常开发中,我们经常会碰到需要用户提交身份证信息,电话号码,银行卡号码等需求,在输入框中获取有效的格式化的数据是必不可少的。在自定义后,能使提交流程简化,大幅度减少错误信息,提高用户体验。 在Flutter中可以使用TextInputFormatter这个类获取到有效的格式化的数据,TextField可以使用它在编辑文本时纠正文本格式。 基本使用: Flutter提供了两个基础的TextInputFormatter FilteringTextInputFormatter— 创建一个格式化工具,常与正则表达式一起使用。LengthLimitingTextInputFormatter— 只允许输入一定数量的字符。TextFormField( inputFormatters:[ //.allow是只允许输入xx //.deny是不允许输入xx //FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z]')),不允许输入字母 FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z]')),//只允许输入字母 LengthLimitingTextInputFormatter(5)//只允许输入五个字符 ], ) 4.gif自定义TextInputFormatter 在日常开发时,仅仅只靠正则表达式是不够的,我们需要针对需求创建自定义的格式化工具。我们可以扩展TextInputFormatter实现formatEditUpdate方法来实现自定义的TextInputFormatter。 为了有更多的扩展性,此处实现一个格式化模板,只需传入xxx-xxxx-xxxx的手机号格式即可,若有其他需求,如银行卡号码等,只需传入它的格式。 classAllFormatterextendsTextInputFormatter{ finalStringmodel;//格式 finalString?separator;//识别格式后中间的分割字符 AllFormatter({ requiredthis.model, requiredthis.separator, //通过TextEditingValue可以读取和写入文本 @override TextEditingValueformatEditUpdate( TextEditingValueoldValue,TextEditingValuenewValue){ varoldText=oldValue.text; varnewText=newValue.text; //判断是否有输入文本 if(newText.isNotEmpty){ if(newText.lengtholdText.length){ if(newText.lengthmodel.length)returnoldValue; if(newText.lengthmodel.length&& model[newText.length-1]==separator){ returnTextEditingValue( //text代表用户输入后的文本(用户自己输入的,经过程序逻辑处理后的文本) text: "$oldText$separator${newText.substring(newText.length-1)}", //通过selection你可以知道当前所选择的光标位置和选择范围 selection: TextSelection.collapsed(offset:newValue.selection.end+ } } } returnnewValue; } } 使用 TextField( inputFormatters:[AllFormatter(model:"xxx-xxxx-xxxx",separator:'-')], ); 5.gif若需求改变只需要传入不同的model,和separator就可以了。 在开发需求中,碰到需要限制金额等,限制输入数字大小的需求时,我们也一样可以通过自定义TextInputFormatter来实现。设置好限制的大小后,如果输入的值超过这个数字,则值自动等于限制的大小。 classMaxInputFormatterextendsTextInputFormatter{ finaldoublemaxValue;//需要限制的大小 MaxInputFormatter({requiredthis.maxValue}); @override TextEditingValueformatEditUpdate( TextEditingValueoldValue,TextEditingValuenewValue){ StringnewText=newValue.text; //通过double.tryParse()检查字符串是否为数字字符串。 //如果返回值等于null,则输入不是数字字符串。 double?value=double.tryParse(newText); if(value==null){ returnTextEditingValue(text:newText,selection:newValue.selection); } if(valuemaxValue){ newText=maxValue.toString(); } returnTextEditingValue(text:newText,selection:newValue.selection); } } 6.gif通过TextInputFormatter我们可以很容易的实现各种格式化的工具,还有很多的功能大家可以自行探索。 自定义文本选中范围 自定义文本可以很大程度上提高用户的体验(前提是处理好的情况下),例如在一段长文本中,可以通过文本选中范围,快速定位到需要的文本,然后对进行复制、删除、修改...等功能。 在Flutter中,我们可以通过设置TextField的controller中的selection,来实现文本选中。 我们通过实现在一段文本中,快速定位选中姓名的例子,来看下怎样自定义文本选中范围。 intextent= intbase= selectText(Stringtext){ Stringname="Taxze"; //判断文本中是否有需要查找的内容 if(text.contains(name)){ //定位到出现内容的第一个位置 extent=text.indexOf(name); base=extent+name.length; } } WidgetselectionText()=TextField( controller:TextEditingController.fromValue( TextEditingValue( //设置内容 text:"HelloTaxze", //设置选中范围 selection:TextSelection( baseOffset:base, extentOffset:extent, ), ), ), ); 7.gif自定义选中文本范围使用恰当的话,我相信可以给用户带来更好的体验! 自定义光标位置 与自定义选中文本范围一样,自定义光标的位置也会有更多的体验。自定义光标和自定义选中范围类似,这里就不在多说了。 TextEditingValue( //设置内容 text:"HelloTaxze", selection:TextSelection.collapsed(offset:10),//设置光标位置 ), TextEditingValue 分析了这么多TextEditingValue的应用,现在来分析它本身。 TextEditingValue有三个属性: String text:TextField显示的默认值,相当于TextEditingController中的text。因为查看源码就可以发现,TextEditingController里的text最终将会赋值给TextEditingValue.text。 TextEditingController({String?text}) :super(text==null?TextEditingValue.empty:TextEditingValue(text:text)); TextSelection selection:通过它可以知道当前选择的光标位置和选择范围,通过它也可以设置光标在换行时的精确位置。 TextRange composing:当前编辑单词的偏移量,当你输入某些文本时,它的下方会有下划线,同时,系统键盘的上方会有建议的文本,点击建议的文本即可替换下划线的文本。 可编辑的文本包含哪些内容呢?我们已经知道在Flutter中,无论是使用TextField还是CupertinoTextField最后都会创建EditableText。也就是因为这个EditableText它将其他的可编辑的模块都集成了后,才能与系统键盘进行通信,才能在编辑文本时,出现光标、选中文本、可垂直滚动文本... ①具有样式、结构(文本高度)、文本对齐方式、本地化EditableText中,具有样式: //可以重写此方法以自定义文本的外观。 TextSpanbuildTextSpan({requiredBuildContextcontext,TextStyle?style,requiredboolwithComposing}){ if(!value.isComposingRangeValid||!withComposing){ returnTextSpan(style:style,text:text); } finalTextStylecomposingStyle=style?.merge(constTextStyle(decoration:TextDecoration.underline)) ??constTextStyle(decoration:TextDecoration.underline); returnTextSpan( 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)), ], } 通过StrutStyle已确保输入的文本符合分配的空间 StrutStylegetstrutStyle{ //如果为空,将继承style if(_strutStyle==null){ returnStrutStyle.fromTextStyle(style,forceStrutHeight:true); } return_strutStyle!.inheritFromTextStyle(style); } 文本具有对齐方式,默认为TextAlign.start,同时具有文本的方向textDirection,用于决定TextAlign.start或TextAlign.end的值。 EditableText({ super.key, ... this.textAlign=TextAlign.start, this.textDirection, ... }) 具有Locale,可以根据手机系统语言环境的不同,以不同的方式呈现文本。 ②具有文本布局EditableText的布局取决于maxLines、minLines和是否启用expands 如果最大行数为一(默认为一),则将在一行上水平滚动。如果最大行数为空,则设置为最小行数,并垂直增长。如果最大行数大于 1,则按照最小行数进行布局,并垂直一行行增加,直到达到最大行数。当达到其最大高度,它将垂直滚动。如果启用了扩展,它会根据传入的约束调整大小。staticTextInputType_inferKeyboardType({ requiredIterableString?autofillHints, requiredint?maxLines, }){ if(autofillHints==null||autofillHints.isEmpty){ returnmaxLines==1?TextInputType.text:TextInputType.multiline; } ... if(maxLines!=1){ returnTextInputType.multiline; } ... returninferKeyboardType[effectiveHint]??TextInputType.text; } 在EditableText的build方法中,嵌套了一层Scrollable,从而使文本可以垂直滚动显示多行文本,水平滚动以显示单行文本。 @override Widgetbuild(BuildContextcontext){ ... returnMouseRegion( ... child:Scrollable() ), } ③对文本的更改有完整的处理流程当文本的内容被更改时,EditableText首先会调用onChanged,通常会通知TextFiled去更改文本、光标或选择文本范围。 finalValueChangedString?onChanged; 然后当用户按下键盘上的搜索或者发送键时,会调用onEditingComplete,将用户输入的内容提交给controller。 注EditableText通过_finalizeEditing处理键盘的操作。 @override voidperformAction(TextInputActionaction){ switch(action){ ... //完成编辑(不代表用户结束了输入文本) _finalizeEditing(action,shouldUnfocus:false); break; } } 最后当用户确认输入完成后,调用onSubmitted(大部分情况下,onSubmitted会在onChanged后调用)。 ④当文本发生更改时,可通过updateEditingValue更新编辑的文本TextEditingValue?_lastKnownRemoteTextEditingValue; @override TextEditingValuegetcurrentTextEditingValue=_value; //用于处理文本的编辑更新 @override voidupdateEditingValue(TextEditingValuevalue){ if(!_shouldCreateInputConnection){ return; } if(widget.readOnly){ //如果是只读的模式下,只需要观察选择文本范围,其他都不用关心。 value=_value.copyWith(selection:value.selection); } _lastKnownRemoteTextEditingValue=value; if(value==_value){ //如果在输入一个数字后,删除它,这时候引擎会通知两次,所以需要一个判断。 return; } if(value.text==_value.textvalue.composing==_value.composing){ //当只有文本选择范围发生变化时 _handleSelectionChanged(value.selection,(_textInputConnection?.scribbleInProgress??false)?SelectionChangedCause.scribble:SelectionChangedCause.keyboard); }else{ ... } //无论发生了什么变化,都需要一个showCaretOnScreen,使用户能观察到文本发生的变化。 _scheduleShowCaretOnScreen(withAnimation:true); if(_hasInputConnection){ _stopCursorBlink(resetCharTicks:false); _startCursorBlink(); } } 如何更好的处理输入表单?表单是我们用于收集用户数据的重要方式,它在应用程序中是不可或缺的组件(不只是移动端)。在用户的登录/注册、地址填写、身份信息填写...等场景中有着很重要的作用,那么在Flutter中,如何使用Form类带来更好的用户体验呢? ①通过Globalkey保存表单状态Flutter Form组件是用于保存、验证表单文本的。 final_formKey=GlobalKeyFormState WidgetformText()=Form( key:_formKey, ... ); ②将TextFormField添加到表单中添加两个TextFormField,用于获取姓名和电话号码。 Form( key:_formKey, child:Column( children:WidGET@[ TextFormField( keyboardType: TextInputType.name, //当获取到焦点时,弹出的键盘类型,使其编辑框有更好的用于体验。 textInputAction:TextInputAction.next,//设置键盘右下角的操作按钮按钮,此处为→按钮 decoration:constInputDecoration( hintText:'请输入姓名', labelText:'Name',//当获取到焦点时显示 ), ), TextFormField( keyboardType:TextInputType.phone, textInputAction:TextInputAction.done,//此处为完成按钮 decoration:constInputDecoration( hintText:'请输入电话号码', labelText:'PhoneNumber', ), ), ], ), ) ③分配FocusNode,使表单可以提交数据final_formKey=GlobalKeyFormState //定义两个FocusNode finalFocusNode_nameFocusNode=FocusNode(); finalFocusNode_phoneFocusNode=FocusNode(); _nextFocus(FocusNodefocusNode){ //点击键盘上的next按钮,之间聚焦到下个焦点的输入框,提高用户体验 FocusScope.of(context).requestFocus(focusNode); } _submitForm(){ //底部弹出完成SnackBar ScaffoldMessenger.of(context) .showSnackBar(constSnackBar(content:Text('完成'))); } WidgetformText()=Form( key:_formKey, child:Column( children:WidGET@[ TextFormField( keyboardType:TextInputType.name, textInputAction:TextInputAction.next, focusNode:_nameFocusNode, onFieldSubmitted:(Stringvalue){ _nextFocus(_phoneFocusNode);//点击按钮触发的回调 }, decoration:constInputDecoration( hintText:'请输入姓名', labelText:'Name', ), ), TextFormField( keyboardType:TextInputType.phone, textInputAction:TextInputAction.done, focusNode:_phoneFocusNode, onFieldSubmitted:(Stringvalue){ _submitForm(); }, decoration:constInputDecoration( hintText:'请输入电话号码', labelText:'PhoneNumber', ), ), ], ), ④验证数据在提交数据前,我们可以根据需求对数据进行验证,大家可以根据需求自己定义,例如: String_dataInput(Stringvalue){ if(value.trim().isEmpty){ return'此为必填项'; } return""; } 尾述在这篇文章中,我们知道了文本的编辑是包含了哪些内容,知道了如何自定义编辑的操作,也知道了如何更好的实现一个表单。但这也只是文本的输入编辑、文本的优化的冰山一角。在后续的文章中我也会和大家一起持续探索。希望这篇文章能对你有所帮助,有问题欢迎在评论区留言讨论~ 参考&推荐阅读Flutter中那些你需要知道的文本知识! Flutter 快速解析 TextField 的内部原理—@恋猫de小郭 阅读原文

上一篇:2023-08-31_当AI遇上品牌 , 对话式营销想象空间有多大 下一篇:2022-11-28_Android Compose 动画使用详解(八)Animatable的使用

TAG标签:

20
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价