Flutter表单构建插件ezy_form的使用

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

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) {
        // 添加表单字段
    }
)

渲染每个字段

每个表单字段可以通过 EzyFormControlEzyFormArrayControl 来渲染。

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

1 回复

更多关于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'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

代码解释:

  1. 依赖导入:首先导入flutterezy_form包。
  2. 主应用MyApp类定义了应用的主入口,设置了主题并指定了主页为MyFormPage
  3. 表单页面MyFormPage是一个有状态的Widget,包含了一个全局的EzyForm_formKey
  4. 表单构建
    • 使用EzyTextFormField创建用户名和电子邮件字段,每个字段都有标签文本和验证器。
    • 使用EzyPasswordFormField创建密码字段,也包含验证器。
    • 使用ElevatedButton创建一个提交按钮,点击按钮时会触发表单验证。
  5. 表单验证和提交:在按钮的onPressed回调中,调用formState.validate()来验证表单,如果验证通过,则通过formState.getValues()获取表单数据。

这个示例展示了如何使用ezy_form插件来快速构建和验证一个简单的注册表单。根据你的实际需求,你可以进一步扩展和自定义表单字段和验证规则。

回到顶部