Flutter TextField 交互实例 —— 新手礼包

十念  论坛元老 | 2024-11-3 15:53:32 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1022|帖子 1022|积分 3066


大家好,我是 17。
新手礼包一共 3 篇文章,每篇都是形貌尽量详细,实例讲解,包会!


  • Flutter Row 实例 —— 新手礼包
  • Flutter TextField UI 实例 —— 新手礼包
  • Flutter TextField 交互实例 —— 新手礼包
本篇包罗所有常见 TextField 交互示例。
设置初始值

在上一篇 Flutter TextField UI 实例 中第一个示例中已经给出了全部代码,并准备好了 controller。
我们梳理一下设置初始值需要的步骤,需要两步完成设置初始值。

  • 初始化 controller var controller = TextEditingController(text: "IAM17");
  • 把 controller 赋值给 TextField
  1. TextField(
  2.      controller: controller,
  3. );
复制代码
完成这两步后,在 TextField 中自就会有文本 “IAM17”。
获取和修改 TextField 内容

有两个办法可以拿到内容。

  • 监听 onChange
  1. TextField(
  2.      controller: controller,
  3.      onChanged: (value) {
  4.        print(value);
  5.      },
  6. );
复制代码
  通过 controller 修改文本不会触发 onChange
  

  • 用 controller 拿到 value。
  1. Column(mainAxisSize: MainAxisSize.min, children: [
  2.    ElevatedButton(
  3.        onPressed: () {
  4.          print(controller.text);
  5.        },
  6.        child: Text('获取内容')),
  7.    TextField(
  8.      controller: controller,
  9.    )
  10. ]);
复制代码
或者当文本发生改变时获取文本
  1. @override
  2.   void initState() {
  3.     controller.addListener(() {
  4.       print(controller.text);
  5.     });
  6.     super.initState();
  7. }
复制代码

  • 用 controller 可以随时修改 TextField 内容
好比可以随时清空 TextField。
  1. Column(mainAxisSize: MainAxisSize.min, children: [
  2.       ElevatedButton(
  3.           onPressed: () {
  4.             controller.text='';
  5.           },
  6.           child: Text('清空')),
  7.       TextField(
  8.         controller: controller,
  9.       )
  10. ]);
复制代码
清空内容还可以用 controller.clear()。
   注意:controller 必须在 disapose 方法中销毁。
  1. @override
  2.   void dispose() {
  3.     controller.dispose();
  4.     super.dispose();
  5.   }
复制代码
控制 TextField Focus

自动获得焦点

  1. TextField(
  2.       autofocus: true,
  3. );
复制代码
手动让一个 TextField 获得或失去焦点

手动获得焦点分四步

  • 创建 focusNode。
  • 把 focusNode 赋值给 TextField。
  • 手动触发 focusNode 的 previousFocus、unfocus 方法获得和失去焦点。
  • 在 dispose 中销毁 focusNode。
  1. class MyWidget extends StatefulWidget {
  2.   const MyWidget({super.key});
  3.   @override
  4.   State<MyWidget> createState() => _MyWidgetState();
  5. }
  6. class _MyWidgetState extends State<MyWidget> {
  7.   FocusNode focusNode = FocusNode(); //第一步
  8.   @override
  9.   void dispose() {
  10.     focusNode.dispose(); //第四步
  11.     super.dispose();
  12.   }
  13.   @override
  14.   Widget build(BuildContext context) {
  15.     return Column(
  16.       mainAxisSize: MainAxisSize.min,
  17.       children: [
  18.         TextField(
  19.           focusNode: focusNode, // 第二步
  20.         )
  21.         ElevatedButton(
  22.             onPressed: () {  // 第三步
  23.               focusNode.hasFocus
  24.                   ? focusNode.unfocus()
  25.                   : focusNode.requestFocus();
  26.             },
  27.             child: Text('变换焦点')),        
  28.       ],
  29.     );
  30.   }
  31. }
复制代码
如果是在手机上实行代码,会看到 TextField 获取焦点的时间会弹出软键盘,失去焦点的时间,软键盘收起。
监听获取焦点和失去焦点

因为 focusNode 混入了 ChangeNotifier,以是可以直接监听他的状态变化。
  1. class _MyWidgetState extends State<MyWidget> {
  2.   var focusNode = FocusNode();
  3.   @override
  4.   void initState() {
  5.     focusNode.addListener(() {
  6.       print(focusNode.hasFocus);
  7.     });
  8.     super.initState();
  9.   }
  10.   @override
  11.   void dispose() {
  12.     focusNode.dispose();
  13.     super.dispose();
  14.   }
  15.   @override
  16.   Widget build(BuildContext context) {
  17.     return TextField(
  18.       focusNode: focusNode,
  19.     );
  20.   }
  21. }
复制代码
控制焦点在差别 TextField 间切换

两个 TextField,第一个自动获得焦点,点击按钮,焦点从第一个 TextField 切换到第二个,再次点击,焦点从第二个 TextField 回到第一个。
  1. class MyWidget extends StatefulWidget {
  2.   const MyWidget({super.key});
  3.   @override
  4.   State<MyWidget> createState() => _MyWidgetState();
  5. }
  6. class _MyWidgetState extends State<MyWidget> {
  7.   var focusNode1 = FocusNode();
  8.   var focusNode2 = FocusNode();
  9.   @override
  10.   Widget build(BuildContext context) {
  11.     return Column(
  12.       mainAxisSize: MainAxisSize.min,
  13.       children: [
  14.         TextField(
  15.           autofocus: true,
  16.           focusNode: focusNode1,
  17.         ),
  18.         const SizedBox(
  19.           height: 10,
  20.         ),
  21.         TextField(
  22.           focusNode: focusNode2,
  23.           autofocus: true,
  24.         ),
  25.         const SizedBox(
  26.           height: 10,
  27.         ),
  28.         ElevatedButton(
  29.             onPressed: () {
  30.               if (focusNode1.hasFocus) {
  31.                 focusNode1.nextFocus();
  32.               } else {
  33.                 focusNode2.previousFocus();
  34.               }
  35.             },
  36.             child:const Text('切换焦点')),
  37.       ],
  38.     );
  39.   }
  40. }
复制代码
这是两个 TextField 连续的情况,如果不连续呢?可以用 FocusScope!
把 onPressed 代码修改为:
  1. ElevatedButton(
  2.       onPressed: () {
  3.         if (focusNode1.hasFocus) {
  4.           FocusScope.of(context).requestFocus(focusNode2);
  5.         } else {
  6.           FocusScope.of(context).requestFocus(focusNode1);
  7.         }
  8.       },
  9.       child: const Text('切换焦点'))
复制代码
控制字母大小写的 textCapitalization

textCapitalization 用来控制字母的大小写。不常用,了解一下即可。


  • 每个单词的第一字母大写 TextCapitalization.words
  • 第个句子的第一个字母大写 TextCapitalization.sentences
  • 每个字母都大写 TextCapitalization.characters
  • 每个字母默认小写 TextCapitalization.none
可能会用到的是 TextCapitalization.characters,把输入的所有小写字母转换为大写。
  1. TextField(
  2.      textCapitalization: TextCapitalization.characters,
  3. ),
复制代码
keyboardType 控制键盘

显示数字键盘TextInputType.number

  1. TextField(
  2.        keyboardType: TextInputType.number
  3. );
复制代码
不显示键盘 TextInputType.none

当 TextField 获得焦点后,不弹出键盘。
更多范例请见 TextInputType
以密码方式显示

  1. TextField(
  2.      obscureText:true
  3. );
复制代码
一样平常会加一个象征密码的图标
  1. TextField(
  2.      obscureText:true,
  3.      decoration: InputDecoration(
  4.        prefixIcon: Icon(Icons.lock)
  5.      ),
  6. );
复制代码
键盘的操作按钮范例 TextInputAction

默认情况下为 TextInputAction.done。TextField 设置为多行时为 TextInputAction.newline。
   ios 不能设置 TextInputAction.done,默认为 null。
  看笔墨形貌可能不大好理解,还是用TextInputAction.search 举个例子吧。
   可能你的手机上显示的是一个搜索图标,
  
  1. TextField(
  2. onSubmitted: (value) {
  3.       print(value);
  4. },
  5. textInputAction: TextInputAction.search,
  6. )
复制代码
键盘上原来的 action button 的文本被替换为 搜索。点搜索会触发提交,onSubmitted 被调用。
固然TextInputAction.search 表明点击按钮后应该进行搜索,但是否实行搜索由开发者决定。
我们再试一下 TextInputAction.newline 的举动。
  1. TextField(
  2.     maxLines: 3,
  3. );
复制代码
maxLines: 3 可以让 TextField 输入多行,点击 action button 会让 TextField 光标换行。
   Flutter 中存在 TextInputAction.newline ,但 Android 或 iOS 中不存在。引入这个术语的缘故起因是开发者可以实现插入新行的通用结果,而无需了解 Android 上的各种 IME 操作和 iOS 上的返回键。
  默认的 tooltip 提示语为英文?

如果你新建了一个项目,还没有做国际化,直接用 TextField,tooltip 的提示语是英文。可以这样操作一下:长按 TextField 输入框,这时会弹出一个提示框上面写着 Paste。
这是因为没有设置国际化的缘故起因。
想要使用 flutter_localizations 的话,我们需要在 pubspec.yaml 文件中添加它作为依赖:
  1. dependencies:
  2.   flutter:
  3.     sdk: flutter
  4.   flutter_localizations:
  5.     sdk: flutter
复制代码
  1. MaterialApp(
  2. supportedLocales: [
  3.     Locale("en"),
  4.     Locale("zh")
  5.   ],
  6.   localizationsDelegates: [
  7.     GlobalMaterialLocalizations.delegate,
  8.     GlobalWidgetsLocalizations.delegate,
  9.     GlobalCupertinoLocalizations.delegate
  10.   ],
  11.   )
复制代码
maxLength 与 counterText

  1. TextField(
  2.      maxLength: 6,
  3. ),
复制代码
counterText 就是显示在右下角的文本。当指定 maxLength 时,会自动显示 counterText,当达到最大字数时,无法再输入。可以用maxLengthEnforcement: MaxLengthEnforcement.none 改变这种限定,字符数超过 maxLength 后也能继承输入。不过,超过限定后,默认会显示赤色的 errrorBorder 和 赤色的 errorText
  1. TextField(
  2.       maxLength: 3,
  3.       maxLengthEnforcement: MaxLengthEnforcement.none);
复制代码
maxLengthEnforcement 一共有三个可选值。


  • 没有限定 MaxLengthEnforcement.none
  • 严格限定,超出后无法再输入 MaxLengthEnforcement.enforced
  • 纵然在达到最大长度限定后,如果当前值正在撰写,用户仍旧可以输入文本。合成竣事后,该值将被截断 MaxLengthEnforcement.truncateAfterCompositionEnds
第三条我表明一下。对于中文来说,我们用拼音打字的时间,起首会输入一些字母,这些字母会出现在输入框中,出现想要的汉字后再确认(也可能会自动上屏),让汉字出现在输入框中。字符数超出 maxLength 的时间,第三条允许这些字母出现在输入框中,第二条不允许。
我们用 chrome 欣赏查看下效果。打开 chrome 欣赏器,输入法调到拼音。要想在地点栏中输入天,需要输入字母 tian,当你输入 t 的时间,t 就已经出现在地点栏中了。随着字母 i,a,n 的输入,候选汉字不停变更,当出现你想到的汉字的时间,按空格(或是其它的上屏按键),地点栏中的字母消失,被 天 替代。如果 maxLength 是 1,MaxLengthEnforcement.enforced 只允许显示 字母 t,MaxLengthEnforcement.truncateAfterCompositionEnds 允许显示全部的 tian,这对于汉字的输入是非常有用的,默认情况下,TextField 的 MaxLengthEnforcement 就是 MaxLengthEnforcement.truncateAfterCompositionEnds。

固然是用 chrome 欣赏器地点栏举的例子,TextField 也是一样的。可以用 web 方式在 chrome 中查看 TextField 的表现,效果是一样的。
当 maxLength 为 -1 的时间,表示没有最大限定,输入多少个字符都可以。counterText 只显示当前字符数,不显示最大值。-1 用 TextField.noMaxLength 表示。
  1. TextField(
  2.      maxLength: TextField.noMaxLength ,
  3. ),
复制代码
自定义 counterText 样式

默认的 counterText 是很小的,我们想把他变大一些。
两种方法,一个是用 theme,我们这次用局部 theme(相当于 css 中的局部样式表)。先获得父级样式,用 copywith 方法,把新的样式补充进来。当 counterStyle 为 null 的时间,counterText 采用 helperStyle,要想同时影响到 helperText,也可以定义 helperStyle。
  1.     var themeData = Theme.of(context);
  2.     var decorationTheme = themeData.inputDecorationTheme;
  3.     return Theme(
  4.       data: themeData.copyWith(
  5.           inputDecorationTheme:
  6.               decorationTheme.copyWith(counterStyle: TextStyle(fontSize: 20))),
  7.       child: TextField(
  8.         maxLength: 6
  9.       ),
  10. );
复制代码
另一个方法是直接用 counterStyle
  1. TextField(
  2.       maxLength: 6,
  3.       decoration: InputDecoration(counterStyle: TextStyle(fontSize: 20)),
  4. );
复制代码
当 maxLengthEnforcement: MaxLengthEnforcement.none ,输入字数超过最大字符数后,border ,counterText 都显示为赤色。这个时间 counterText 的样式是 errorStyle 控制的。我们可以修改为其它样式。errorStyle 还会影响 errorText 的样式。
我们定义错误时 counterText 显示为绿色。
  1. InputDecoration(
  2.       errorStyle: TextStyle(color: Colors.green),
  3. );
复制代码
自定义 counterText

maxLength 设为 3,我们要实现下面的效果:字符数没有超过 3 时 currentCount 样式为玄色,超过 3 时显示为赤色。
  1. TextField(
  2.       style: const TextStyle(color: Color(0xFFC45F84), fontSize: 24),
  3.       maxLength: 3,
  4.       maxLengthEnforcement: MaxLengthEnforcement.none,
  5.       decoration: const InputDecoration(counterStyle: TextStyle(fontSize: 20)),
  6.       buildCounter: (context,
  7.           {required  currentLength, required bool  isFocused, maxLength}) {
  8.         return Text.rich(TextSpan(children: [
  9.           TextSpan(
  10.               text: '$currentLength',
  11.               style:  TextStyle(color:currentLength>maxLength!? Colors.red:Colors.black)),
  12.           const TextSpan(text: ' / '),
  13.           TextSpan(text: '$maxLength')
  14.         ]));
  15.       },
  16.     );
复制代码
用 buildCounter 天生 counterText ,当字符数超过最大限定数后,TextField 不再显示错误状态,border 显示为 focusBorder 样式。
用 Text.rich ,包罗多个 TextSpan,以便加差别的样式。上面的代码实现了所有的功能,但是有一个隐患,当 maxLength 为TextField.noMaxLength 的时间,maxLength 为 -1,不应该直接显示,应该处理一下:当 maxLength 为 -1 的时间,直接不显示了。
  1. if (maxLength >= 0) const TextSpan(text: ' / '),
  2. if (maxLength >= 0) TextSpan(text: '$maxLength')
复制代码
decoration 的 counterText 与 counter 属性

用 buildCounter 自定义 counterText 完全没有题目,为什么还要弄出这两个属性来呢?我们看下他们的作用。


  • 当 counterText 不为 null ,buildCounter 被忽略。
  • 当 counter 不为 null,counterText 被忽略。
由此可见,如果不想显示 countText,设置 counterText: '' 和定义 buildCounter 的效果一样,字符超出最大限定后,TextField 也不再显示错误状态,border 显示为 focusBorder 样式。
  1. TextField(
  2.       decoration: InputDecoration(
  3.         counterText: ''
  4.       ),
  5. );
复制代码
如果要用 counter ,counterText 显示内容的话,不得当动态显示字符数,固然能实现,但不如用 buildCounter 方便。如果非要显示内容的话,可以放一些提示语什么的,类似于 helperText。
设置 TextField 的显示行数,maxLines 与 maxLines

默认显示一行,超出后横向滚动。不能换行。
调解默认 TextField 默认显示行数的参数有两个 minLines,maxLines。 他们规定了初始显示的行数,但并不限定可以输入的最大行数。
设置 maxLines: 3 ,TextField 默认 显示 3 行,可以从第一行输入,连续输入三行,过超三行,还可以输入,但会坚向滚动。
设置 maxLines: null ,TextField 默认 显示 1 行,可以从第一行输入,连续输入第二行,第三行,无限行,超过束缚的最大高度后,坚向滚动。
设置 minLines:2, maxLines:3 ,TextField 默认显示 2 行,可以从第一行输入,连续输入三行,,超过 三行,坚向滚动。
   如果设置了 minLines,必须同时设置 maxLines。
  自定义验证

  1. abstract class TextInputFormatter {
  2.   
  3.   TextEditingValue formatEditUpdate(
  4.     TextEditingValue oldValue,
  5.     TextEditingValue newValue,
  6.   );
  7.   static TextInputFormatter withFunction(
  8.     TextInputFormatFunction formatFunction,
  9.   ) {
  10.     return _SimpleTextInputFormatter(formatFunction);
  11.   }
  12. }
复制代码
TextInputFormatter 是一个抽象类,当需要定义本身的子类的时间,只需要 override formatEditUpdate 方法。formatEditUpdate 可以修改 text 的值,好比克制不被允许的值。 withFunction 是一个快捷函数,让我们直接定义 text 的逻辑,而不必定义新的类。
好比只允许输入 0-9
  1. TextField(
  2.       inputFormatters: [
  3.         TextInputFormatter.withFunction(
  4.           (TextEditingValue oldValue, TextEditingValue newValue) {
  5.             return RegExp(r'^[0-9]*$').hasMatch(newValue.text)
  6.                 ? newValue
  7.                 : oldValue;
  8.           },
  9.         ),
  10.       ],
  11. );
复制代码
如果再限定一下长度这样写RegExp(r'^[0-9]{0,10}$') ,只能输入 0 到 10 个数字。
Flutter 为 TextInputFormatter 实现了两个子类,我们可以直接用。


  • FilteringTextInputFormatter
  • LengthLimitingTextInputFormatter
TextField 的 maxLength 功能就是用 LengthLimitingTextInputFormatter 实现的。
FilteringTextInputFormatter 用来过滤字符。
  1. FilteringTextInputFormatter(
  2.     this.filterPattern, {
  3.     required this.allow,
  4.     this.replacementString = '',
  5. });
复制代码
filterPattern 我们一样平常用 RegExp,就是正则来判断是否匹配。allow 如果为 true,filterPattern 是用于匹共同法的文本,如果为 false,匹配非法的文本。replacementString 是用来替换非法文本的替换字符串。
FilteringTextInputFormatter 有两个定名构造函数,对 allow 进行了赋值,一样平常情况下,我们优先使用这两个构造函数,让代码更可读。
  1.   FilteringTextInputFormatter.allow(
  2.     Pattern filterPattern, {
  3.     String replacementString = '',
  4.   }) : this(filterPattern, allow: true, replacementString: replacementString);
  5.   FilteringTextInputFormatter.deny(
  6.     Pattern filterPattern, {
  7.     String replacementString = '',
  8.   }) : this(filterPattern, allow: false, replacementString: replacementString);
复制代码
举个例子,只允许输入 0-9
  1. TextField(
  2.       inputFormatters: [FilteringTextInputFormatter.allow(RegExp('r[0-9]'))],
  3. );
复制代码
再好比不允许输入 x
  1. TextField(
  2.       inputFormatters: [FilteringTextInputFormatter.deny(RegExp('x'))],
  3.     );
复制代码
你可能疑问 deny 的参数不是 Patten 吗?怎么能输入字符串?答案是 String 实现了 Patten 接口
  1. abstract class String implements Comparable<String>, Pattern
复制代码
替换的功能就不举例子了,可以本身试试。如果需要替换的时间,优先思量用 FilteringTextInputFormatter。
如果你的验证逻辑很常用,可以 extends 一个子类出来,把逻辑封装起来。还是用只能输入 0-9 这个例子:
  1. class MyFormatter extends TextInputFormatter {
  2.   @override
  3.   TextEditingValue formatEditUpdate(
  4.       TextEditingValue oldValue, TextEditingValue newValue) {
  5.     return RegExp(r'^[0-9]*$').hasMatch(newValue.text) ? newValue : oldValue;
  6.   }
  7. }
复制代码
用的时间就会很方便
  1. TextField(
  2.     inputFormatters: [MyFormatter()],
  3. );
复制代码
onEditingComplete 与 onSubmitted

这两个事件是同时触发的,onEditingComplete 在前,onSubmitted 在后。
如果 onEditingComplete 不设置,默承认能让 TextField 失去焦点。如果不为空,就走 onEditingComplete 的逻辑。
onSubmitted 可以获得 TextFile 的 text。
在下面的例子中,固然 onEditingComplete 函数体为空,但毕竟不是 null,乐成制止了默认的可能让 TextField 失去焦点的举动。在 onSubmitted 中可以打印 TextField 中的文本。
  1. TextField(
  2.       onEditingComplete: () {},
  3.       onSubmitted: (value) {
  4.         print(value);
  5.       },
  6. );
复制代码
选中文本

选中文本 还是比力简朴的,指定 TextSelection 的开始位置 baseOffset,和竣事位置 extentOffset 即可。
下面的例子中,点击按钮会选中全部已经输入的文本。
  1. Column(
  2.       children: [
  3.         TextField(
  4.           controller: controller,
  5.           style: const TextStyle(color: Color(0xFFC45F84), fontSize: 24),
  6.         ),
  7.         ElevatedButton(
  8.             onPressed: () {
  9.               controller.selection = TextSelection(
  10.                   baseOffset: 0, extentOffset: controller.text.length);
  11.             },
  12.             child: Text('选中全部文本'))
  13.       ],
  14. );
复制代码
最后要了解的是 当 TextField 获得焦点时,它会制止本身通过 AutomaticKeepAliveClientMixin.wantKeepAlive 进行 dispose,以避免丢失 selection。移除焦点将允许它被disposed。
Flutter TextField 交互实例 到这里就竣事了,谢谢观看。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

十念

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表