Flutter表单管理插件fpformz的使用
Flutter表单管理插件fpformz的使用
FPFormz
FPFormz 是一个基于 Fpdart 的功能输入验证库,受到 Formz 库的启发。
特点
FPFormz 大部分与原始的 Formz 库相似,但有几个显著的不同点:
- FPFormz 允许为输入和验证值指定不同的类型,这在使用非字符串类型的值(例如
int
、枚举等)时非常方便。 - 它将验证值和错误作为函数式构造(如
Either
或Option
)暴露出来,使它们更容易以声明性方式处理。 - 它还提供了一种方法,可以将验证逻辑编写为混入类,可以组合这些混入类来处理更复杂的用例。
安装
你可以在 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.'));
}
你可以使用 pristine
或 dirty
构造函数来创建实例:
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);
}
然后可以通过添加到 BaseFormInput
或 StringFormInput
中来构建具体的输入字段:
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
更多关于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: 定义表单字段和验证逻辑
首先,我们需要定义表单字段和它们的验证逻辑。使用fpformz
的FormzField
和FormzInput
类来创建这些字段。
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内置的Form
和TextFormField
的validator
和onChanged
回调。这是因为fpformz
库本身并不强制要求使用特定的UI框架或组件,而是提供了表单状态管理的底层逻辑。你可以根据自己的需求选择合适的UI组件和状态管理方式。