Flutter表单验证与状态管理插件flutter_mobx_form_validation_kit的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter表单验证与状态管理插件flutter_mobx_form_validation_kit的使用

Benefits of the kit

该插件具有以下优点:

  • 兼容Android、iOS、Web、桌面等平台。
  • 兼容Mobx。
  • 设计用于异步验证。
  • 轻松嵌入现有项目。

Getting Started

库可以在不同的代码结构方法中使用,但本文将使用MVC(模型-视图-控制器)模式来描述。换句话说,数据展示通过“傻瓜”组件实现,而业务逻辑(包括验证)嵌入在Stores中。组件将使用React Hooks构建,因为这是一种更现代的方法,但库也适用于基于类的方法。

class RegistrationForm extends ControlsCollection {
  final FormControl<String> name;
  RegistrationForm({this.name});
  [@override](/user/override)
  Iterable<AbstractControl> allFields() => [this.name];
}

class RegistrationStore {
  FormGroup<RegistrationForm> form;

  RegistrationStore() {
    this.form = FormGroup(RegistrationForm(
        name: FormControl(
            value: "",
            options: OptionsFormControl(validators: [requiredValidator()]))));
  }

  void dispose() {
    this.form.dispose();
  }
}
class _RegistrationState extends State<Registration> {
  RegistrationStore store = RegistrationStore();

  [@override](/user/override)
  void dispose() {
    store.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(_) => Observer(builder: (BuildContext context) {
        return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(store.form.controls.name.value, style: TextStyle(fontSize: 20)),
              TextField(
                controller: store.form.controls.name.controller,
                onChanged: (String text) => store.form.controls.name.value = text,
                decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "姓名",
                ),
              ),
            ]);
      });
}

如果你开始输入,错误消息会立即消失。在自然且熟悉的显示格式下,验证错误消息会在输入字符后或失去字段焦点时出现;mobx-form-validation-kit 拥有所有必要的工具来实现这一点。

控制器状态

表单控制状态

现在,让我们讨论控制器嵌套的结构及其功能。mobx-form-validation-kit 库有三种主要类型的节点。

名称 描述
FormGroup 允许将验证组件组合在一起。它是类型化的类,允许你以字段列表作为泛型参数重新设计界面。
FormControl 用于验证特定字段。这是最常用的类。它是一个类型化的类,接受必须存储的变量类型作为泛型参数。
FormArray 允许创建和管理一组验证组件。

这些节点可以以树状结构组装。任何级别的嵌套都受支持,但通常从FormGroup开始。

FormGroup
-- FormControl
-- FormControl
-- -- FormGroup
-- -- FormArray
-- -- -- FormGroup
-- -- --  -- FormControl
-- -- FormArray
-- -- --  FormControl

当定义每个对象类时,它们获得以下选项集:

名称 描述
validators 验证器集合。
activate 函数允许根据条件启用/禁用验证(默认情况下始终启用)。例如,如果未选中“无限制”复选框,则无需验证服务结束日期的有效性。因此,只需在这里添加一个检查“无限”复选框状态的函数,就可以自动禁用与验证日期相关的所有验证,而不是为每个日期字段验证指定此逻辑。
additionalData 一个包含额外信息的块,允许向特定FormControl添加更多信息并稍后使用,例如用于可视化目的。如果有一些FormControl的构建器,你想硬编码某些信息,这允许你避免通过复杂的捆绑数据将这些信息发送到控件进行可视化。尽管我们未能找到additionalData的确切和不可争议的应用场景,但最好有这种选择,而不是没有它。

此外,FormControl还有一组附加选项:

名称 描述
onChangeValue 值变化时总是触发。
onChangeValidValue 发送最后一个有效值。
callSetterOnInitialize 允许在创建FormControl时调用onChangeValidValue
callSetterOnReinitialize 允许每次更改第一个参数的getter函数的结果时调用onChangeValidValue

每个树元素支持以下字段集:

名称 描述
processing 在分析过程中。mobx-form-validation-kit 支持异步验证,例如那些需要服务器请求的验证。你可以通过此字段找到当前的检查状态。
await this.form.wait();
if (this.form.invalid) {
  ...
}
名称 描述
disabled 禁用错误检查(控件始终有效)。
active 启用错误检查。这取决于激活函数的结果。这非常方便地使用,当你想隐藏表单上的一组字段并避免编写额外和重复的业务逻辑函数。
invalid 对于FormControl,这意味着字段包含验证错误。对于FormGroupFormArray,这意味着组控件包含错误或其中一个嵌套字段(在任何级别嵌套)包含验证错误。换句话说,要检查整个表单的有效性,只需执行一次检查是否顶级FormGroup有效或无效。
valid 对于FormControl,这意味着字段不包含验证错误。对于FormGroupFormArray,这意味着组控件不包含错误或没有嵌套字段(在任何级别嵌套)包含验证错误。
pristine 字段在初始化后没有改变其值。
dirty 字段在初始化后改变了其值。
untouched 对于FormControl,这意味着字段(例如输入)没有被关注。对于FormGroupFormArray,这意味着没有嵌套的FormControl被关注。此字段的值为false意味着焦点不仅被设置,而且被移除。
touched 对于FormControl,这意味着字段(例如输入)已经被关注。对于FormGroupFormArray,这意味着一个嵌套的FormControl被关注。此字段的值为true意味着焦点不仅被设置,而且被移除。
focused 对于FormControl,这意味着字段(例如输入)目前处于焦点。对于FormGroupFormArray,这意味着一个嵌套的FormControl目前处于焦点。
errors 此字段包含验证错误。与上述字段不同,此数组包含FormControlFormGroupFormArray的错误,即该控件的错误,而不是所有嵌套的。这影响了有效/无效字段。
warnings 字段包含“注意”消息。与上述字段不同,此数组包含FormControlFormGroupFormArray的错误,即该控件的消息,而不是所有嵌套的。这不影响有效/无效字段。
informationMessages 字段包含“信息消息”。与上述字段不同,此数组包含FormControlFormGroupFormArray的错误,即该控件的消息,而不是所有嵌套的。这不影响有效/无效字段。
successes 字段包含附加的有效性消息。与上述字段不同,此数组包含FormControlFormGroupFormArray的错误,即该控件的消息,而不是所有嵌套的。这不影响有效/无效字段。
maxEventLevel() 返回当前字段中包含的最大验证消息级别。此方法将按以下优先级返回一个枚举值。
1. ValidationEventTypes.Error;
2. ValidationEventTypes.Warning;
3. ValidationEventTypes.Info;
4. ValidationEventTypes.Success;
名称 描述
serverErrors 发送到服务器后,最好检查表单的服务器端有效性。结果,服务器可能会返回表单最终验证的错误,为此类错误设计了serverErrors数组。serverErrors的关键特性是在分配给字段的服务器错误丢失焦点时自动清除验证消息,并且如果字段已被更改,则服务器错误也会被清除。
setDirty(dirty: boolean) 方法允许更改pristine/dirty字段的值。
setTouched(touched: boolean) 方法允许更改untouched/touched字段的值。
setFocused() 方法允许更改focused字段的值(仅对FormControl可用)。
dispose() 必须在页面负责的控件的componentWillUnmount中调用。

注意: 在FormGroupFormArray中,字段validinvalid关注嵌套元素。 通过检查顶层元素,你可以找出表单的所有低级元素的有效性。 但是!FormGroupFormArray节点有自己的验证集和错误列表(错误、警告、信息消息、成功)。换句话说,如果你问FormGroup关于错误,它只会返回自己的错误,而不是嵌套的FormControl的错误。

Validation

mobx-form-validation-kit 允许编写自定义验证,但也包含了一套自己的验证规则。

名称 描述
requiredValidator 检查值是否不为空,并且对于字符串检查字符串是否为空。
notEmptyOrSpacesValidator 检查值是否不为空,并且对于字符串检查字符串是否为空或只包含空格。
notContainSpacesValidator 检查字符串是否包含空格。
patternValidator 如果不匹配模式则返回错误。
invertPatternValidator 如果匹配模式则返回错误。
minLengthValidator 检查字符串的最小长度。
maxLengthValidator 检查字符串的最大长度。
absoluteLengthValidator 检查字符串的特定长度。
isEqualValidator 检查精确值。
compareValidator 是复杂验证的包装器(如果验证返回false则出错)。
firstName: FormControl(
    value: "",
    options: OptionsFormControl(validators: [
    requiredValidator(),
    minLengthValidator(2),
    maxLengthValidator(5),
    notContainSpacesValidator()
])),

如你所见,列表中的所有验证都已执行,这个问题通过使用wrapperSequentialCheck解决。它的调用和使用方式与普通验证器函数相同,但它接受一个验证器数组,这些验证器将依次运行,即只有在前一个验证没有错误的情况下才会启动下一个验证。

第二个包装函数是验证流程控制函数。wrapperActivateValidation的第一个参数接受一个函数,在其中你需要指定激活验证的条件。与传递给FormControlactivate函数不同,这种检查适用于更复杂的逻辑。假设我们有一个通用的FormGroup支付表单构建器,并且服务器只有一个方法接受通用的一组字段。但是问题在于,即使表单是一个,我们需要根据“支付类型”向用户显示不同的字段集。因此,wrapperActivateValidation允许你编写根据支付类型执行各种检查的逻辑。

firstName: FormControl(
          value: "",
          options: OptionsFormControl(validators: [
            wrapperSequentialCheck([
              requiredValidator(),
              minLengthValidator(2),
              maxLengthValidator(5),
              notContainSpacesValidator()
            ])
          ]))

自定义的组验证:

class FormRange extends ControlsCollection {
  final FormControl<DateTime> min;
  final FormControl<DateTime> max;
  FormRange({this.min, this.max});
  [@override](/user/override)
  Iterable<AbstractControl> allFields() => [this.min, this.max];
}

class RegistrationForm extends ControlsCollection {
…
  RegistrationForm(
      {this.firstName, this.lastName, this.email, this.age, this.dateRange});

  [@override](/user/override)
  Iterable<AbstractControl> allFields() =>
      [this.firstName, this.lastName, this.email, this.age, this.dateRange];
}
this.form = FormGroup(RegistrationForm(
...
      dateRange: FormGroup<FormRange>(
          FormRange(
              min: FormControl<DateTime>(value: DateTime.now()),
              max: FormControl<DateTime>(value: DateTime.now())),
          options: OptionsFormGroup<FormRange>(validators: [
            (FormGroup<FormRange> group) async {
              if (group.controls.max.value.isBefore(group.controls.min.value)) {
                return [
                  ValidationEvent(
                    message: '日期“从”晚于日期“到”,'
                    type: ValidationEventTypes.Error,
                  )
                ];
              }
              return [];
            },
          ])),    
));

如你从示例中看到的,验证没有分配给某个具体的东西[虽然这也是可能的],而是分配给了整个组。剩下的就是显示组的错误。

Final Check Before Submission

await this.form.wait();
if (this.form.invalid) {
  this.form.setTouched(true);
  final firstError = this.form.allControls().firstWhere((c) => c.invalid);
  firstError.focusNode.requestFocus();
}

Conclusion

很明显,库的功能不仅仅局限于描述的用例。我们可以提供更多用例并提供其他示例。

  • 这些验证不仅会在值更改时工作,还会在验证内的可观察变量更改时工作。
  • active方法的工作方式。
  • FormControl 可以不仅由特定变量初始化,还可以由返回值的函数初始化。此函数内的更改也将被跟踪。
  • 如何使用FormArray
  • 如何在向服务器发起请求的同时进行复杂验证,并确保此验证中的变量仍然被跟踪。

还有很多其他功能,使得mobx-form-validation-kit可以开箱即用。


PS 如果您发现任何错误,请联系我们,我们将修复它 :)

示例代码

以下是示例代码:

import 'package:flutter/material.dart';
import 'components/registration/registration.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Scaffold(body: SafeArea(child: Registration())),
    );
  }
}

更多关于Flutter表单验证与状态管理插件flutter_mobx_form_validation_kit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter表单验证与状态管理插件flutter_mobx_form_validation_kit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用flutter_mobx_form_validation_kit插件进行表单验证和状态管理的代码示例。这个插件结合了MobX状态管理和表单验证功能,使得表单处理更加简洁和高效。

首先,确保你已经在pubspec.yaml文件中添加了依赖项:

dependencies:
  flutter:
    sdk: flutter
  flutter_mobx: ^2.0.0
  mobx_flutter: ^2.0.0
  flutter_mobx_form_validation_kit: ^x.y.z  # 请替换为最新版本号

然后,运行flutter pub get来安装这些依赖。

接下来,让我们创建一个简单的表单示例,其中包含用户名和密码字段,并使用flutter_mobx_form_validation_kit进行验证。

1. 创建Store类

首先,我们需要创建一个MobX store类来管理表单的状态和验证逻辑。

import 'package:mobx/mobx.dart';
import 'package:flutter_mobx_form_validation_kit/flutter_mobx_form_validation_kit.dart';

part 'form_store.g.dart';

class FormStore = _FormStore with _$FormStore;

abstract class _FormStore with Store {
  // 表单字段
  @observable
  String username = '';

  @observable
  String password = '';

  // 表单验证规则
  final usernameValidationRule = ValidationRule<String>(
    validations: [
      RequiredValidation(),
      MinLengthValidation(minLength: 3),
    ],
  );

  final passwordValidationRule = ValidationRule<String>(
    validations: [
      RequiredValidation(),
      MinLengthValidation(minLength: 6),
    ],
  );

  // 表单状态
  @computed
  bool get isFormValid =>
      usernameValidationRule.isValid(username) &&
      passwordValidationRule.isValid(password);

  // 提交表单
  void submitForm() {
    if (isFormValid) {
      // 执行提交逻辑,例如发送到服务器
      print('Form Submitted');
    }
  }
}

运行flutter pub run build_runner build来生成form_store.g.dart文件。

2. 创建UI组件

接下来,我们创建一个Flutter UI组件来使用这个store。

import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'form_store.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final store = FormStore();
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Form Validation with flutter_mobx_form_validation_kit'),
        ),
        body: Observer(
          builder: (_) => Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              children: [
                TextFormField(
                  decoration: InputDecoration(labelText: 'Username'),
                  onChanged: (value) => store.username = value,
                  validator: (value) => store.usernameValidationRule.validate(value),
                ),
                SizedBox(height: 16),
                TextFormField(
                  decoration: InputDecoration(labelText: 'Password'),
                  obscureText: true,
                  onChanged: (value) => store.password = value,
                  validator: (value) => store.passwordValidationRule.validate(value),
                ),
                SizedBox(height: 16),
                ElevatedButton(
                  onPressed: store.isFormValid ? () => store.submitForm() : null,
                  child: Text('Submit'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

3. 运行应用

现在,你可以运行你的Flutter应用。你应该会看到一个包含用户名和密码字段的表单,并且这些字段会根据你定义的验证规则进行验证。如果表单有效,提交按钮将变为可点击状态,并且点击后会打印“Form Submitted”。

这个示例展示了如何使用flutter_mobx_form_validation_kit进行表单验证和状态管理。你可以根据需要扩展和修改这个示例以适应你的具体需求。

回到顶部