Flutter表单构建插件ezy_form的使用
Flutter表单构建插件ezy_form的使用
“Ezy Form” 是一个用于简化 Flutter 应用中表单处理的 Flutter 包。它提供了简化且高效的表单管理方式,包括表单验证、数据输入处理、错误报告和状态管理等功能。通过该包,开发者可以轻松高效地创建交互式和动态表单。
安装与导入库
在 pubspec.yaml
文件中添加依赖:
dependencies:
ezy_form: <最新版本>
然后运行 flutter pub get
来安装依赖。
使用说明
声明一个表单
首先需要声明一个 FormGroup
对象,其中包含所有表单字段及其验证器。
final form = FormGroup({
'name': FormControl<String>(null, validators: [requiredValidator]),
'email': FormControl<String>(null, validators: [requiredValidator]),
'gender': FormControl<String>(null, validators: [requiredValidator]),
'agreed': FormControl<bool>(false, validators: [requiredTrueValidator]),
'tags': FormArrayControl<String>(null, validators: [requiredValidator]),
'info': FormGroup({
'firstName': FormControl<String>("fistname", validators: [requiredValidator]),
'lastName': FormControl<String>('last', validators: [requiredValidator]),
}),
});
注意,所有的 [FormControl]
或 [FormArrayControl]
必须放在 [FormGroup]
中。
渲染表单组件
使用 EzyFormWidget
渲染表单组件,并传递已声明的表单对象。
EzyFormWidget(
formGroup: form, // 上面声明的表单
builder: (context, model) {
// 添加表单字段
}
)
渲染每个字段
每个表单字段可以通过 EzyFormControl
或 EzyFormArrayControl
来渲染。
EzyFormControl<String>(
formControlName: 'email',
builder: (context, control) => TextField(
onChanged: (value) => control.setValue(value),
decoration: InputDecoration(
labelText: 'Email',
errorText: control.valid ? null : 'Email is required',
),
)
)
对于表单数组字段,可以使用 EzyFormArrayControl
来渲染,并通过 arrayControl.add()
方法来添加子控件。
EzyFormArrayControl<String>(
formControlName: 'tags',
builder: (context, arrayControl) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text('Tags:'),
TextButton.icon(
onPressed: () => arrayControl.add(),
icon: Icon(Icons.add),
label: Text('Add Tag')
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: (arrayControl.controls ?? [])
.mapIndexed((control, index) => TextFormField(
key: ValueKey(control.hashCode + index),
initialValue: control.value,
decoration: InputDecoration(
labelText: 'Tag $index',
errorText: control.valid ? null : 'Tag $index is required',
suffixIcon: IconButton(
icon: Icon(Icons.remove),
onPressed: () => arrayControl.remove(index),
),
),
onChanged: (value) => control.setValue(value),
))
.toList(),
),
],
),
)
在其他地方消费表单
你可以使用 EzyFormConsumer
来访问周围的 EzyFormWidget
。
EzyFormConsumer(
builder:(context, form){
// 访问表单数据
}
)
示例代码
以下是一个完整的示例代码,展示了如何使用 ezy_form
构建一个动态表单:
import 'package:ezy_form/ezy_form.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const DynamicFormExample(),
);
}
}
final form = FormGroup({
'name': FormControl<String>(null, validators: [requiredValidator]),
'email': FormControl<String>(null, validators: [requiredValidator]),
'gender': FormControl<String>(null, validators: [requiredValidator]),
'agreed': FormControl<bool>(false, validators: [requiredTrueValidator]),
'tags': FormArrayControl<String>(null, validators: [requiredValidator]),
'info': FormGroup({
'firstName': FormControl<String>("fistname", validators: [requiredValidator]),
'lastName': FormControl<String>('last', validators: [requiredValidator]),
}),
});
class DynamicFormExample extends StatelessWidget {
const DynamicFormExample({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Dynamic Form Example')),
body: SingleChildScrollView(
child: EzyFormWidget(
formGroup: form,
builder: (context, model) => Column(
children: [
EzyFormControl<String>(
formControlName: 'info.firstName',
builder: (context, control) => ControlledTextField(
onTextChanged: (value) => control.setValue(value),
onFormControlReset: (fn) => control.onReset(fn),
onDirty: () => control.markAsDirty(),
onTouched: () => control.markAsTouched(),
decoration: InputDecoration(
labelText: 'First Name',
errorText: control.valid ? null : 'First name is required',
helperText: control.dirty
? 'First name is dirty'
: control.touched
? 'First name is touched'
: null,
),
),
),
EzyFormControl<String>(
formControlName: 'name',
builder: (context, control) => ControlledTextField(
onTextChanged: (value) => control.setValue(value),
onFormControlReset: (fn) => control.onReset(fn),
onDirty: () => control.markAsDirty(),
onTouched: () => control.markAsTouched(),
decoration: InputDecoration(
labelText: 'Name',
errorText: control.valid ? null : 'Name is required',
helperText: control.dirty
? 'Name is dirty'
: control.touched
? 'Name is touched'
: null,
),
),
),
EzyFormControl<String>(
formControlName: 'email',
builder: (context, control) => TextField(
onChanged: (value) => control.setValue(value),
decoration: InputDecoration(
labelText: 'Email',
errorText: control.valid ? null : 'Email is required',
),
),
),
EzyFormControl<String>(
formControlName: 'gender',
builder: (context, control) => DropdownButtonFormField<String>(
value: control.value,
onChanged: (value) => control.setValue(value),
decoration: InputDecoration(
labelText: 'Gender',
errorText: control.valid ? null : 'Gender is required',
),
items: const [
DropdownMenuItem(value: 'male', child: Text('Male')),
DropdownMenuItem(value: 'female', child: Text('Female')),
],
),
),
EzyFormArrayControl<String>(
formControlName: 'tags',
builder: (context, arrayControl) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text('Tags:'),
TextButton.icon(
onPressed: () => arrayControl.add(),
icon: Icon(Icons.add),
label: Text('Add Tag')),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: (arrayControl.controls ?? [])
.mapIndexed((control, index) => TextFormField(
key: ValueKey(control.hashCode + index),
initialValue: control.value,
decoration: InputDecoration(
labelText: 'Tag $index',
errorText: control.valid
? null
: 'Tag $index is required',
suffixIcon: IconButton(
icon: Icon(Icons.remove),
onPressed: () =>
arrayControl.remove(index),
)),
onChanged: (value) => control.setValue(value),
))
.toList(),
),
],
),
),
EzyFormControl<bool>(
formControlName: 'agreed',
builder: (context, control) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
CheckboxListTile(
value: control.value ?? false,
onChanged: (value) => control.setValue(value),
title: const Text('I agree'),
),
if (!control.valid)
const Text(
'You must agree to the terms and conditions',
style: TextStyle(color: Colors.red),
),
],
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
model.validate();
print(model.values);
},
child: const Text('Submit'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
model.reset();
print(model.values);
},
child: const Text('reset'),
)
],
),
),
),
);
}
}
class ControlledTextField extends HookWidget {
const ControlledTextField({
super.key,
this.decoration,
this.onTextChanged,
this.onFormControlReset,
this.onTouched,
this.onDirty,
});
final InputDecoration? decoration;
final Function(String value)? onTextChanged;
final Function(VoidCallback fn)? onFormControlReset;
final VoidCallback? onTouched;
final VoidCallback? onDirty;
[@override](/user/override)
Widget build(BuildContext context) {
return HookBuilder(builder: (context) {
final textCtrl = useTextEditingController();
final focusNode = useFocusNode();
useEffect(() {
onFormControlReset?.call(textCtrl.clear);
focusNode.addListener(() {
if (focusNode.hasFocus) {
onTouched?.call();
}
});
textCtrl.addListener(() {
if (textCtrl.text != '') {
onDirty?.call();
}
onTextChanged?.call(textCtrl.text);
});
return null;
}, []);
return TextField(
focusNode: focusNode,
controller: textCtrl,
decoration: decoration,
);
});
}
}
extension IndexedIterable<E> on Iterable<E> {
Iterable<T> mapIndexed<T>(T Function(E e, int i) f) {
var i = 0;
return map((e) => f(e, i++));
}
}
更多关于Flutter表单构建插件ezy_form的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter表单构建插件ezy_form的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何使用Flutter中的ezy_form
插件来构建表单的代码示例。ezy_form
是一个用于简化表单构建的Flutter插件,通过它你可以快速创建复杂的表单布局,同时管理表单验证和状态。
首先,确保你已经在pubspec.yaml
文件中添加了ezy_form
依赖:
dependencies:
flutter:
sdk: flutter
ezy_form: ^最新版本号 # 请替换为实际的最新版本号
然后,运行flutter pub get
来安装依赖。
以下是一个完整的示例,展示了如何使用ezy_form
来创建一个简单的用户注册表单:
import 'package:flutter/material.dart';
import 'package:ezy_form/ezy_form.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Ezy Form Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyFormPage(),
);
}
}
class MyFormPage extends StatefulWidget {
@override
_MyFormPageState createState() => _MyFormPageState();
}
class _MyFormPageState extends State<MyFormPage> {
final _formKey = GlobalKey<EzyFormState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Ezy Form Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: EzyForm(
key: _formKey,
child: Column(
children: [
EzyTextFormField(
name: 'username',
labelText: 'Username',
validators: [
EzyRequiredValidator('Username is required'),
EzyMinLengthValidator(3, 'Username must be at least 3 characters long'),
],
),
EzyTextFormField(
name: 'email',
labelText: 'Email',
keyboardType: TextInputType.emailAddress,
validators: [
EzyRequiredValidator('Email is required'),
EzyEmailValidator('Email is not valid'),
],
),
EzyPasswordFormField(
name: 'password',
labelText: 'Password',
validators: [
EzyRequiredValidator('Password is required'),
EzyMinLengthValidator(6, 'Password must be at least 6 characters long'),
],
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
final formState = _formKey.currentState;
if (formState != null) {
final isValid = await formState.validate();
if (isValid) {
final values = formState.getValues();
print('Form Data: $values');
// 在这里处理表单提交,例如发送到服务器
}
}
},
child: Text('Submit'),
),
],
),
),
),
);
}
}
代码解释:
- 依赖导入:首先导入
flutter
和ezy_form
包。 - 主应用:
MyApp
类定义了应用的主入口,设置了主题并指定了主页为MyFormPage
。 - 表单页面:
MyFormPage
是一个有状态的Widget,包含了一个全局的EzyForm
键_formKey
。 - 表单构建:
- 使用
EzyTextFormField
创建用户名和电子邮件字段,每个字段都有标签文本和验证器。 - 使用
EzyPasswordFormField
创建密码字段,也包含验证器。 - 使用
ElevatedButton
创建一个提交按钮,点击按钮时会触发表单验证。
- 使用
- 表单验证和提交:在按钮的
onPressed
回调中,调用formState.validate()
来验证表单,如果验证通过,则通过formState.getValues()
获取表单数据。
这个示例展示了如何使用ezy_form
插件来快速构建和验证一个简单的注册表单。根据你的实际需求,你可以进一步扩展和自定义表单字段和验证规则。