Flutter基础Widget使用----动画图解输入框
输入框同样是UI组成的必要部分,比如常见的登陆场景,需要输入用户名和密码,多个输入框组成在一起就成了一个表单了。在 Flutter 中输入框是由 TextField Widget实现的,而表单是由 Form Widget实现的。
一、TextField
TextField用于文本输入,它提供了很多属性,下面逐一来看这些属性的含义。
const TextField({
Key key,
this.controller,
this.focusNode,
this.decoration = const InputDecoration(),
TextInputType keyboardType,
this.textInputAction,
this.textCapitalization = TextCapitalization.none,
this.style,
this.strutStyle,
this.textAlign = TextAlign.start,
this.textAlignVertical,
this.textDirection,
this.readOnly = false,
this.showCursor,
this.autofocus = false,
this.obscureText = false,
this.autocorrect = true,
this.maxLines = 1,
this.minLines,
this.expands = false,
this.maxLength,
this.maxLengthEnforced = true,
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.inputFormatters,
this.enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection,
this.onTap,
this.buildCounter,
this.scrollController,
this.scrollPhysics,
})
controller ---- 控制正在编辑的文本,它是 TextEditingController 类型。
focusNode ---- 定义此 Widget 的键盘焦点。
decoration ---- 在 TextField 周围显示的装饰。默认情况下,在文本字段下绘制一条水平线,但可以配置为显示图标,标签,提示文本和错误文本。
keyboardType ---- TextInputType 枚举类,用于设置该输入框默认的键盘输入类型,在Android上,行为可能会因设备和键盘提供商而异。
枚举值 | 含义 |
---|---|
text | 文本输入键盘 |
multiline | 多行文本,需和 maxLines 配合使用(设为 null 或大于1) |
number | 会弹出数字键盘 |
phone | 优化后的电话号码输入键盘,会弹出数字键盘并显示“* #” |
datetime | 优化后的日期输入键盘,Android 上会显示“: -” |
emailAddress | 优化后的电子邮件地址,会显示“@ .” |
url | 优化后的url输入键盘,会显示“/ .” |
textInputAction ---- 用于键盘的操作按钮的类型。
enum TextInputAction {
/// 逻辑含义:当前输入源(例如 [TextField] )没有相关的输入操作。
none,
/// 逻辑含义:让操作系统决定最合适的操作。
unspecified,
/// 逻辑含义:用户完成向一组输入(如表单)提供输入的操作。现在应该发生某种终结行为。
done,
/// 逻辑含义:用户输入了一些代表目的地的文本,例如餐馆名称。
/// “go”按钮旨在将用户带到与该目的地相对应的应用程序部分。
go,
/// 逻辑含义:执行搜索查询。
search,
/// 逻辑含义:发送用户撰写的内容,例如电子邮件或短信。
send,
/// 逻辑含义:用户已完成当前输入源的操作,并希望移至下一个输入源。
next,
/// 逻辑含义:用户希望返回到组中的上一个输入源,例如,具有多个[TextField]的表单。
previous,
/// 逻辑含义:在iOS应用中,通常会在屏幕顶部显示“返回”按钮和“继续”按钮。
continueAction,
/// 逻辑含义:用户想加入某些东西,例如无线网络。
join,
/// 逻辑含义:用户想要路线选择,例如,驾驶路线。
route,
/// 逻辑含义:启动对紧急服务的呼叫。
emergencyCall,
/// 逻辑含义:在焦点文本输入中插入换行符,例如 [TextField]。
newline,
}
textCapitalization ---- 配置平台键盘如何选择大写或小写键盘。
enum TextCapitalization {
/// 每个单词的第一个字母默认为大写的键盘。
words,
/// 每个句子的首字母默认为大写的键盘。
sentences,
/// 每个字符默认为大写的键盘。
characters,
/// 默认为小写键盘。
none,
}
style ---- TextStyle 类型,之前在 Text Widget 一节中已经详细说明。
strutStyle ---- StrutStyle 类型,之前在 Text Widget 一节中已经详细说明。
textAlign ---- 如何水平对齐文本。
enum TextAlign {
/// 将文本在容器的左边缘对齐。
left,
/// 在容器的右边缘对齐文本。
right,
/// 在容器的中心对齐文本。
center,
/// 文本的延伸行,以填充容器宽度的软换行结束。
/// 以换行符结束的行与[开始]边缘对齐。
justify,
/// 将文本在容器的前端对齐。
start,
/// 将文本在容器的尾部对齐。
end,
}
textAlignVertical ---- 输入中文本的垂直对齐方式。
/// 将TextField的输入Text与TextField中的最高位置对齐。
static const TextAlignVertical top = TextAlignVertical(y: -1.0);
/// 将TextField的输入Text对齐到TextField的中心。
static const TextAlignVertical center = TextAlignVertical(y: 0.0);
/// 将TextField的输入Text与TextField中最底部的位置对齐。
static const TextAlignVertical bottom = TextAlignVertical(y: 1.0);
textDirection ---- 文字方向,是从左向右,还是从右向左(例如阿拉伯语、希伯来语)。
readOnly ---- 是否只读。
showCursor ---- 是否显示光标。
autofocus ---- 是否自动获取焦点。
obscureText ---- 是否隐藏正在编辑的文本(例如,用于密码)。
autocorrect ---- 是否启用自动校正。
maxLines ---- 最大行数。
minLines ---- 最小行数。
expands ---- 此 Widget 的高度是否将调整大小以填充其父级。
maxLength ---- 文本字段中允许的最大字符数。
maxLengthEnforced ---- 如果为 true,则阻止该 Widget 允许超过 [maxLength] 个字符。如果设置了 [maxLength],则 [maxLengthEnforced] 指示是否强制执行限制,或者仅在超过 [maxLength] 时提供字符计数器和警告。
onChanged ---- 输入框内容改变时的回调函数。
onEditingComplete ---- 当用户提交可编辑的内容时调用(例如,用户按下键盘上的“完成”按钮)。
onSubmitted ---- 当用户指示他们已完成字段中文本的编辑时调用。
inputFormatters ---- 可选的输入验证和格式化替代。文本输入更改时,格式化程序将按提供的顺序运行。
enabled ---- 如果为false,则文本字段为“禁用”:它将忽略点击,其 [decoration] 呈现为灰色。
cursorWidth ---- 光标宽度。
cursorRadius ---- 光标圆角。
cursorColor ---- 光标颜色。
keyboardAppearance ---- 键盘的外观。仅在iOS设备上支持此设置。
scrollPadding ---- 当 Textfield 滚动到视图中时,将 padding 配置为围绕 [Scrollable] 的边缘。
当此 Widget 获得焦点并且不完全可见时(例如,部分滚动到屏幕外或被键盘重叠),则它将尝试通过滚动周围的 [Scrollable](如果有)来使其自身可见。此值控制 TextField 在滚动之后将距 [Scrollable] 边缘的距离。
dragStartBehavior ---- 设置确定用户开始拖动时拖动何时正式开始。
enableInteractiveSelection ---- 如果为true,则长按此TextField将选择文本并显示 剪切/复制/粘贴菜单,而点击将移动文本插入符号。如果为false,则将禁用用于选择文本、复制和粘贴以及移动插入符号的大多数辅助功能支持。
onTap ---- 当用户点击此文本字段时调用。
buildCounter ---- 生成自定义 [InputDecorator.counter] Widget 的回调。有关传递的参数的说明,请参见 [InputCounterWidgetBuilder]。返回的 Widget 将放置在该行下方,以代替指定 [counterText] 时构建的默认 Widget。
scrollController ---- 垂直滚动时要使用的 [ScrollController](可用于控制此 Widget 滚动到的位置的对象)。如果为null,它将实例化一个新的ScrollController。
scrollPhysics ---- 垂直滚动时要使用的 [ScrollPhysics](Widget 应如何响应用户输入) 。如果未指定,它将根据当前平台运行。
TextField 可控制的字段太多了,我们先来一个最简单的输入框,就一行代码。
TextField()
1.1 使用 InputDecoration
在 TextField 周围显示的装饰。默认情况下,在文本字段下绘制一条水平线,但可以配置为显示图标,标签,提示文本和错误文本。属性非常多,有兴趣的可以逐个研究一番,我就不再多谈。
const InputDecoration({
this.icon,
this.labelText,
this.labelStyle,
this.helperText,
this.helperStyle,
this.hintText,
this.hintStyle,
this.hintMaxLines,
this.errorText,
this.errorStyle,
this.errorMaxLines,
this.hasFloatingPlaceholder = true,
this.isDense,
this.contentPadding,
this.prefixIcon,
this.prefix,
this.prefixText,
this.prefixStyle,
this.suffixIcon,
this.suffix,
this.suffixText,
this.suffixStyle,
this.counter,
this.counterText,
this.counterStyle,
this.filled,
this.fillColor,
this.focusColor,
this.hoverColor,
this.errorBorder,
this.focusedBorder,
this.focusedErrorBorder,
this.disabledBorder,
this.enabledBorder,
this.border,
this.enabled = true,
this.semanticCounterText,
this.alignLabelWithHint,
}) : assert(enabled != null),
下面实现一个简易登陆输入界面。
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
autofocus: true,
decoration: InputDecoration(
labelText: "用户名",
hintText: "请输入用户名",
prefixIcon: Icon(Icons.person)),
),
TextField(
decoration: InputDecoration(
labelText: "密 码",
hintText: "请输入密码",
prefixIcon: Icon(Icons.lock)),
obscureText: true,
),
],
));
}
}
1.2 使用 textInputAction
我们知道 textInputAction 用于控制键盘的操作按钮的类型。具体看一下如何影响键盘上按钮的外观。
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
autofocus: true,
decoration: InputDecoration(
labelText: "百度一下",
hintText: "请输入你要查询的内容",
prefixIcon: Icon(Icons.search)),
textInputAction: TextInputAction.search),
TextField(
decoration: InputDecoration(
labelText: "对照组",
),
)
],
));
}
}
1.3 使用 controller
只要设置了controller,获取 TextField 非常容易。
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _searchController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
autofocus: true,
decoration: InputDecoration(
labelText: "百度一下",
hintText: "请输入你要查询的内容",
prefixIcon: Icon(Icons.search)),
textInputAction: TextInputAction.search,
controller: _searchController,
),
RaisedButton(
onPressed: () {
setState(() {});
},
child: Text("获取TextField内容"),
),
Text(_searchController.text),
],
));
}
}
1.4 使用 focusNode
焦点可以通过 FocusNode 和 FocusScopeNode 来控制,默认情况下,焦点由 FocusScope 来管理,它代表焦点控制范围,可以在这个范围内可以通过 FocusScopeNode 在输入框之间移动焦点、设置默认焦点等。我们可以通过 FocusScope.of(context) 来获取 Widget 树中默认的 FocusScopeNode。
下面定义了三个输入框,焦点在它们三个之间,点击焦点移动按钮时候从上到下移动焦点。
class _MyHomePageState extends State<MyHomePage> {
FocusNode focusNode1 = new FocusNode();
FocusNode focusNode2 = new FocusNode();
FocusNode focusNode3 = new FocusNode();
FocusScopeNode focusScopeNode;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
autofocus: true,
focusNode: focusNode1,
decoration: InputDecoration(
labelText: "输入1",
),
),
TextField(
focusNode: focusNode2,
decoration: InputDecoration(
labelText: "输入2",
),
),
TextField(
focusNode: focusNode3,
decoration: InputDecoration(
labelText: "输入3",
),
),
RaisedButton(
onPressed: () {
setState(() {
if (null == focusScopeNode) {
focusScopeNode = FocusScope.of(context);
}
if (focusNode1.hasFocus) {
focusScopeNode.requestFocus(focusNode2);
} else if (focusNode2.hasFocus) {
focusScopeNode.requestFocus(focusNode3);
} else if (focusNode3.hasFocus) {
focusScopeNode.requestFocus(focusNode1);
} else {
focusScopeNode.requestFocus(focusNode1);
}
});
},
child: Text("焦点移动"),
),
],
));
}
}
二、Form
Flutter提供了一个 Form 组件,它可以对输入框进行分组,然后进行一些统一操作,如输入内容校验、输入框重置以及输入内容保存。当需要提交很多字段内容的时候就派上了用场。
const Form({
Key key,
@required this.child,
this.autovalidate = false,
this.onWillPop,
this.onChanged,
})
[child] 参数不能为 null。
autovalidate ---- 是否自动校验输入内容;当为 true 时,每一个子 FormField 内容发生变化时都会自动校验合法性,并直接显示错误信息。否则,需要通过调用 FormState.validate() 来手动校验。
onWillPop ---- 决定 Form 所在的路由是否可以直接返回(如点击返回按钮),该回调返回一个 Future 对象,如果 Future 的最终结果是 false,则当前路由不会返回;如果为 true,则会返回到上一个路由。此属性通常用于拦截返回按钮。
onChanged ---- Form 的任意一个子 FormField 内容发生变化时会触发此回调。
2.1 FormField
Form 的子孙元素必须是 FormField 类型,FormField 是一个抽象类。
const FormField({
Key key,
@required this.builder,
this.onSaved,
this.validator,
this.initialValue,
this.autovalidate = false,
this.enabled = true,
})
这些属性基本上都是自解释的。
onSaved ---- 保存回调。
validator ---- 验证回调。
initialValue ---- 初始值。
autovalidate ---- 是否自动校验。
Flutter提供了一个 TextFormField Widget,它继承自 FormField 类,也是 TextField 的一个包装类,所以除了FormField 定义的属性之外,它还包括 TextField 的属性。
TextFormField({
Key key,
this.controller,
String initialValue,
FocusNode focusNode,
InputDecoration decoration = const InputDecoration(),
TextInputType keyboardType,
TextCapitalization textCapitalization = TextCapitalization.none,
TextInputAction textInputAction,
TextStyle style,
StrutStyle strutStyle,
TextDirection textDirection,
TextAlign textAlign = TextAlign.start,
bool autofocus = false,
bool readOnly = false,
bool showCursor,
bool obscureText = false,
bool autocorrect = true,
bool autovalidate = false,
bool maxLengthEnforced = true,
int maxLines = 1,
int minLines,
bool expands = false,
int maxLength,
VoidCallback onEditingComplete,
ValueChanged<String> onFieldSubmitted,
FormFieldSetter<String> onSaved,
FormFieldValidator<String> validator,
List<TextInputFormatter> inputFormatters,
bool enabled = true,
double cursorWidth = 2.0,
Radius cursorRadius,
Color cursorColor,
Brightness keyboardAppearance,
EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
bool enableInteractiveSelection = true,
InputCounterWidgetBuilder buildCounter,
})
2.2 FormState
FormState 是与 [Form] Widget 关联的状态类。[FormState] 对象可用于 [save],[reset] 和 [validate] 作为关联 [Form] 的子孙的每个 [FormField]。
FormState.validate():调用此方法后,会调用 Form 子孙 FormField 的 validate 回调,如果有一个校验失败,则返回 false,所有校验失败项都会返回用户返回的错误提示。
FormState.save():调用此方法后,会调用 Form 子孙 FormField 的 save 回调,用于保存表单内容。
FormState.reset():调用此方法后,会将子孙 FormField 的内容清空。
下面是一个实例。
class FormTestRoute extends StatefulWidget {
@override
_FormTestRouteState createState() => new _FormTestRouteState();
}
class _FormTestRouteState extends State<FormTestRoute> {
TextEditingController _nameController = new TextEditingController();
TextEditingController _pwdController = new TextEditingController();
GlobalKey _formKey = new GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Form Test"),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0),
child: Form(
key: _formKey,
autovalidate: true,
child: Column(
children: <Widget>[
TextFormField(
autofocus: true,
controller: _nameController,
decoration: InputDecoration(
labelText: "用户名",
hintText: "请输入用户名",
icon: Icon(Icons.person)),
validator: (v) {
return v.trim().length > 0 ? null : "用户名不能为空";
}),
TextFormField(
controller: _pwdController,
decoration: InputDecoration(
labelText: "密 码",
hintText: "请输入密码",
icon: Icon(Icons.lock)),
obscureText: true,
validator: (v) {
return v.trim().length > 5 ? null : "密码不能少于6位";
}),
// 登录按钮
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
padding: EdgeInsets.all(6.0),
child: Text("登录"),
color: Theme.of(context).primaryColor,
textColor: Colors.white,
onPressed: () {
if ((_formKey.currentState as FormState).validate()) {
//验证通过提交数据
setState(() {});
}
},
),
),
],
),
),
Text("我的用户名=${_nameController.text},密码=${_pwdController.text}"),
],
),
),
),
);
}
}
上一篇: Kubernetes 第一个容器