Flutter表单管理插件fpformz的使用

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

Flutter表单管理插件fpformz的使用

FPFormz

FPFormz 是一个基于 Fpdart 的功能输入验证库,受到 Formz 库的启发。

特点

FPFormz 大部分与原始的 Formz 库相似,但有几个显著的不同点:

  • FPFormz 允许为输入和验证值指定不同的类型,这在使用非字符串类型的值(例如 int、枚举等)时非常方便。
  • 它将验证值和错误作为函数式构造(如 EitherOption)暴露出来,使它们更容易以声明性方式处理。
  • 它还提供了一种方法,可以将验证逻辑编写为混入类,可以组合这些混入类来处理更复杂的用例。

安装

你可以在 pubspec.yaml 文件中添加以下条目来安装 FPFormz:

dependencies:
  fpformz: ^0.1.3

入门指南

FormInput 及其衍生类

为了定义一个可验证的输入,你需要编写一个扩展自 FormInput<V, I, E> 的类,其中泛型参数分别对应结果值类型、输入值类型和潜在错误类型:

class AgeInput extends FormInput<int, String, ValidationError> {
  const AgeInput.pristine(name, value) : super.pristine(name, value);
  const AgeInput.dirty(name, value) : super.dirty(name, value);

  Either<ValidationError, int> validate(String value) =>
      Either.tryCatch(() => int.parse(value),
          (e, s) => ValidationError(name, '$name should be a number.'));
}

你可以使用 pristinedirty 构造函数来创建实例:

void example() {
  // 创建一个未修改的 ('pristine') 输入实例:
  final age = AgeInput.pristine('age', '');

  // 或者创建一个已修改的 ('dirty') 输入实例:
  final editedAge = AgeInput.dirty('age', '23');

  print(age.isPristine); // 返回 'true'
  print(editedAge.isPristine); // 返回 'false'
}

你可以通过函数式构造或可空值来访问验证信息:

void example() {
  print(editedAge.isValid); // 返回 true
  print(editedAge.result); // 返回 Right(23)
  print(editedAge.resultOrNull); // 返回 23
  print(editedAge.error); // 返回 None
  print(editedAge.errorOrNull); // 返回 null

  print(age.isValid); // 返回 false
  print(age.result); // 返回 Left(ValidationError)
  print(age.resultOrNull); // 返回 null
  print(age.error); // 返回 Some(ValidationError)
  print(age.errorOrNull); // 返回 ValidationError
}

由于大多数输入组件将用户输入视为 String 实例,你可以简化类型签名,通过继承 StringFormInput

class NameInput extends StringFormInput<String, ValidationError> {
  const NameInput.pristine(name, value) : super.pristine(name, value);
  const NameInput.dirty(name, value) : super.dirty(name, value);

  [@override](/user/override)
  String convert(String value) => value;

  [@override](/user/override)
  Either<ValidationError, String> validate(String value) =>
      value.isEmpty
          ? Either.left(ValidationError(name, 'The name cannot be empty.'))
          : super.validate(value);
}

表单

与 Formz 类似,你可以创建一个表单类来托管多个输入字段并一起验证它们:

class RegistrationForm extends Form {
  final NameInput name;
  final EmailInput email;

  const RegistrationForm({
    this.name = const NameInput.pristine('name', ''),
    this.email = const EmailInput.pristine('email', '')
  });

  [@override](/user/override)
  get inputs => [name, email];
}

然后你可以使用类似于 FormInput 的 API 来验证它:

void example() {
  final form = RegistrationForm();

  print(form.isPristine); // 返回 true
  print(form.isValid); // 返回 false

  print(form.result); // 它在遇到第一个错误时会‘短路’
  print(form.errors); // 但是你可以通过这种方式获取所有错误。
}

Form.result 返回一个包含所有验证输入值的映射,你可以使用它来调用服务方法:

void example() {
  final form = RegistrationForm();

  final params = form.resultOrNull;

  service.register(params[form.email.name], params[form.password.name]);

  // 或者,如果输入名称与参数匹配,可以这样做:  
  Function.apply(service.register, [], params);
}

混入类

你还可以将可重用的验证逻辑编写为一个混入类:

@immutable
mixin NonEmptyString<V> on FormInput<V, String, ValidationError> {
  ValidationError get whenEmpty => ValidationError(name, 'Please enter $name.');

  [@override](/user/override)
  Either<ValidationError, V> validate(String value) =>
      value.isEmpty ? Either.left(whenEmpty) : super.validate(value);
}

然后可以通过添加到 BaseFormInputStringFormInput 中来构建具体的输入字段:

class EmailInput extends StringFormInput<String, ValidationError>
    with EmailString, NonEmptyString {
  const EmailInput.pristine(name, value) : super.pristine(name, value);
  const EmailInput.dirty(name, value) : super.dirty(name, value);

  [@override](/user/override)
  String convert(String value) => value;
}

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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用fpformz插件来管理表单状态的代码示例。fpformz是一个用于表单状态管理的Dart库,可以帮助你更轻松地处理表单验证和数据绑定。

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

dependencies:
  flutter:
    sdk: flutter
  fpformz: ^最新版本号 # 请替换为当前最新版本号

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

接下来,我们创建一个简单的表单示例,包括用户名和密码字段,并添加一些基本的验证规则。

步骤 1: 定义表单字段和验证逻辑

首先,我们需要定义表单字段和它们的验证逻辑。使用fpformzFormzFieldFormzInput类来创建这些字段。

import 'package:fpformz/fpformz.dart';

class Username extends FormzInput<String> {
  const Username.pure() : super.pure('');
  const Username.dirty([String value]) : super.dirty(value ?? '');

  @override
  FormzInputStatus get status => value.isEmpty
      ? FormzInputStatus.error
      : FormzInputStatus.valid;

  @override
  String? get errorText => value.isEmpty ? 'Username is required' : null;
}

class Password extends FormzInput<String> {
  const Password.pure() : super.pure('');
  const Password.dirty([String value]) : super.dirty(value ?? '');

  @override
  FormzInputStatus get status => value.length < 6
      ? FormzInputStatus.error
      : FormzInputStatus.valid;

  @override
  String? get errorText => value.length < 6 ? 'Password must be at least 6 characters' : null;
}

步骤 2: 创建表单类

接下来,我们创建一个表单类来管理这些字段。使用Formz类来创建表单。

import 'package:fpformz/fpformz.dart';

class LoginForm extends Formz<LoginForm> {
  final Username username;
  final Password password;

  LoginForm({
    required this.username,
    required this.password,
  }) : super.pure([username, password]);

  LoginForm.from(Map<String, dynamic> json)
      : username = Username.dirty(json['username'] as String?),
        password = Password.dirty(json['password'] as String?),
        super.dirty([username, password]);

  @override
  List<FormzField> get fields => [username, password];

  @override
  FormzStatus get status => fields.every((field) => field.status == FormzInputStatus.valid)
      ? FormzStatus.valid
      : FormzStatus.submissionFailed;

  Map<String, dynamic> toJson() {
    return {
      'username': username.value,
      'password': password.value,
    };
  }
}

步骤 3: 在Flutter UI中使用表单

最后,我们在Flutter的UI中使用这个表单。使用FormzProvider来提供表单状态,并使用Consumer来监听表单状态的变化。

import 'package:flutter/material.dart';
import 'package:fpformz/fpformz.dart';
import 'login_form.dart'; // 假设LoginForm类定义在这个文件中

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: LoginScreen(),
    );
  }
}

class LoginScreen extends StatelessWidget {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Login'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: FormzProvider<LoginForm>(
          create: () => LoginForm(
            username: Username.pure(),
            password: Password.pure(),
          ),
          child: Column(
            children: <Widget>[
              TextFormField(
                key: const ValueKey('username'),
                decoration: InputDecoration(labelText: 'Username'),
                validator: (value) {
                  final formzField = context.read<LoginForm>().username;
                  return formzField.errorText;
                },
                onChanged: (value) {
                  context.read<LoginForm>().username.updateValue(value);
                },
                initialValue: context.read<LoginForm>().username.value,
              ),
              TextFormField(
                key: const ValueKey('password'),
                decoration: InputDecoration(labelText: 'Password'),
                obscureText: true,
                validator: (value) {
                  final formzField = context.read<LoginForm>().password;
                  return formzField.errorText;
                },
                onChanged: (value) {
                  context.read<LoginForm>().password.updateValue(value);
                },
                initialValue: context.read<LoginForm>().password.value,
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  final form = context.read<LoginForm>();
                  if (form.status == FormzStatus.valid) {
                    // 提交表单数据
                    print(form.toJson());
                  } else {
                    // 表单验证失败,显示错误信息
                    _formKey.currentState?.validate();
                  }
                },
                child: Text('Login'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

注意:在上面的代码中,我们使用了context.read<LoginForm>()来访问表单状态,并且手动更新了表单字段的值。这种方法可以工作,但在实际开发中,你可能会考虑使用更高级的状态管理解决方案(如Provider、Riverpod或GetX)来更简洁地管理表单状态。

此外,上面的代码示例并没有完全依赖FormzProvider的自动状态管理功能,而是结合了Flutter内置的FormTextFormFieldvalidatoronChanged回调。这是因为fpformz库本身并不强制要求使用特定的UI框架或组件,而是提供了表单状态管理的底层逻辑。你可以根据自己的需求选择合适的UI组件和状态管理方式。

回到顶部