Flutter表单构建插件former的使用
Flutter表单构建插件former的使用
Motivation
在React中,Formik
是一个非常受欢迎的表单库。它通过大幅减少跟踪字段值、验证和表单提交所需的样板代码,极大简化了表单的开发过程。
在Flutter中,表单构建也面临类似的问题:
- 开发者需要手动跟踪字段值,例如使用
TextEditingController
。 - 验证和错误处理需要编写显式的逻辑。
为了解决这些问题,former
应运而生。
Installation
当前最新版本为:0.2.0
注意:此包仍处于预发布阶段,未来API可能会发生重大变化。
在你的 pubspec.yaml
文件中添加以下依赖:
dependencies:
# 其他依赖...
former: # 可选锁定版本
dev_dependencies:
# 其他依赖...
former_gen: # 可选锁定版本
然后运行以下命令以安装依赖:
flutter pub get
Features
former
提供以下功能:
- 全局启用/禁用表单
- 声明式表单验证
- 自动跟踪字段值
- 使用
FormerError
小部件轻松处理错误 - 类型安全的表单访问
Usage
以下是 former
的使用方法。
创建表单
former
通过分析你的表单类并生成相应的代码来与 former
API 配合使用。
首先,在 my_form.dart
中创建表单类:
import 'package:former_gen/former_gen.dart';
@Formable()
abstract class _MyForm extends FormerForm {
}
需要注意以下几点:
- 表单类是抽象且私有的,因为需要混合一些逻辑才能被
former
使用。 - 表单类扩展自
FormerForm
,它将表单类与former
内部进行接口化。
FormerForm
要求子类实现方括号运算符。由于这是抽象类,这种负担将由 former
的代码生成器处理。我们只需要实现 submit
方法即可。例如,可以提交表单到某个API进行进一步处理。
为了简单起见,我们的 submit
方法仅返回一个空的 Future
:
@Formable()
abstract class _MyForm extends FormerForm {
[@override](/user/override)
Future<void> submit(BuildContext context) {
// TODO: 实现提交逻辑
return Future.value();
}
}
接下来,为表单添加一些字段:
@Formable()
abstract class _MyForm extends FormerForm {
String username = '';
String email = '';
[@override](/user/override)
Future<void> submit(BuildContext context) {
return Future.value();
}
}
直到我们混入生成的混合类(使表单支持方括号运算符并包含字段类型信息),该表单类才可用。在类声明之前添加以下代码:
class MyForm = _MyForm with _$MyForm;
同时添加以下导入语句以引入生成的代码:
part 'my_form.g.dart';
Dart 分析器会提示未识别的符号和导入。要解决此问题,请通过 build_runner
启动代码生成:
flutter pub run build_runner build
指定表单需求
假设我们的表单有以下要求:
- 用户名长度应在10到50个字符之间。
- 邮箱字段必须包含有效的邮箱地址。
在 my_form.dart
中创建表单模式类:
final schema = MyFormSchema(
username: StringMust()
..hasMinLength(10)
..hasMaxLength(50),
email: StringMust()
..beAnEmail(),
);
可以看到,API 非常直观。注意使用了级联操作符 ..
,在 Dart 中,它比返回 this
更常用。
构建表单控件
former
导出了各种与表单交互的小部件。首先,创建表单小部件:
import 'package:flutter/material.dart';
import 'package:former/former.dart';
import 'my_form.dart';
class Form extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Column(
children: [
FormerTextField<MyForm>(field: MyFormField.username),
FormerTextField<MyForm>(field: MyFormField.email),
ElevatedButton(
onPressed: () {
Former.of<MyForm>(context, listen: false).submit();
},
child: Text('提交表单')),
],
);
}
}
表单包含两个文本字段,分别控制 username
和 email
字段。MyFormField
类是自动生成的,无需手动创建。
当按钮被点击时,调用 MyForm
的 submit
方法提交表单。此外,Former.of(context)
还提供了以下功能:
- 访问当前表单的
.form
属性。例如,可以通过Former.of<MyForm>(context).form.username
获取用户名字段的当前值。 - 使用
.isFormEnabled
属性启用或禁用表单。 - 使用
.errorOf(field)
获取指定字段的错误信息。
这是一个极简化的表单示例,实际应用中每个 Former
控件应附带描述其用途的标签。目前,这需要手动完成。
完整示例
最后,将表单包裹在 Former
小部件中:
import 'package:flutter/material.dart';
import 'package:former/former.dart';
import 'my_form.dart';
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Former(
form: () => MyForm(),
schema: () => schema, // 导出自 my_form.dart
child: _MyForm(),
),
),
);
}
}
Source Code
完整的源代码可以在 example
文件夹中找到。
API
可用的小部件
以下小部件可用于 former
:
FormerTextField
FormerCheckbox
FormerSwitch
FormerSlider
正在开发中的小部件:
FormerRadio
FormerDropdownButton
Schema
以下验证器可用于表单字段。每个验证器都有各种方法对给定值施加额外的要求(称为“需求方法”)。
每种需求方法都接受可选的错误消息参数,当值不符合要求时返回该消息。
可用的验证器:
StringMust
NumberMust
BoolMust
可以通过实现 Validator
类来自定义验证逻辑。
示例代码
以下是完整示例代码:
import 'package:example/my_form.dart';
import 'package:flutter/material.dart';
import 'package:former/former.dart';
import 'package:former/validators.dart';
void main() {
runApp(FormerExampleApp());
}
class FormerExampleApp extends StatelessWidget {
const FormerExampleApp();
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Former 示例',
home: Scaffold(
body: SafeArea(
child: Former<MyForm>(
form: () => MyForm(),
schema: () => MyFormSchema(
username: StringMust()
..hasMinLength(10)
..hasMaxLength(50),
email: StringMust()..beAnEmail(),
age: NumberMust()
..beAtLeast(1)
..beAtMost(150),
shouldEnableAnalytics: BoolMust()..exist(),
shouldSendNewsletter: BoolMust()..exist(),
),
child: _Form(),
),
),
),
);
}
}
class _Form extends StatefulWidget {
const _Form();
[@override](/user/override)
__FormState createState() => __FormState();
}
class __FormState extends State<_Form> {
bool _isFormEnabled = true;
void _toggleForm(bool isEnabled) {
Former.of(context, listen: false).isFormEnabled = isEnabled;
setState(() {
_isFormEnabled = isEnabled;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('启用表单?'),
Switch(
value: _isFormEnabled,
onChanged: _toggleForm,
),
],
),
FormerTextField<MyForm>(field: MyFormField.username),
FormerError<MyForm>(field: MyFormField.username),
FormerTextField<MyForm>(field: MyFormField.email),
FormerError<MyForm>(field: MyFormField.email),
FormerSlider<MyForm>(field: MyFormField.age, min: 1, max: 100),
FormerError<MyForm>(field: MyFormField.age),
FormerCheckbox<MyForm>(field: MyFormField.shouldEnableAnalytics),
FormerError<MyForm>(field: MyFormField.shouldEnableAnalytics),
FormerSwitch<MyForm>(field: MyFormField.shouldSendNewsletter),
FormerError<MyForm>(field: MyFormField.shouldSendNewsletter),
ElevatedButton(
onPressed: () {
Former.of<MyForm>(context, listen: false).submit();
},
child: Text('提交'),
),
],
);
}
}
更多关于Flutter表单构建插件former的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html