Flutter表单构建插件easy_form_kit的使用
Flutter表单构建插件easy_form_kit的使用
EasyForm
是一组用于处理表单字段数据的控件。它类似于 Flutter 的 Form
和 TextFormField
控件,并且与之紧密兼容。
EasyForm
和 EasyTextFormField
控件将表单字段放在一个容器内,每个字段都有一个名称(通过 name
参数指定)。当保存表单时,表单字段的数据会被传递给 onSave
回调作为 Map<String, dynamic>
,其中键是字段名,值是表单字段的值。
表单可以通过 EasyFormSaveButton
或者通过调用 EasyForm.of(context).save()
进行保存。
要插入文本字段,可以使用 EasyTextFormField
控件,对于其他类型的字段,可以直接在 widget 树中使用 EasyCustomFormField
控件或创建其继承类。
示例代码
import 'package:flutter/material.dart';
import 'package:easy_form_kit/easy_form_kit.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'EasyForm Example',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: ExampleScreen(),
);
}
}
class ExampleScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
child: EasyForm(
onSave: (values, form) async {
return Future.delayed(const Duration(seconds: 3), () {
return <String, dynamic>{
'hasError': false,
};
});
},
onSaved: (response, values, form) {
if (response['hasError']) {
_alert(context, response['error']);
} else {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LoggedScreen(),
),
);
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
EasyTextFormField(
name: 'username',
decoration: const InputDecoration(
hintText: 'Enter your username',
),
validator: (value, [values]) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
const SizedBox(height: 16.0),
EasyTextFormField(
name: 'password',
decoration: const InputDecoration(
hintText: 'Enter your password',
),
obscureText: true,
validator: (value, [values]) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: EasyFormSaveButton.text('Sign In'),
),
],
),
),
),
),
),
);
}
Future<void> _alert(BuildContext context, String text) async {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Error'),
content: Text(text),
),
);
}
}
class LoggedScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Welcome, John.'),
const SizedBox(height: 24),
TextButton(
child: Text('Back'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
),
),
);
}
}
数据处理
EasyForm
在表单保存期间,所有字段的值会被收集到一个 Map<String, dynamic>
中并传递给 onSave
回调。
onSave
回调是异步的,你可以在其中保存或发送值,等待响应后再传递给 onSaved
。如果 onSave
返回空值,则会将表单字段的值映射传递给 onSaved
。
EasyDataForm
与 EasyForm
不同的是,在 EasyDataForm<T>
中,onSave
回调返回的结果必须与 EasyDataForm<T>
实例化时指定的类型 T
匹配。
EasyDataForm<T>
处理 onSave
回调结果的方式也有所不同:
EasyForm
将onSave
回调的结果传递给onSaved
回调。如果onSave
结果为null
(即onSave
返回null
或未指定),则传递表单字段的值映射。EasyDataForm
始终仅将onSave
回调的结果传递给onSaved
回调,或者传递null
如果onSave
未指定。
错误处理
如果需要将外部来源(例如通过 API)的错误传递给字段,可以使用 EasyForm
构造函数的 errors
参数或 EasyFormState
的 setErrors
方法。
使用构造函数设置错误
class SampleFormPage extends StatefulWidget {
@override
_SampleFormPageState createState() => _SampleFormPageState();
}
class _SampleFormPageState extends State<SampleFormPage> {
Map<String, String> errors;
@override
Widget build(BuildContext context) {
return EasyForm(
errors: errors, // 这里设置字段中的错误。
onSave: (values, form) async {
return API.login(values['username'], values['password']);
},
onSaved: (response, values, form) {
if (response.hasError) {
setState(() {
errors = response.fieldsErrors;
});
} else {
// ... 导航到另一个屏幕
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
EasyTextFormField(
name: 'username',
decoration: const InputDecoration(
hintText: 'Enter your username',
),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
const SizedBox(height: 16.0),
EasyTextFormField(
name: 'password',
decoration: const InputDecoration(
hintText: 'Enter your password',
),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: EasyFormSaveButton.text('Sign In'),
),
],
),
);
}
}
使用 setErrors
方法
class SampleFormPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return EasyForm(
onSave: (values, form) async {
return API.login(values['username'], values['password']);
},
onSaved: (response, values, form) {
if (response.hasError) {
form.setErrors(response.fieldsErrors); // 这里设置字段中的错误。
} else {
// ... 导航到另一个屏幕
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
EasyTextFormField(
name: 'username',
decoration: const InputDecoration(
hintText: 'Enter your username',
),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
const SizedBox(height: 16.0),
EasyTextFormField(
name: 'password',
decoration: const InputDecoration(
hintText: 'Enter your password',
),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: EasyFormSaveButton.text('Sign In'),
),
],
),
);
}
}
自定义错误显示
对于非标准错误显示,或者希望在布局的其他位置显示错误,可以使用 EasyFormFieldError
小部件。
例如,以下示例中,错误消息显示在一个自定义字段的布局下方:
class CustomErrorDisplayPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return EasyForm(
onSave: (values, form) async {
return {
'file': 'Invalid file.',
};
},
onSaved: (response, values, form) {
if (response != null) {
form.setErrors(response);
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: [
// 自定义表单字段。
DocumentFormField(
name: 'file',
),
const SizedBox(width: 16),
Text('Document to upload.'),
],
),
// 错误消息将显示在这里。
EasyFormFieldError(
name: 'file',
textStyle: TextStyle(color: Colors.red),
),
const Divider(
height: 24,
),
EasyTextFormField(
name: 'username',
decoration: const InputDecoration(
hintText: 'Enter your username',
),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
const SizedBox(height: 16.0),
EasyTextFormField(
name: 'password',
decoration: const InputDecoration(
hintText: 'Enter your password',
),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: EasyFormSaveButton.text('Sign In'),
),
],
),
);
}
}
自定义字段
对于文本字段,可以使用 EasyTextFormField
小部件,该小部件封装了 TextField
小部件及其所有属性。
EasyForm
可以轻松创建自定义类型的表单字段。
作为控制器,可以使用泛型 EasyFormFieldController
或创建其继承类:
class ColorController extends EasyFormFieldController<Color> {
ColorController(super.value);
}
要创建字段,可以创建 EasyFormGenericField
的继承类并实现其 build
方法:
class ColorField extends EasyFormGenericField<Color> {
const ColorField({
super.key,
required ColorController super.controller,
super.onChange,
});
void _change() {
value = _getRandomColor();
}
Color _getRandomColor() {
return Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
}
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: GestureDetector(
onTap: _change,
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: value,
border: Border.all(
color: Colors.grey,
width: 2,
),
shape: BoxShape.circle,
),
),
),
);
}
}
这个字段和控制器可以独立于 EasyForm
使用。
要在 EasyForm
中使用它们,可以通过 EasyCustomFormField
的泛型小部件直接使用:
EasyCustomFormField<Color, ColorController>(
name: 'color',
initialValue: Colors.teal,
controllerBuilder: (value) => ColorController(value),
builder: (fieldState, onChangedHandler) => ColorField(
controller: fieldState.controller as ColorController,
onChange: onChangedHandler,
),
),
或者创建 EasyCustomFormField
小部件的继承类:
class ColorFormField extends EasyCustomFormField<Color, ColorController> {
ColorFormField({
super.key,
required super.name,
super.controller,
Color? initialValue,
}) : super(
initialValue: initialValue ?? const Color(0x00000000),
controllerBuilder: (value) => ColorController(value),
builder: (state, onChangedHandler) {
return ColorField(
controller: state.controller as ColorController,
onChange: onChangedHandler,
);
},
);
}
然后在表单中使用它:
ColorFormField(
name: 'color',
initialValue: Colors.teal,
),
状态自定义字段
EasyFormGenericField
小部件是一个状态小部件,如果你需要存储中间状态(例如选择照片),可以将上述字段代码转换为状态小部件:
class ColorField extends EasyFormGenericField<Color> {
const ColorField({
super.key,
required ColorController super.controller,
super.onChange,
});
@override
ColorFieldState createState() => ColorFieldState();
}
class ColorFieldState extends EasyFormGenericFieldState<Color> {
void _change() {
value = _getRandomColor();
}
Color _getRandomColor() {
return Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
}
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: GestureDetector(
onTap: _change,
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: value,
border: Border.all(
color: Colors.grey,
width: 2,
),
shape: BoxShape.circle,
),
),
),
);
}
}
自定义
表单保存按钮、重置按钮、保存指示器和错误消息的外观和布局可以进行定制。
你可以使用 EasyFormDefaultSettings
设置默认外观应用于所有底层小部件。
通常这些设置是在 MaterialApp.builder
中设置的:
return MaterialApp(
...
builder: (context, child) => EasyFormDefaultSettings(
saveButton: EasyFormSaveButtonSettings(
builder: (context, key, child, onPressed, adaptivity) =>
ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.amber.shade700,
),
onPressed: onPressed,
child: child ?? const SizedBox.shrink(),
),
),
),
...
);
这样可以为以下内容设置默认设置:
EasyFormSaveButton
- builder
- layoutBuilder
- indicatorBuilder
EasyFormResetButton
- builder
EasyFormActionButton
- builder
EasyFormSaveIndicator
- builder
- layoutBuilder
EasyFormFieldError
- builder
表单按钮
为了方便起见,EasyForm
提供了三种类型的按钮:
EasyFormSaveButton
- 用于保存表单;EasyFormResetButton
- 用于将表单恢复到初始状态;EasyFormButton
- 用于任何表单操作;
EasyFormSaveButton
和 EasyFormResetButton
按钮各有两种构造函数,一种接受小部件作为按钮内容,另一种名为 text
接受文本作为按钮内容:
EasyFormSaveButton(child: Text('Save')),
// 或
EasyFormSaveButton.text('Save'),
按钮定制
默认情况下,EasyFormSaveButton
创建为 ElevatedButton
或 CupertinoButton.filled
,而 EasyFormResetButton
创建为 OutlinedButton
或 CupertinoButton
。
可以通过 EasyForm
构造函数的 adaptivity
参数来定制按钮的外观:
EasyFormAdaptivity.auto
- 根据平台选择按钮类型;EasyFormAdaptivity.material
- Material-design 按钮;EasyFormAdaptivity.cupertion
- Apple 设计按钮。
这些按钮可以通过覆盖创建按钮小部件本身的 builder
来定制。
builder
可以传递给构造函数:
EasyFormSaveButton.text(
'Save',
builder: (context, key, child, onPressed) => ElevatedButton(
key: key,
child: child,
onPressed: onPressed,
),
),
… 或者全局重新定义,适用于整个应用程序(除了那些在构造函数中传递了 builder
的按钮)使用 EasyFormDefaultSettings
。
表单保存过程
在表单保存期间,EasyFormSaveButton
和 EasyFormResetButton
按钮会被禁用,EasyFormSaveButton
按钮的内容(例如文本)会被进度指示器替换。
默认情况下,指示器是一个 18x18 的 CircularProgressIndicator
或 CupertinoActivityIndicator
,颜色与 ElevatedButton
的文本颜色相同。
可以通过在按钮构造函数中覆盖 builder
来定制指示器:
EasyFormSaveButton.text(
'Save',
indicatorBuilder: (context, size) => SizedBox.fromSize(
size: size,
child: CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
),
)
更多关于Flutter表单构建插件easy_form_kit的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter表单构建插件easy_form_kit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用easy_form_kit
插件来构建表单的代码示例。easy_form_kit
是一个用于快速创建和管理表单的Flutter插件,它提供了一系列方便的方法来定义和处理表单字段。
首先,确保你已经在pubspec.yaml
文件中添加了easy_form_kit
的依赖:
dependencies:
flutter:
sdk: flutter
easy_form_kit: ^最新版本号 # 请替换为最新的版本号
然后,运行flutter pub get
来安装依赖。
接下来,是一个简单的示例,展示如何使用easy_form_kit
来创建一个包含文本字段、数字字段和提交按钮的表单:
import 'package:flutter/material.dart';
import 'package:easy_form_kit/easy_form_kit.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Easy Form Kit Demo'),
),
body: EasyFormKitDemo(),
),
);
}
}
class EasyFormKitDemo extends StatefulWidget {
@override
_EasyFormKitDemoState createState() => _EasyFormKitDemoState();
}
class _EasyFormKitDemoState extends State<EasyFormKitDemo> {
final _formKey = GlobalKey<FormState>();
late EasyFormController _formController;
@override
void initState() {
super.initState();
_formController = EasyFormController(
formFields: [
EasyFormTextField(
name: 'name',
label: 'Name',
validators: [
EasyFormValidators.required(),
],
),
EasyFormTextField(
name: 'email',
label: 'Email',
keyboardType: TextInputType.emailAddress,
validators: [
EasyFormValidators.required(),
EasyFormValidators.email(),
],
),
EasyFormNumberField(
name: 'age',
label: 'Age',
validators: [
EasyFormValidators.required(),
EasyFormValidators.numberRange(min: 0, max: 120),
],
),
],
);
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Expanded(
child: EasyForm(
controller: _formController,
),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
final formValues = _formController.getValues();
print('Form Values: $formValues');
// 这里可以添加提交表单的逻辑,比如发送到服务器
}
},
child: Text('Submit'),
),
],
),
),
);
}
@override
void dispose() {
_formController.dispose();
super.dispose();
}
}
在这个示例中,我们做了以下几件事:
- 创建一个
EasyFormController
实例,并定义了表单字段(包括文本字段和数字字段),以及相应的验证规则。 - 使用
Form
和EasyForm
小部件来渲染表单。 - 在提交按钮的
onPressed
回调中,验证表单字段并获取表单值。
这个示例展示了如何使用easy_form_kit
快速创建和管理表单,包括字段验证和值获取。你可以根据需要扩展和自定义这个示例,以满足你的具体需求。