在您开发的几乎每个应用程序中,迟早都会需要捕获用户输入。幸运的是,在 Flutter 中捕获文本输入相当简单。但是,随着更多字段和输入类型添加到表单中,捕获此信息的复杂性迅速增加。

通常,这些输入字段,无论是文本字段、日期字段还是任何其他类型的输入,都称为“控件”。验证也可能成为一个问题,因为即使是对某些字段的简单验证也可能需要编写冗长的自定义验证器。

在本文中,我们将创建一个注册表单,其中包含输入验证和根据其他字段值更改的字段。我们将首先在不使用响应式表单的情况下完成此任务,然后使用响应式表单重新实现相同的表单,以了解 Flutter 中响应式表单的好处。

我们将涵盖的内容:

  • Flutter 反应式表单项目概述

  • 在 Flutter 中制作没有响应式表单的表单

  • 在 Flutter 中手动创建表单的问题

  • 需要考虑的两种反应式 Flutter 包选项

  • 用于在Flutter

    flutter_form_builder

    中创建响应式表单

    • 设置基本表单输入

    • 设置宠物类型选择器

    • 最后设置三个问题

  • 验证和检索表单中的值

Flutter 反应式表单项目概述

我们将创建的应用程序是一个用于宠物进入“宠物旅馆”的注册应用程序——人们可以在度假时将宠物送走的地方。

为了让这个应用程序正常工作,人们需要提供详细信息,例如他们的姓名和电话号码、他们拥有什么样的宠物以及他们的宠物的好恶。最终结果将如下所示:

此表格有一些要求。

首先,三个后续问题必须根据用户选择的宠物类型而变化。

接下来,需要这三个问题的答案,所以我们必须添加Flutter 表单验证逻辑以确保它们被填写。

最后,电话号码必须只包含数字,所以如果它包含非数字值,那么表单应该拒绝该条目并通知用户。

在 Flutter 中制作没有响应式表单的表单

在第一种方法中,我们自己手动创建表单,并且我们还希望捕获这些单独字段中的文本输入。

因此,我们负责创建TextControllers可以关联到TextFormField小部件的个体。我们还负责创建一个容纳所选宠物的变量。

现在让我们创建这些变量:

final _formKey = GlobalKey<FormState>();
PetType? _petType;
final firstName = TextEditingController();
final lastName = TextEditingController();
final questionResponses = List.generate(3, (index) => TextEditingController());

要将文本写入这些字段,我们将创建TextFormField小部件并将它们绑定到适当的控制器:

TextFormField(decoration: InputDecoration(hintText: 'First Name'),controller: firstName,
),
TextFormField(decoration: InputDecoration(hintText: 'Last Name'),controller: lastName,
),

电话号码输入字段有点不同,因为我们需要验证其中是否包含有效的电话号码,并在检测到无效输入时提示用户:

TextFormField(decoration: InputDecoration(hintText: 'Phone number'),autovalidateMode: AutovalidateMode.always,validator: (val) {if (val == null || val == "") {return 'Please enter a phone number';}if (int.tryParse(val) == null) {return 'Only enter numbers in the phone number field';}return null;},
),

接下来,我们指定宠物选择器。这是一个RadioListTile允许用户选择他们带入的宠物类型:猫、狗或针鼹。

当用户选择一种宠物时,我们还希望遍历之前对这些问题给出的答案并清除它们,以便一次只选择一个选项。


超过 20 万开发人员使用 LogRocket 来创造更好的数字体验了解更多 →


RadioListTile<PetType>(value: PetType.cat,groupValue: _petType,onChanged: (val) => setState(() {for (final controller in questionResponses) {controller.clear();}_petType = val;}),title: Text('Cat'),
),

最后,我们想根据选择的宠物类型来改变我们提出的问题。

我们可以通过使用Builder, 来实现,它将根据给定变量的值更新小部件树。因此,如果选择的动物类型是“猫”,表格将显示该动物类型的问题,对于狗或针鼹类型的动物也是如此。

Builder(builder: (context) {switch (_petType) {case PetType.cat:return Column(children: [Text("Aw, it's a cat!"),PetQuestionField(question: 'Can we pat the cat?', controller: questionResponses[0]),PetQuestionField(question: 'Can we put a little outfit on it?', controller: questionResponses[1]),PetQuestionField(question: 'Does it like to jump in boxes?', controller: questionResponses[2]),],);
​case PetType.dog:return Column(children: [Text("Yay, a puppy! What's its details?"),PetQuestionField(question: 'Can we wash your dog?', controller: questionResponses[0]),PetQuestionField(question: 'What is your dog\'s favourite treat?', controller: questionResponses[1]),PetQuestionField(question: 'Is your dog okay with other dog\'s?', controller: questionResponses[2]),],);
​case PetType.echidna:return Column(children: [Text("It's a small spiky boi. Can you fill us in on some of the details?"),PetQuestionField(question: 'How spikey is the echidna?', controller: questionResponses[0]),PetQuestionField(question: 'Can we read the echidna a story?', controller: questionResponses[1]),PetQuestionField(question: 'Does it like leafy greens?', controller: questionResponses[2]),],);case null:{return Text('Please choose your pet type from above');}}},
),

创建了单独的表单控件后,是时候为用户创建一个按钮来注册他们的宠物了。此按钮应仅在提供的输入有效时允许用户继续,并应提示用户更正任何无法验证的输入。

ElevatedButton(onPressed: () {// Form is valid if the form controls are reporting that // they are valid, and a pet type has been specified.final valid = (_formKey.currentState?.validate() ?? false) && _petType != null;if (!valid) {// If it's not valid, prompt the user to fix the formshowDialog(context: context,builder: (context) => SimpleDialog(contentPadding: EdgeInsets.all(20),title: Text('Please check the form'),children: [Text('Some details are missing or incorrect. Please check the details and try again.')],));} else {// If it is valid, show the received valuesshowDialog(context: context,builder: (context) => SimpleDialog(contentPadding: EdgeInsets.all(20),title: Text("All done!"),children: [Text("Thanks for all the details! We're going to check your pet in with the following details.",style: Theme.of(context).textTheme.caption,),Card(child: Column(children: [Text('First name: ${firstName.text}'),Text('Last name: ${lastName.text}\r\n'),Text('Pet type: ${_petType}'),Text('Response 1: ${questionResponses[0].text}'),Text('Response 2: ${questionResponses[1].text}'),Text('Response 3: ${questionResponses[2].text}'),],),)],),);}},child: Text('REGISTER'))

在 Flutter 中手动创建表单的问题

在 Flutter 中使用表单并不是特别困难,但是手工制作我们自己的表单可能会有点费力。让我们分解为什么会这样。

首先,如果您希望能够从字段中获取文本或清除字段的输入,您必须TextEditingController为每个字段创建自己的。很容易看出你怎么能用其中的一些来结束,你必须自己跟踪。

其次,您必须为简单的事情编写自己的验证逻辑,例如检查数字是否正确。

最后,这种方法会产生相当多的样板代码。对于一两个文本字段,它并没有那么糟糕,但很容易看出它的扩展性如何。


来自 LogRocket 的更多精彩文章:

  • 不要错过来自 LogRocket 的精选时事通讯The Replay

  • 了解LogRocket 的 Galileo 如何消除噪音以主动解决应用程序中的问题

  • 使用 React 的 useEffect优化应用程序的性能

  • 在多个 Node 版本之间切换

  • 了解如何使用 AnimXYZ 为您的 React 应用程序制作动画

  • 探索 Tauri,一个用于构建二进制文件的新框架

  • 比较NestJS 与 Express.js


需要考虑的两种反应式 Flutter 包选项

如果我们要开始寻找一个可以让这个过程更容易的包,并且我们有“反应形式”的想法,我们可能会很快遇到reactive_formsFlutter 包。然而,它不是我用来在我的应用程序中创建反应式表单的包。

为什么不?

好吧,pub.dev 上的第一句话告诉我们,Reactive Forms 是“……一种处理表单输入和验证的模型驱动方法,深受Angular 的 Reactive Forms 的启发。”RM入梦音频变声器,支持实时变声已解锁VIP所有功能,聊天接视频变声都可以!因此,我们可以确定reactive_forms包中使用的心态将类似于我们在 Angular 中发现的心态。

如果我们已经了解 Angular,那可能更有理由使用reactive_forms. 但如果我们不了解 Angular,我们更感兴趣的是在表单中实现响应性的最简单方法。

根据我的经验,我发现使用包flutter_form_builder是一种更简单、更可扩展的创建表单的方式。

当然,我鼓励您研究这两个包并选择您喜欢的一个,因为一个包不一定比另一个包“更好”,但它们确实代表了实现相似结果的两种不同方式。

用于flutter_form_builder创建反应式表单

现在让我们使用这个包flutter_form_builder来创建我们的表单。这可以减少我们必须编写的代码量,使我们更容易理解我们编写的代码,也可以避免我们编写自己的验证逻辑。

首先,我们将在我们的文件中的flutter_form_builder包中添加一个依赖项:pubspec.yaml

flutter_form_builder: ^7.4.0

有了这个设置,让我们重新实现我们的表单以使用flutter_form_builder.

我们需要为我们打算在表单中使用的字段添加一些名称。我们应该将它们设置为对我们来说合乎逻辑的变量名称,因为稍后我们需要将它们绑定FormBuilderTextField到它们。

final String FIRST_NAME = 'FirstName';
final String LAST_NAME = 'LastName';
final String PHONE_NUMBER = 'PhoneNumber';
final String PET_CHOICE = 'PetChoice';
final String QUESTION_ANSWER_1 = 'QuestionAnswer1';
final String QUESTION_ANSWER_2 = 'QuestionAnswer2';
final String QUESTION_ANSWER_3 = 'QuestionAnswer3';

我们还需要指定一个GlobalKey<FormBuilderState>, 来存储我们的表单捕获的详细信息。

final _fbKey = GlobalKey<FormBuilderState>();

下一个重大变化是,我们的表单不再被包裹在 a 中,而是被包裹在 aForm中FormBuilder,并为FormBuilder.

FormBuilder(key: _fbKey,child: Column(children: [...children widgets here])
)

这意味着FormBuilder将表单中的值存储在此键中,以便我们以后可以轻松地检索它们。

设置基本表单输入

通常,我们将负责手动指定TextEditingController应该使用什么,以及手动设置诸如验证之类的东西。但是有了flutter_form_builder,这两件事就变得微不足道了。

对于文本输入字段,我们指定字段的name参数,如果我们要标记字段,则指定装饰。我们也可以从现有的一组验证器中进行选择,而不是自己编写。这意味着我们的名字和姓氏输入字段如下所示:

FormBuilderTextField(name: FIRST_NAME,decoration: InputDecoration(labelText: 'First Name'),validator: FormBuilderValidators.required(),
),

对于我们的电话号码字段,我们可以利用验证器,而不是编写自己的验证FormBuilderValidators.numeric()器:

FormBuilderTextField(name: PHONE_NUMBER,validator: FormBuilderValidators.numeric(),decoration: InputDecoration(labelText: 'Phone number'),autovalidateMode: AutovalidateMode.always,
),

设置宠物类型选择器

现在,我们希望通过在 Flutter 应用程序中选择适当的单选按钮,为用户提供一个宠物类型选项列表以供选择。我们可以从我们提供的一组枚举中以编程方式生成这个列表。

这意味着如果我们在程序中的枚举中添加或删除选项,选项也会在我们的表单中发生变化。这将比我们自己手动维护列表更容易。

FormBuilderRadioGroup<PetType>(onChanged: (val) {print(val);setState(() {_petType = val;});},name: PET_CHOICE,validator: FormBuilderValidators.required(),orientation: OptionsOrientation.vertical, // Lay out the options verticallyoptions: [// Retrieve all options from the PetType enum and show them as options// Capitalize the first letters of the options as well...PetType.values.map((e) => FormBuilderFieldOption(value: e,child: Text(describeEnum(e).replaceFirst(describeEnum(e)[0],describeEnum(e)[0].toUpperCase(),),),),),],
),

最后设置三个问题

对于 Flutter 表单的这一部分,我们的 builder 方法基本保持不变,但有几个重要区别:我们现在使用FormBuilderTextField类作为输入,并通过name参数将它们与表单中的适当条目相关联。

case PetType.cat:return Column(children: [Text("Aw, it's a cat!"),FormBuilderTextField(name: QUESTION_ANSWER_1,decoration: InputDecoration(labelText: 'Can we pat the cat?'),),FormBuilderTextField(name: QUESTION_ANSWER_2,decoration: InputDecoration(labelText: 'Can we put a little outfit on it?'),),FormBuilderTextField(name: QUESTION_ANSWER_3,decoration: InputDecoration(labelText: 'Does it like to jump in boxes?'),),],);

验证和检索表单中的值

设置好响应式 Flutter 表单后,我们现在需要做最后两件事:验证表单中是否包含可用数据并从表单中检索这些值。

幸运的是,因为我们已经在每个字段本身中设置了验证要求,所以我们的验证变得非常简单:

final valid = _fbKey.currentState?.saveAndValidate() ?? false;

这个操作的结果是,如果我们的表单的当前状态不是null,并且当前被认为valid是——即所有表单字段都通过了验证——那么,表单被认为是有效的。如果currentState是null,或形式是invalid,这个变量将改为返回false。

在成功结果的情况下,这些值将显示给用户。currentState我们可以通过访问对象中的对象轻松访问表单中的值_fbKey。

showDialog(context: context,builder: (context) => SimpleDialog(contentPadding: EdgeInsets.all(20),title: Text("All done!"),children: [Text("Thanks for all the details! We're going to check your pet in with the following details.",style: Theme.of(context).textTheme.caption,),Card(child: Column(children: [// It's okay to use the ! operator with currentState, because we// already checked that it wasn't null when we did the form// validationText('First name: ${_fbKey.currentState!.value[FIRST_NAME]}'),Text('Last name: ${_fbKey.currentState!.value[LAST_NAME]}'),Text('Number: ${_fbKey.currentState!.value[PHONE_NUMBER]}'),Text('Pet type: ${_fbKey.currentState!.value[PET_CHOICE]}'),Text('Response 1: ${_fbKey.currentState!.value[QUESTION_ANSWER_1]}'),Text('Response 2: ${_fbKey.currentState!.value[QUESTION_ANSWER_2]}'),Text('Response 3: ${_fbKey.currentState!.value[QUESTION_ANSWER_3]}'),],),)],),
);

包起来

正如我们所看到的,使用flutter_form_builder在 Flutter 中创建响应式表单可以为我们作为开发人员带来许多改进。与往常一样,您可以在 Github 中浏览此项目的代码,以了解如何flutter_form_builder在您的项目中使用。

您还可以使用下面的这些链接在两个提交之间进行比较,以准确了解项目的变化情况:

  • 没有flutter_form_builder

  • 实施后flutter_form_builder

有很多不同类型的字段flutter_form_builder可以开箱即用,因此您应该始终能够根据需要使用正确的字段类型。

在 Flutter 中创建响应式表单相关推荐

  1. Angular中响应式表单 FormBuilder、FormControl 、FormGroup、FormArray、setControl、setValue用法总结

    以我的项目作为示例,总结一下Angular响应式表单的应用和常用的方法: 1.创建表单 form.ts代码 import { Component, OnInit } from "@angul ...

  2. Angular响应式表单及表单验证

    1. 什么是响应式表单? 响应式表单提供了一种模型驱动的方式来处理表单输入,其中的值会随时间而变化. 响应式表单使用显示的,不可变的方式,管理表单在特定时间点上的状态.对表单状态的每一次变更都会返回一 ...

  3. Angular2响应式表单

    本文将半翻译半总结的讲讲ng2官网的另一个未翻译高级教程页面. 原文地址. 文章目的是使用ng2提供的响应式表单技术快速搭出功能完善丰富的界面表单组件. 响应式表单是一项响应式风格的ng2技术,本文将 ...

  4. angular6的响应式表单

    1:在AppModule模块里面引入 ReactiveFormsModule 要使用响应式表单,就要从@angular/forms包中导入ReactiveFormsModule,并把它添加到你的NgM ...

  5. Angular Reactive Forms -- Model-Driven Forms响应式表单

    Angular 4.x 中有两种表单: Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )  官方文档:https://v2.angul ...

  6. angularjs 表单校验指令_angular4.0的模板式表单、响应式表单及其错误提示

    模板式表单 NgForm.NgModel.NgModelGroup是FormModule里的内容,NgForm会自动拦截标准的表单处理事件(eg.提交),angular用ngSubmit代替标准的表单 ...

  7. Angular 响应式表单 patchValue和setValue

    在 Angular 4 中有多种方式可以更新表单的值,对于使用响应式表单的场景,我们可以通过框架内部提供的 API ,(如 patchValue 和 setValue )方便地更新表单的值.这篇文章我 ...

  8. Angular4模板式表单、响应式表单、表单状态字段

    时隔十多天我(独臂侠 灰常怀念两只胳膊~~(><)~~)又来了,之前是第三章的内容,今天先写*第七章表单处理*的内容吧,内容太多了,有必要记下来,如果有幸被看到欢迎指正^^ 表单有: - ...

  9. Angular 响应式表单 基础例子

    1.案例需求 表单提交,表单全部校验成功才能提交,当表单校验错误,表单边框变红,同时有错误提示信息,有重置功能 2.代码分析 在线预览 git仓库 本案例中使用了响应式表单,响应式表单在表单的校验方面 ...

  10. angular7+ngzorro响应式表单验证

    使用响应式表单在开发的过程中,经常会用到.使用响应式表单的不仅能够提高开发效率,并且还能有效减少冗余的代码,在表单校验的时候也更方面,并且结合ngzorro进行表单校验效果更完美. 首先引入在对应的模 ...

最新文章

  1. 微信小程序开发之https从无到有
  2. 电脑雕刻教程_湖南益阳3DMAX建模培训入门教程【仁厚教育】
  3. 【C++】 C++标准模板库(六) Queue
  4. 未来ui设计的发展趋势_2025年的未来UI趋势?
  5. oracle表空间如何压缩,Oracle里表空间的压缩
  6. 深入理解Python中的全局解释锁GIL
  7. cesium 局部加载_cesium自定义气泡窗口infoWindow后续优化篇 - GIS之家
  8. C static extern和全局变量
  9. 手机升降式摄像头有什么优缺点?
  10. AC日记——可能的路径 51nod 1247
  11. 弃用 Notepad++ 还有更牛逼的选择
  12. Lamp兄弟连Linux视频教程
  13. 从零开始学Android!渣本毕业两年经验,附超全教程文档
  14. NorthWind基本数据库添加问题
  15. 小米手机刷机ROOT原理
  16. grunt html模块化管理插件,grunt模块化配置
  17. 基于F4/F7/H7飞控硬件和px4飞控固件的廉价自主无人机系统(2)- 外设和固件修改
  18. 什么是PON光模块?PON光模块有哪些作用和类型?
  19. python项目实战:pygame控制键盘方向键随意移动
  20. 全网热议的云原生技术到底什么?看完这25点你就知道了

热门文章

  1. “囍”博物馆与Interesting 有点意思
  2. 目标检测模型组件构成
  3. 计算机专业在房管局,计算机故障 昨天房产局无法办过户
  4. 用于将 InfoPath 2007 集成到 Visual Studio 2005 中的 InfoPath Designer API 概述
  5. python根据生日自动批量产生中奖双色球
  6. 怎么删除TEMP文件夹
  7. ImprovedGAN论文略读
  8. 二分图判定 hdu5285 wyh2000 and pupil
  9. DS1308 Datasheet
  10. python远程访问服务器获取文件