Flutter表单管理插件form_model的使用

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

Flutter表单管理插件form_model的使用

form_model 是一个强大的 Flutter 表单验证包。它提供了易于使用的表单验证功能,内置了多种验证器,并支持国际化。

功能

  • 🚀 简易的表单验证
  • 🎯 单一责任验证器,代码清晰可维护
  • 🌐 内置国际化支持,包含备用语言
  • 🧩 可扩展性,可以添加自定义验证器和翻译
  • 🔄 不可变状态管理
  • 🔗 无缝集成到 Flutter 小部件中
  • 🧩 支持高级表单控件(如日期选择器、多选等)
  • 🧬 与状态管理解决方案(如 BLoC)配合良好
  • 🧪 全面的测试覆盖率

截图

以下是一些 form_model 的运行截图:

Screenshot 1 Screenshot 2 Screenshot 3 Screenshot 4 Screenshot 5 Screenshot 6 Screenshot 7

安装

在你的 pubspec.yaml 文件中添加 form_model

dependencies:
  form_model: ^1.0.2+2

然后运行:

flutter pub get

基本用法

以下是一个简单的 form_model 使用示例:

import 'package:form_model/form_model.dart';

// 创建一个用于电子邮件字段的 FormModel
final emailModel = FormModel<String>(
  validators: [RequiredValidator(), EmailValidator()],
);

// 设置值并验证
final validatedModel = emailModel.setValue('user@example.com').validate();

if (validatedModel.isValid) {
  print('Email is valid: ${validatedModel.value}');
} else {
  print('Validation error: ${validatedModel.error?.translatedMessage}');
}

注意:每个验证器负责检查输入的一个特定方面,并且只返回一种类型的错误。遵循这一原则可以帮助你创建适用于应用的自定义验证器。

高级用法

对于更复杂的表单,你可以组合多个 FormModel 实例。以下是一个带有密码确认字段的示例:

class RegistrationForm {
  final FormModel<String> username;
  final FormModel<String> email;
  final FormModel<String> password;
  late final FormModel<String> confirmPassword;

  RegistrationForm({
    required this.username,
    required this.email,
    required this.password,
  }) {
    confirmPassword = FormModel<String>(
      validators: [
        RequiredValidator(),
        StringConfirmPasswordMatchValidator(matchingValue: password.value),
      ],
    );
  }

  bool get isValid => 
    username.isValid && 
    email.isValid && 
    password.isValid && 
    confirmPassword.isValid;

  void validate() {
    username.validate();
    email.validate();
    password.validate();
    confirmPassword = confirmPassword
      .replaceValidator(
        predicate: (validator) => validator is StringConfirmPasswordMatchValidator,
        newValidator: StringConfirmPasswordMatchValidator(matchingValue: password.value),
      )
      .validate();
  }

  void updatePassword(String newPassword) {
    password.setValue(newPassword);
    // 更新确认密码验证器
    confirmPassword = confirmPassword
      .replaceValidator(
        predicate: (validator) => validator is StringConfirmPasswordMatchValidator,
        newValidator: StringConfirmPasswordMatchValidator(matchingValue: newPassword),
      );
  }
}

此示例展示了如何正确设置和更新密码匹配验证器。

验证器

form_model 包含多种内置验证器,每个验证器负责单个方面的验证:

  • RequiredValidator
  • EmailValidator
  • StringMinLengthValidator
  • StringMaxLengthValidator
  • PasswordLengthValidator
  • PasswordUppercaseValidator
  • PasswordLowercaseValidator
  • PasswordNumberValidator
  • PasswordSpecialCharValidator
  • DateTimeValidator
  • StringDateTimeAgeMinValidator
  • StringDateTimeAgeMaxValidator
  • StringPhoneNumberValidator
  • StringNumbersOnlyValidator
  • StringTextOnlyValidator
  • StringUrlValidator
  • StringWordsCountMinValidator
  • StringWordsCountMaxValidator
  • StringCreditCardValidator
  • CustomEqualValidator
  • StringCustomPatternValidator
  • IpAddressValidator
  • Ipv6AddressValidator
  • StringNumMinValidator
  • StringNumMaxValidator
  • StringContainsValidator
  • StringNotContainsValidator
  • FileTypeValidator
  • FileSizeValidator
  • BoolTrueValidator
  • BoolFalseValidator
  • BoolAgreeToTermsAndConditionsValidator

每个验证器专注于单一的验证规则,符合单一职责原则。

国际化

form_model 支持多种语言:

// 设置当前语言环境
FormModelLocalizations().setCurrentLocale(const Locale('es'));

// 获取翻译后的错误消息
final errorMessage = emailModel.error?.translatedMessage;

支持的语言

form_model 支持以下语言:

  • 英语 (en)
  • 西班牙语 (es)
  • 法语 (fr)
  • 德语 (de)
  • 阿拉伯语 (ar)
  • 中文(简体) (zh_CN)
  • 印地语 (hi)
  • 意大利语 (it)
  • 日语 (ja)
  • 韩语 (ko)
  • 荷兰语 (nl)
  • 葡萄牙语 (pt)
  • 俄语 (ru)
  • 土耳其语 (tr)
  • 乌克兰语 (uk)
  • 越南语 (vi)
  • 波兰语 (pl)
  • 瑞典语 (sv)

与 BLoC 和 Freezed 集成

form_model 可以无缝集成到 BLoC 模式中。以下是一个使用 Freezed 的 Flutter BLoC 状态示例:

@freezed
class RegistrationState with _$RegistrationState {
  const factory RegistrationState({
    @Default(FormModel<String>(validators: [RequiredValidator()]))
    FormModel<String> username,
    
    @Default(FormModel<String>(validators: [RequiredValidator(), EmailValidator()]))
    FormModel<String> email,
    
    @Default(FormModel<String>(validators: [
      RequiredValidator(),
      PasswordLengthValidator(minLength: 8),
      PasswordUppercaseValidator(),
      PasswordLowercaseValidator(),
      PasswordNumberValidator(),
      PasswordSpecialCharValidator(),
    ]))
    FormModel<String> password,
    
    @Default(false) bool isSubmitting,
    @Default(false) bool isSuccess,
    String? errorMessage,
  }) = _RegistrationState;
}

自定义验证器

自定义验证器允许你创建未被内置验证器覆盖的特定验证规则。以下是一个简单的自定义验证器示例,该验证器检查字符串是否以特定前缀开头:

String? validatePrefix(String? value) {
  const requiredPrefix = 'USER_';
  if (value == null || !value.startsWith(requiredPrefix)) {
    return 'Value must start with $requiredPrefix';
  }
  return null;
}

final userIdModel = FormModel<String>(
  validators: [
    RequiredValidator(),
    CustomValidator(validator: validatePrefix),
  ],
);

// 使用示例
void validateUserId(String userId) {
  final validatedModel = userIdModel.setValue(userId).validate();
  if (validatedModel.isValid) {
    print('Valid User ID: ${validatedModel.value}');
  } else {
    print('Invalid User ID: ${validatedModel.error?.translatedMessage}');
  }
}

// 示例
validateUserId('USER_123'); // 有效
validateUserId('ADMIN_456'); // 无效

此自定义验证器检查输入字符串是否以前缀 'USER_' 开头。它演示了如何轻松创建应用程序特定的验证逻辑。自定义验证函数应返回一个包含错误消息的字符串(如果验证失败),或者返回 null(如果验证通过)。这种方法允许你扩展 form_model 的能力以满足特定需求,同时保持代码的整洁和模块化。

处理自定义对象

form_model 还可以处理复杂的自定义对象。以下是一个使用 Address 类的示例:

class Address {
  final String street;
  final String city;

  Address({required this.street, required this.city});
}

String? validateStreet(Address? value) {
  if (value == null || value.street.isEmpty) return 'Street is required';
  return null;
}

String? validateCity(Address? value) {
  if (value == null || value.city.isEmpty) return 'City is required';
  return null;
}

final addressModel = FormModel<Address>(
  validators: [
    CustomValidator(validator: validateStreet),
    CustomValidator(validator: validateCity),
  ],
);

// 使用示例
void updateAddress(String street, String city) {
  final newAddress = Address(street: street, city: city);
  addressModel.setValue(newAddress).validate();
}

这种方法允许你在保持单独验证规则分离的同时验证复杂对象。

自定义翻译

你可以为自定义和预定义错误消息设置自定义翻译:

void setCustomTranslations() {
  // 为自定义错误设置自定义翻译
  FormModelLocalizations().setCustomErrorTranslations(
    'en',
    const CustomFormErrorKey('password_no_exclamation'),
    'Password must contain an exclamation mark',
  );

  // 覆盖预定义错误的翻译
  FormModelLocalizations().setCustomErrorTranslations(
    'en',
    const PredefinedFormErrorKey(PredefinedFormErrorType.required),
    'This field cannot be left empty',
  );

  // 为其他语言设置翻译
  FormModelLocalizations().setCustomErrorTranslations(
    'es',
    const CustomFormErrorKey('password_no_exclamation'),
    'La contraseña debe contener un signo de exclamación',
  );

  FormModelLocalizations().setCustomErrorTranslations(
    'es',
    const PredefinedFormErrorKey(PredefinedFormErrorType.required),
    'Este campo no puede estar vacío',
  );
}

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

1 回复

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


当然,下面是一个关于如何在Flutter中使用form_model插件来管理表单的示例代码。form_model插件提供了一种声明式的方式来处理表单状态,使得表单验证和管理变得更加简单。

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

dependencies:
  flutter:
    sdk: flutter
  form_model: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来获取依赖。

接下来,我们将创建一个简单的表单,包含用户名和密码字段,并使用form_model来管理这些字段的状态和验证。

import 'package:flutter/material.dart';
import 'package:form_model/form_model.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Form Model Example'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: MyForm(),
        ),
      ),
    );
  }
}

class MyForm extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FormModel(
      model: MyFormModel(),
      builder: (context, formModel, child) {
        return Column(
          children: [
            TextFormFieldModel(
              formModel: formModel,
              attributeName: 'username',
              decoration: InputDecoration(labelText: 'Username'),
              validators: [
                Validators.required('Username is required'),
                Validators.minLength(3, 'Username must be at least 3 characters long'),
              ],
            ),
            TextFormFieldModel(
              formModel: formModel,
              attributeName: 'password',
              decoration: InputDecoration(labelText: 'Password'),
              obscureText: true,
              validators: [
                Validators.required('Password is required'),
                Validators.minLength(6, 'Password must be at least 6 characters long'),
              ],
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: formModel.isValid ? () => formModel.submit() : null,
              child: Text('Submit'),
            ),
            if (formModel.isSubmitting)
              CircularProgressIndicator(),
            if (formModel.hasError)
              Text(formModel.error ?? ''),
          ],
        );
      },
    );
  }
}

class MyFormModel extends FormModel {
  String? username;
  String? password;

  @override
  void initialize() {
    addAttribute('username', String, [null]);
    addAttribute('password', String, [null]);
  }

  @override
  Future<void> submit() async {
    setState(() {
      isSubmitting = true;
      error = null;
    });

    try {
      // 模拟异步提交操作,例如发送到服务器
      await Future.delayed(Duration(seconds: 2));
      print('Form submitted successfully!');
      // 根据实际情况处理提交结果
    } catch (e) {
      setState(() {
        error = 'Submission failed: $e';
      });
    } finally {
      setState(() {
        isSubmitting = false;
      });
    }
  }
}

在这个示例中,我们做了以下几件事情:

  1. 定义表单模型MyFormModel继承自FormModel,并定义了usernamepassword属性。在initialize方法中,我们通过addAttribute方法注册这些属性。

  2. 创建表单视图MyForm是一个无状态组件,它使用FormModel组件来包装表单,并通过TextFormFieldModel组件来创建具体的输入字段。TextFormFieldModel组件通过attributeName属性与表单模型中的属性关联。

  3. 添加验证规则:在TextFormFieldModel组件中,我们使用了validators属性来定义验证规则。这里使用了Validators.requiredValidators.minLength来验证输入。

  4. 提交表单:在ElevatedButtononPressed回调中,我们检查表单是否有效(formModel.isValid),如果有效则调用formModel.submit方法来提交表单。

  5. 处理提交结果:在MyFormModelsubmit方法中,我们模拟了一个异步提交操作,并在提交完成后更新表单的状态。

这个示例展示了如何使用form_model插件来管理一个简单的Flutter表单,包括输入字段、验证和提交逻辑。你可以根据需要扩展和修改这个示例以适应你的具体需求。

回到顶部