qidao123.com技术社区-IT企服评测·应用市场
标题:
Flutter TextField 交互实例 —— 新手礼包
[打印本页]
作者:
十念
时间:
2024-11-3 15:53
标题:
Flutter TextField 交互实例 —— 新手礼包
大家好,我是 17。
新手礼包一共 3 篇文章,每篇都是形貌尽量详细,实例讲解,包会!
Flutter Row 实例 —— 新手礼包
Flutter TextField UI 实例 —— 新手礼包
Flutter TextField 交互实例 —— 新手礼包
本篇包罗所有常见 TextField 交互示例。
设置初始值
在上一篇 Flutter TextField UI 实例 中第一个示例中已经给出了全部代码,并准备好了 controller。
我们梳理一下设置初始值需要的步骤,需要两步完成设置初始值。
初始化 controller var controller = TextEditingController(text: "IAM17");
把 controller 赋值给 TextField
TextField(
controller: controller,
);
复制代码
完成这两步后,在 TextField 中自就会有文本 “IAM17”。
获取和修改 TextField 内容
有两个办法可以拿到内容。
监听 onChange
TextField(
controller: controller,
onChanged: (value) {
print(value);
},
);
复制代码
通过 controller 修改文本不会触发 onChange
用 controller 拿到 value。
Column(mainAxisSize: MainAxisSize.min, children: [
ElevatedButton(
onPressed: () {
print(controller.text);
},
child: Text('获取内容')),
TextField(
controller: controller,
)
]);
复制代码
或者当文本发生改变时获取文本
@override
void initState() {
controller.addListener(() {
print(controller.text);
});
super.initState();
}
复制代码
用 controller 可以随时修改 TextField 内容
好比可以随时清空 TextField。
Column(mainAxisSize: MainAxisSize.min, children: [
ElevatedButton(
onPressed: () {
controller.text='';
},
child: Text('清空')),
TextField(
controller: controller,
)
]);
复制代码
清空内容还可以用 controller.clear()。
注意:controller 必须在 disapose 方法中销毁。
@override
void dispose() {
controller.dispose();
super.dispose();
}
复制代码
控制 TextField Focus
自动获得焦点
TextField(
autofocus: true,
);
复制代码
手动让一个 TextField 获得或失去焦点
手动获得焦点分四步
创建 focusNode。
把 focusNode 赋值给 TextField。
手动触发 focusNode 的 previousFocus、unfocus 方法获得和失去焦点。
在 dispose 中销毁 focusNode。
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
FocusNode focusNode = FocusNode(); //第一步
@override
void dispose() {
focusNode.dispose(); //第四步
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
focusNode: focusNode, // 第二步
)
ElevatedButton(
onPressed: () { // 第三步
focusNode.hasFocus
? focusNode.unfocus()
: focusNode.requestFocus();
},
child: Text('变换焦点')),
],
);
}
}
复制代码
如果是在手机上实行代码,会看到 TextField 获取焦点的时间会弹出软键盘,失去焦点的时间,软键盘收起。
监听获取焦点和失去焦点
因为 focusNode 混入了 ChangeNotifier,以是可以直接监听他的状态变化。
class _MyWidgetState extends State<MyWidget> {
var focusNode = FocusNode();
@override
void initState() {
focusNode.addListener(() {
print(focusNode.hasFocus);
});
super.initState();
}
@override
void dispose() {
focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return TextField(
focusNode: focusNode,
);
}
}
复制代码
控制焦点在差别 TextField 间切换
两个 TextField,第一个自动获得焦点,点击按钮,焦点从第一个 TextField 切换到第二个,再次点击,焦点从第二个 TextField 回到第一个。
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
var focusNode1 = FocusNode();
var focusNode2 = FocusNode();
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
autofocus: true,
focusNode: focusNode1,
),
const SizedBox(
height: 10,
),
TextField(
focusNode: focusNode2,
autofocus: true,
),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () {
if (focusNode1.hasFocus) {
focusNode1.nextFocus();
} else {
focusNode2.previousFocus();
}
},
child:const Text('切换焦点')),
],
);
}
}
复制代码
这是两个 TextField 连续的情况,如果不连续呢?可以用 FocusScope!
把 onPressed 代码修改为:
ElevatedButton(
onPressed: () {
if (focusNode1.hasFocus) {
FocusScope.of(context).requestFocus(focusNode2);
} else {
FocusScope.of(context).requestFocus(focusNode1);
}
},
child: const Text('切换焦点'))
复制代码
控制字母大小写的 textCapitalization
textCapitalization 用来控制字母的大小写。不常用,了解一下即可。
每个单词的第一字母大写 TextCapitalization.words
第个句子的第一个字母大写 TextCapitalization.sentences
每个字母都大写 TextCapitalization.characters
每个字母默认小写 TextCapitalization.none
可能会用到的是 TextCapitalization.characters,把输入的所有小写字母转换为大写。
TextField(
textCapitalization: TextCapitalization.characters,
),
复制代码
keyboardType 控制键盘
显示数字键盘TextInputType.number
TextField(
keyboardType: TextInputType.number
);
复制代码
不显示键盘 TextInputType.none
当 TextField 获得焦点后,不弹出键盘。
更多范例请见 TextInputType
以密码方式显示
TextField(
obscureText:true
);
复制代码
一样平常会加一个象征密码的图标
TextField(
obscureText:true,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock)
),
);
复制代码
键盘的操作按钮范例 TextInputAction
默认情况下为 TextInputAction.done。TextField 设置为多行时为 TextInputAction.newline。
ios 不能设置 TextInputAction.done,默认为 null。
看笔墨形貌可能不大好理解,还是用TextInputAction.search 举个例子吧。
可能你的手机上显示的是一个搜索图标,
TextField(
onSubmitted: (value) {
print(value);
},
textInputAction: TextInputAction.search,
)
复制代码
键盘上原来的 action button 的文本被替换为
搜索
。点搜索会触发提交,onSubmitted 被调用。
固然TextInputAction.search 表明点击按钮后应该进行搜索,但是否实行搜索由开发者决定。
我们再试一下 TextInputAction.newline 的举动。
TextField(
maxLines: 3,
);
复制代码
maxLines: 3 可以让 TextField 输入多行,点击 action button 会让 TextField 光标换行。
Flutter 中存在 TextInputAction.newline ,但 Android 或 iOS 中不存在。引入这个术语的缘故起因是开发者可以实现插入新行的通用结果,而无需了解 Android 上的各种 IME 操作和 iOS 上的返回键。
默认的 tooltip 提示语为英文?
如果你新建了一个项目,还没有做国际化,直接用 TextField,tooltip 的提示语是英文。可以这样操作一下:长按 TextField 输入框,这时会弹出一个提示框上面写着 Paste。
这是因为没有设置国际化的缘故起因。
想要使用 flutter_localizations 的话,我们需要在 pubspec.yaml 文件中添加它作为依赖:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
复制代码
MaterialApp(
supportedLocales: [
Locale("en"),
Locale("zh")
],
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate
],
)
复制代码
maxLength 与 counterText
TextField(
maxLength: 6,
),
复制代码
counterText 就是显示在右下角的文本。当指定 maxLength 时,会自动显示 counterText,当达到最大字数时,无法再输入。可以用maxLengthEnforcement: MaxLengthEnforcement.none 改变这种限定,字符数超过 maxLength 后也能继承输入。不过,超过限定后,默认会显示赤色的 errrorBorder 和 赤色的 errorText
TextField(
maxLength: 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 表示。
TextField(
maxLength: TextField.noMaxLength ,
),
复制代码
自定义 counterText 样式
默认的 counterText 是很小的,我们想把他变大一些。
两种方法,一个是用 theme,我们这次用局部 theme(相当于 css 中的局部样式表)。先获得父级样式,用 copywith 方法,把新的样式补充进来。当 counterStyle 为 null 的时间,counterText 采用 helperStyle,要想同时影响到 helperText,也可以定义 helperStyle。
var themeData = Theme.of(context);
var decorationTheme = themeData.inputDecorationTheme;
return Theme(
data: themeData.copyWith(
inputDecorationTheme:
decorationTheme.copyWith(counterStyle: TextStyle(fontSize: 20))),
child: TextField(
maxLength: 6
),
);
复制代码
另一个方法是直接用 counterStyle
TextField(
maxLength: 6,
decoration: InputDecoration(counterStyle: TextStyle(fontSize: 20)),
);
复制代码
当 maxLengthEnforcement: MaxLengthEnforcement.none ,输入字数超过最大字符数后,border ,counterText 都显示为赤色。这个时间 counterText 的样式是 errorStyle 控制的。我们可以修改为其它样式。errorStyle 还会影响 errorText 的样式。
我们定义错误时 counterText 显示为绿色。
InputDecoration(
errorStyle: TextStyle(color: Colors.green),
);
复制代码
自定义 counterText
maxLength 设为 3,我们要实现下面的效果:字符数没有超过 3 时 currentCount 样式为玄色,超过 3 时显示为赤色。
TextField(
style: const TextStyle(color: Color(0xFFC45F84), fontSize: 24),
maxLength: 3,
maxLengthEnforcement: MaxLengthEnforcement.none,
decoration: const InputDecoration(counterStyle: TextStyle(fontSize: 20)),
buildCounter: (context,
{required currentLength, required bool isFocused, maxLength}) {
return Text.rich(TextSpan(children: [
TextSpan(
text: '$currentLength',
style: TextStyle(color:currentLength>maxLength!? Colors.red:Colors.black)),
const TextSpan(text: ' / '),
TextSpan(text: '$maxLength')
]));
},
);
复制代码
用 buildCounter 天生 counterText ,当字符数超过最大限定数后,TextField 不再显示错误状态,border 显示为 focusBorder 样式。
用 Text.rich ,包罗多个 TextSpan,以便加差别的样式。上面的代码实现了所有的功能,但是有一个隐患,当 maxLength 为TextField.noMaxLength 的时间,maxLength 为 -1,不应该直接显示,应该处理一下:当 maxLength 为 -1 的时间,直接不显示了。
if (maxLength >= 0) const TextSpan(text: ' / '),
if (maxLength >= 0) TextSpan(text: '$maxLength')
复制代码
decoration 的 counterText 与 counter 属性
用 buildCounter 自定义 counterText 完全没有题目,为什么还要弄出这两个属性来呢?我们看下他们的作用。
当 counterText 不为 null ,buildCounter 被忽略。
当 counter 不为 null,counterText 被忽略。
由此可见,如果不想显示 countText,设置 counterText: '' 和定义 buildCounter 的效果一样,字符超出最大限定后,TextField 也不再显示错误状态,border 显示为 focusBorder 样式。
TextField(
decoration: InputDecoration(
counterText: ''
),
);
复制代码
如果要用 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。
自定义验证
abstract class TextInputFormatter {
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
);
static TextInputFormatter withFunction(
TextInputFormatFunction formatFunction,
) {
return _SimpleTextInputFormatter(formatFunction);
}
}
复制代码
TextInputFormatter 是一个抽象类,当需要定义本身的子类的时间,只需要 override formatEditUpdate 方法。formatEditUpdate 可以修改 text 的值,好比克制不被允许的值。 withFunction 是一个快捷函数,让我们直接定义 text 的逻辑,而不必定义新的类。
好比只允许输入 0-9
TextField(
inputFormatters: [
TextInputFormatter.withFunction(
(TextEditingValue oldValue, TextEditingValue newValue) {
return RegExp(r'^[0-9]*$').hasMatch(newValue.text)
? newValue
: oldValue;
},
),
],
);
复制代码
如果再限定一下长度这样写RegExp(r'^[0-9]{0,10}$') ,只能输入 0 到 10 个数字。
Flutter 为 TextInputFormatter 实现了两个子类,我们可以直接用。
FilteringTextInputFormatter
LengthLimitingTextInputFormatter
TextField 的 maxLength 功能就是用 LengthLimitingTextInputFormatter 实现的。
FilteringTextInputFormatter 用来过滤字符。
FilteringTextInputFormatter(
this.filterPattern, {
required this.allow,
this.replacementString = '',
});
复制代码
filterPattern 我们一样平常用 RegExp,就是正则来判断是否匹配。allow 如果为 true,filterPattern 是用于匹共同法的文本,如果为 false,匹配非法的文本。replacementString 是用来替换非法文本的替换字符串。
FilteringTextInputFormatter 有两个定名构造函数,对 allow 进行了赋值,一样平常情况下,我们优先使用这两个构造函数,让代码更可读。
FilteringTextInputFormatter.allow(
Pattern filterPattern, {
String replacementString = '',
}) : this(filterPattern, allow: true, replacementString: replacementString);
FilteringTextInputFormatter.deny(
Pattern filterPattern, {
String replacementString = '',
}) : this(filterPattern, allow: false, replacementString: replacementString);
复制代码
举个例子,只允许输入 0-9
TextField(
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('r[0-9]'))],
);
复制代码
再好比不允许输入 x
TextField(
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('x'))],
);
复制代码
你可能疑问 deny 的参数不是 Patten 吗?怎么能输入字符串?答案是 String 实现了 Patten 接口
abstract class String implements Comparable<String>, Pattern
复制代码
替换的功能就不举例子了,可以本身试试。如果需要替换的时间,优先思量用 FilteringTextInputFormatter。
如果你的验证逻辑很常用,可以 extends 一个子类出来,把逻辑封装起来。还是用只能输入 0-9 这个例子:
class MyFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
return RegExp(r'^[0-9]*$').hasMatch(newValue.text) ? newValue : oldValue;
}
}
复制代码
用的时间就会很方便
TextField(
inputFormatters: [MyFormatter()],
);
复制代码
onEditingComplete 与 onSubmitted
这两个事件是同时触发的,onEditingComplete 在前,onSubmitted 在后。
如果 onEditingComplete 不设置,默承认能让 TextField 失去焦点。如果不为空,就走 onEditingComplete 的逻辑。
onSubmitted 可以获得 TextFile 的 text。
在下面的例子中,固然 onEditingComplete 函数体为空,但毕竟不是 null,乐成制止了默认的可能让 TextField 失去焦点的举动。在 onSubmitted 中可以打印 TextField 中的文本。
TextField(
onEditingComplete: () {},
onSubmitted: (value) {
print(value);
},
);
复制代码
选中文本
选中文本 还是比力简朴的,指定 TextSelection 的开始位置 baseOffset,和竣事位置 extentOffset 即可。
下面的例子中,点击按钮会选中全部已经输入的文本。
Column(
children: [
TextField(
controller: controller,
style: const TextStyle(color: Color(0xFFC45F84), fontSize: 24),
),
ElevatedButton(
onPressed: () {
controller.selection = TextSelection(
baseOffset: 0, extentOffset: controller.text.length);
},
child: Text('选中全部文本'))
],
);
复制代码
最后要了解的是 当 TextField 获得焦点时,它会制止本身通过 AutomaticKeepAliveClientMixin.wantKeepAlive 进行 dispose,以避免丢失 selection。移除焦点将允许它被disposed。
Flutter TextField 交互实例 到这里就竣事了,谢谢观看。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/)
Powered by Discuz! X3.4