【发布时间】:2019-07-06 18:40:00
【问题描述】:
我是 Flutter(和 Dart)的新手,在尝试构建表单来编辑对象时,我在网上搜索了示例和教程,我看到这两种方法都被使用了。
两者有什么区别?我应该使用哪一个?
【问题讨论】:
标签: flutter
我是 Flutter(和 Dart)的新手,在尝试构建表单来编辑对象时,我在网上搜索了示例和教程,我看到这两种方法都被使用了。
两者有什么区别?我应该使用哪一个?
【问题讨论】:
标签: flutter
如果您在需要保存、重置或验证的地方创建
Form操作-使用TextFormField。 Else 用于简单的用户输入捕获TextField就足够了。
TextFormField,与Form 小部件集成。
这是一个将 TextField 小部件包装在 FormField 中的便利小部件。
不需要Form 祖先。表单只是让一次保存、重置或验证多个字段变得更容易。
要在没有表单的情况下使用,请将 GlobalKey 传递给构造函数并使用 GlobalKey.currentState 保存或重置表单字段。
样本:
TextFormField(
autovalidateMode: AutovalidateMode.always
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: 'What do people call you?',
labelText: 'Name *',
),
onSaved: (String value) {
// This optional block of code can be used to run
// code when the user saves the form.
},
validator: (String value) {
return value.contains('@') ? 'Do not use the @ char.' : null;
},
)
TextField,这是没有 Form 集成的基础文本字段。
每当用户更改字段中的文本时,文本字段都会调用onChanged 回调。如果用户指示他们已完成在字段中的输入(例如,通过按下软键盘上的按钮),则文本字段将调用 onSubmitted 回调。
【讨论】:
如果您不知道自己需要什么,请使用TextField。这是从用户获取文本输入的最基本的 Flutter 小部件。这是你应该首先掌握的。
使用TextField 是一种允许用户输入的简单方法。
TextField(
decoration: InputDecoration(
hintText: 'Name'
),
);
要获取用户输入的文本,您可以在每次发生更改时收到通知,如下所示:
TextField(
decoration: InputDecoration(
hintText: 'Name'
),
onChanged: (text) {
// do something with text
},
),
或者您可以使用TextEditingController,如here 所述。这将使您能够访问文本状态。
如果您发现自己需要在保存之前验证用户文本输入,您可以考虑使用TextFormField。想象一下这样的事情:
您可能希望对用户名和密码进行大量验证检查。
当然,您仍然可以只使用几个 TextField,但TextFormField 具有额外的内置功能,可以让您的生活更轻松。通常,只有在 Form 小部件中使用 TextFormField 时才会使用它(尽管这不是严格要求)。
这是来自documentation 的精简示例:
class MyCustomForm extends StatefulWidget {
@override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
class MyCustomFormState extends State<MyCustomForm> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (value) {
// validation logic
},
),
RaisedButton(
child: Text('Submit'),
onPressed: () {
if (_formKey.currentState.validate()) {
// text in form is valid
}
},
),
],
),
);
}
}
【讨论】:
TextField 是一个简单的文本字段。 (你不关心用户输入)
TextFormField 是要在表单中使用的文本字段(您关心用户输入)。
如果您不需要验证TextField。
如果您需要验证用户输入,请使用 TextFormField 和 validator。
【讨论】:
TextFormField 与 TextField
TextFormField 返回一个TextField,但将TextField 包装为您可以通过Form 使用的额外功能,也可以不使用(例如重置、验证、保存等)。
一般来说,你想使用TextFormField,除非你喜欢写样板代码。
TextFormField 是如何工作的?它能做什么?TextFormField 扩展 FormField 类,(StatefulWidget)。
FormField 对象在实例化时会做一件特殊的事情:它们会在小部件树中查找 Form 并将自己注册到 Form。
注册后,这些FormField 小部件可以由父Form 更改。
Form 也是一个StatefulWidget。它有一个FormState 对象。
FormState 可以获取和设置任何/所有FormFields 注册的数据。
例如,要清除整个表单,我们可以在FormState 上调用reset(),FormState 将遍历所有已注册的子FormFields,将每个FormField 重置为其initialValue(默认为空)。
验证是将多个FormField(如TextFormField)放入Form/FormState 的另一个常见用例。这允许通过对Form 的单个validate() 调用来验证多个字段。
怎么样? FormState 有一个 validate() 方法,它遍历每个注册的 FormField 并调用 FormField's validate() 方法。在Form 上调用validate() 比手动跟踪所有TextField 并使用自定义代码分别验证每个@ 更方便。
FormField(TextFormField 的基类等)在其build() 方法中进行调用:
Form.of(context)?._register(this);
在英语中,这意味着:
搜索我的上下文层次结构,直到找到 Form 小部件(如果有)并使用该表单注册自己。
? 是在没有 Form 小部件父级的情况下。 _register 调用只会在如果是在上面某个地方运行Form 和FormState。
Form.of(context)?._register(this) 是如何工作的?Form & FormState 偷偷使用InheritedWidget。
在FormState.build() 你会看到这段代码:
return WillPopScope(
onWillPop: widget.onWillPop,
child: _FormScope( // ← sneaky
formState: this,
generation: _generation,
child: widget.child,
),
);
看着_FormScope我们看到了:
class _FormScope extends InheritedWidget
当父窗口小部件是InheritedWidget 时,任何孩子都可以使用特殊的“为我找到这个确切类型的父母” 方法找到该父母。
下面是如何在Form 中使用/公开“找到我”方法作为我们可以从任何地方调用的静态方法:
static FormState of(BuildContext context) {
final _FormScope scope = context.dependOnInheritedWidgetOfExactType<_FormScope>();
return scope?._formState;
}
该方法dependOnInheritedWidgetOfExactType 的命名有点棘手。 findInheritedWidgetOfExactType 的可读性更高,但它不仅仅是查找。 (我们可以通过注册为dependOn this FormState 的子代触发Form 的重建。
TextFormField 是TextField 的豪华包、空调、蓝牙连接的 8 扬声器立体声版本。它包含许多您在接受用户输入信息时会用到的常用功能。
【讨论】:
(2021年10月)我认为这可能是对两者差异的最简洁和简单的解释。
TextField:材料设计文本字段。
TextFormField:一个包含TextField的FormField。
同样,您可以将 FormField 包裹在任何 Cupertino 输入组件周围,例如 CupertinoTextField
下面是一个关于自定义CheckboxFormField 的示例,它是一个包裹在材料设计组件Checkbox 周围的FormField:
// A custom CheckboxFormField, which is similar to the built-in TextFormField
bool agreedToTerms = false;
FormField(
initialValue: false,
validator: (value) {
if (value == false) {
return 'You must agree to the terms of service.';
}
return null;
},
builder: (FormFieldState formFieldState) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Checkbox(
value: agreedToTerms,
onChanged: (value) {
// When the value of the checkbox changes,
// update the FormFieldState so the form is
// re-validated.
formFieldState.didChange(value);
setState(() {
agreedToTerms = value;
});
},
),
Text(
'I agree to the terms of service.',
style: Theme.of(context).textTheme.subtitle1,
),
],
),
if (!formFieldState.isValid)
Text(
formFieldState.errorText ?? "",
style: Theme.of(context)
.textTheme
.caption
.copyWith(color: Theme.of(context).errorColor),
),
],
);
},
),
经验法则:如果您的框只有一个输入字段,则只需使用 TextField 之类的原始输入(尽管在这种情况下FormField 有点过分)。如果您的框有许多输入字段,您需要将每个输入字段包装在 FormField 中,然后将所有输入字段集成到 Form 小部件中,以便同时验证和保存所有表单字段。
额外提示:如果您将TextField 包裹在FormField 中,则不允许用户输入任何文本,例如CupertinoPickerFormField 或SimpleDialogFormField用户可以在多个选项之间进行选择(基本上是包裹在FormField 中的材料SimpleDialog 小部件),只需简单地使用InputDecoration 的hintText 参数即可,而无需使用TextEditingController 来操作文本。使用hintStyle: const TextStyle(color: Color(0xdd000000)) 使提示文本与正常输入文本具有相同的颜色。
这个video from Flutter Europe 将帮助您立即掌握 Flutter 中的表单。
【讨论】: