【问题标题】:What is the difference between TextFormField and TextField?TextFormField 和 TextField 有什么区别?
【发布时间】:2019-07-06 18:40:00
【问题描述】:

我是 Flutter(和 Dart)的新手,在尝试构建表单来编辑对象时,我在网上搜索了示例和教程,我看到这两种方法都被使用了。

两者有什么区别?我应该使用哪一个?

【问题讨论】:

    标签: flutter


    【解决方案1】:

    如果您在需要保存、重置或验证的地方创建 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 回调。

    【讨论】:

    【解决方案2】:

    简答

    如果您不知道自己需要什么,请使用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
                  }
                },
              ),
            ],
          ),
        );
      }
    }
    

    另见

    【讨论】:

      【解决方案3】:

      TextField 是一个简单的文本字段。 (你不关心用户输入) TextFormField 是要在表单中使用的文本字段(您关心用户输入)。

      如果您不需要验证TextField。 如果您需要验证用户输入,请使用 TextFormField 和 validator

      【讨论】:

      • “(你不关心用户输入)”当然我们关心 :D 它只是不是表单的一部分。
      • 我们始终关注用户输入,因为这是应用成功的一个因素!
      【解决方案4】:

      TextFormFieldTextField

      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 并使用自定义代码分别验证每个@ 更方便。

      详情

      TextFormField 如何在表单中注册自己?

      FormFieldTextFormField 的基类等)在其build() 方法中进行调用: Form.of(context)?._register(this);

      在英语中,这意味着: 搜索我的上下文层次结构,直到找到 Form 小部件(如果有)并使用该表单注册自己。

      ? 是在没有 Form 小部件父级的情况下。 _register 调用只会在如果在上面某个地方运行FormFormState

      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 的重建。

      总结

      TextFormFieldTextField 的豪华包、空调、蓝牙连接的 8 扬声器立体声版本。它包含许多您在接受用户输入信息时会用到的常用功能。

      【讨论】:

        【解决方案5】:

        2021年10月)我认为这可能是对两者差异的最简洁和简单的解释。

        来自material library

        TextField:材料设计文本字段。

        TextFormField:一个包含TextFieldFormField

        同样,您可以将 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 中,则不允许用户输入任何文本,例如CupertinoPickerFormFieldSimpleDialogFormField用户可以在多个选项之间进行选择(基本上是包裹在FormField 中的材料SimpleDialog 小部件),只需简单地使用InputDecorationhintText 参数即可,而无需使用TextEditingController 来操作文本。使用hintStyle: const TextStyle(color: Color(0xdd000000)) 使提示文本与正常输入文本具有相同的颜色。

        这个video from Flutter Europe 将帮助您立即掌握 Flutter 中的表单。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-13
          • 2021-12-04
          • 1970-01-01
          • 2010-10-02
          • 2011-12-12
          • 2010-09-16
          • 2012-03-14
          • 2012-02-06
          相关资源
          最近更新 更多