Flutter表单管理插件flutter_reactive_form的使用

Flutter表单管理插件flutter_reactive_form的使用

最低要求

  • Dart SDK: >=2.18.6 <4.0.0
  • Flutter: >=1.17.0

安装和使用

一旦你熟悉了Flutter,可以将flutter_reactive_form添加到pubspec.yaml文件的依赖项列表中:

dependencies:
  flutter:
    sdk: flutter

  flutter_reactive_form: ^0.0.1

然后在控制台运行命令flutter packages get

创建一个表单

一个表单由多个字段控件组成。要声明一个包含namebirthday字段的表单,可以按如下方式编写:

final form = ReactiveForm(
    onChanged: (formData, fieldChanged) {
        /// 处理更改。
    },
    formGroup: {
        "name": FormFieldControl<String>(
          fieldEnum: "name",
          name: "Name",
          isRequired: true,
          isEnabled: true,
          data: "Nghi",
        ),
        "birthday": FormFieldControl<DateTime?>(
          fieldEnum: "birthday",
          name: "Birthday",
          isRequired: true,
          isEnabled: true,
          data: null,
        ),
    },
);

如何获取和设置表单数据

你可以获取字段的值:

String get name => _reactiveForm.getFieldData<String>(fieldEnum: 'name');
DateTime? get birthday => _reactiveForm.getFieldData<DateTime>(fieldEnum: 'birthday');

要设置字段的值:

_reactiveForm.setFieldData(fieldEnum: 'name', data: "Kate");

验证

设置验证规则如下:

_reactiveForm.setFormatValidationMap({
    'birthday': (birthday) {
      if (birthday == null || birthday.isAfter(DateTime.now())) {
         return "The time is invalid";
       }
       return '';
    },  
});

验证一个字段:

final fieldControl = _reactiveForm.getField(fieldEnum: 'name');
final error = _reactiveForm.validateFormatField(fieldControl!);

验证整个表单:

final errors = _reactiveForm.validateFormatFields();

使用mixin

ReactiveFormMixin中提供了与表单内部交互的可用函数:

class ProfileFormController with ReactiveFormMixin {
  /// 在state.initState()中调用
  void init() {
    final form = ReactiveForm(
      onChanged: (formData, fieldChanged) {
        /// 处理更改。
      },
      formGroup: {
        "name": FormFieldControl<String>(
          fieldEnum: "name",
          name: "Name",
          isRequired: true,
          isEnabled: true,
          data: "Nghi",
        ),
        "birthday": FormFieldControl<DateTime?>(
          fieldEnum: "birthday",
          name: "Birthday",
          isRequired: true,
          isEnabled: true,
          data: null,
        ),
      },
    );
    initForm(form);
    setFormatValidationMap({
      'birthday': (birthday) {
        if (birthday == null || birthday.isAfter(DateTime.now())) {
          return "The time is invalid";
        }
        return '';
      },
    });
  }

  /// 与UI交互
  String? getName() {
    return getFieldData<String>(fieldEnum: 'name');
  }

  DateTime? getBirthday() {
    return getFieldData<DateTime>(fieldEnum: 'birthday')!;
  }

  void setName(String name) {
    setFieldData(fieldEnum: 'name', data: name);
  }

  void setBirthday(DateTime birthday) {
    setFieldData(fieldEnum: 'birthday', data: birthday);
  }
}

完整示例代码

以下是一个完整的示例代码,展示了如何使用flutter_reactive_form创建和管理表单:

// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:flutter/material.dart';
import 'package:flutter_reactive_form/flutter_reactive_form.dart';
import 'package:mvc_pattern/mvc_pattern.dart';

enum ProfileFieldEnum {
  fullName("Full Name"),
  email("Email"),
  gender("Gender");

  final String label;
  const ProfileFieldEnum(this.label);
}

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final profileController = ProfileController();
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ProfileScreen(controller: profileController),
    );
  }
}

class ProfileScreen extends StatefulWidget {
  const ProfileScreen({
    Key? key,
    required this.controller,
  }) : super(key: key);
  final ProfileController controller;

  [@override](/user/override)
  _ProfileScreenState createState() => _ProfileScreenState(controller);
}

class _ProfileScreenState extends StateMVC<ProfileScreen> {
  _ProfileScreenState(ProfileController controller) : super(controller) {
    _controller = controller;
  }
  late ProfileController _controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    _controller.onInit();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Profile')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _controller.formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              TextFormField(
                controller: _controller.fullNameController,
                decoration: InputDecoration(labelText: ProfileFieldEnum.fullName.label),
                onChanged: _controller.onFullNameChanged,
                validator: _controller.validateFullName,
              ),
              TextFormField(
                controller: _controller.emailController,
                decoration: InputDecoration(labelText: ProfileFieldEnum.email.label),
                onChanged: _controller.onEmailNameChanged,
                validator: _controller.validateEmail,
              ),
              DropdownButtonFormField<String>(
                decoration: InputDecoration(labelText: ProfileFieldEnum.gender.label),
                value: _controller.getFieldData(fieldEnum: ProfileFieldEnum.gender.name),
                items: ['Male', 'Female'].map((String value) {
                  return DropdownMenuItem<String>(
                    value: value,
                    child: Text(value),
                  );
                }).toList(),
                onChanged: _controller.onGenderChanged,
                validator: _controller.validateGender,
              ),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 16.0),
                child: ElevatedButton(
                  onPressed: _controller.create,
                  child: Text('Create'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  [@override](/user/override)
  void dispose() {
    _controller.onDispose();
    super.dispose();
  }
}

class ProfileController extends ControllerMVC with ReactiveFormMixin {
  final formKey = GlobalKey<FormState>();
  late final TextEditingController fullNameController;
  late final TextEditingController emailController;

  void onInit() {
    /// 初始化文本字段的文本。
    String initialFullName = '';
    String initialLastName = '';
    String initialGender = 'Male';

    fullNameController = TextEditingController(text: initialFullName);
    emailController = TextEditingController(text: initialLastName);

    final form = ReactiveForm(
      onChanged: (formData, fieldChanged) {
        /// 处理更改。
      },
      formGroup: {
        ProfileFieldEnum.fullName.name: FormFieldControl<String?>(
          fieldEnum: ProfileFieldEnum.fullName.name,
          name: ProfileFieldEnum.fullName.label,
          isRequired: true,
          isEnabled: true,
          data: initialFullName, // 初始值。
        ),
        ProfileFieldEnum.email.name: FormFieldControl<String?>(
          fieldEnum: ProfileFieldEnum.email.name,
          name: ProfileFieldEnum.email.label,
          isRequired: true,
          isEnabled: true,
          data: initialLastName, // 初始值。
        ),
        ProfileFieldEnum.gender.name: FormFieldControl<String?>(
          fieldEnum: ProfileFieldEnum.gender.name,
          name: ProfileFieldEnum.gender.label,
          isRequired: true,
          isEnabled: true,
          data: initialGender, // 初始值。
        ),
      },
    );

    form.setFormatValidationMap({
      ProfileFieldEnum.fullName.name: (value) {
        assert(value is String?, " The value must be string.");
        final fullname = value as String?;
        if (fullname == null || fullname.isEmpty) {
          return "Please enter your full name";
        }
        return null;
      },
      ProfileFieldEnum.email.name: (value) {
        assert(value is String?, " The value must be string.");
        final email = value as String?;
        if (email == null || email.isEmpty) {
          return "Please enter an email";
        } else if (!(value?.contains('@') ?? false)) {
          return 'Email must contain the "@" character';
        }
        return null;
      },
      ProfileFieldEnum.gender.name: (value) {
        assert(value is String?, " The value must be string.");
        final gender = value as String?;
        if (gender == null || gender.isEmpty) {
          return "Please choose Gender";
        }
        return null;
      },
    });

    initForm(form);
  }

  void onFullNameChanged(String? value) {
    setFieldData(fieldEnum: ProfileFieldEnum.fullName.name, data: value);
  }

  void onEmailNameChanged(String? value) {
    setFieldData(fieldEnum: ProfileFieldEnum.email.name, data: value);
  }

  void onGenderChanged(String? value) {
    setFieldData(fieldEnum: ProfileFieldEnum.gender.name, data: value);
    refresh();
  }

  String? validateFullName(String? value) {
    final fullNameControl = getField(fieldEnum: ProfileFieldEnum.fullName.name)!;
    return validateFormatField(fullNameControl, value: value);
  }

  String? validateEmail(String? value) {
    final emailControl = getField(fieldEnum: ProfileFieldEnum.email.name)!;
    return validateFormatField(emailControl, value: value);
  }

  String? validateGender(String? value) {
    final genderControl = getField(fieldEnum: ProfileFieldEnum.gender.name)!;
    return validateFormatField(genderControl, value: value);
  }

  void create() {
    if (formKey.currentState!.validate()) {
      // 验证成功后的逻辑
      final fullName = getFieldData<String>(fieldEnum: ProfileFieldEnum.fullName.name);
      final email = getFieldData<String>(fieldEnum: ProfileFieldEnum.email.name);
      final gender = getFieldData<String>(fieldEnum: ProfileFieldEnum.gender.name);
      print("fullName: $fullName, email: $email, gender: $gender");
      // 执行Profile操作(例如API调用)
    }
  }

  void onDispose() {
    fullNameController.dispose();
    emailController.dispose();
  }
}

更多关于Flutter表单管理插件flutter_reactive_form的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter表单管理插件flutter_reactive_form的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_reactive_form 是一个用于管理 Flutter 表单的插件,它基于响应式编程思想,可以帮助开发者更轻松地管理和验证表单数据。以下是如何使用 flutter_reactive_form 的基本步骤和示例。

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 flutter_reactive_form 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_reactive_form: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 创建表单模型

flutter_reactive_form 使用 FormGroup 来管理表单的字段。你可以通过继承 FormGroup 来创建自定义的表单模型。

import 'package:flutter_reactive_form/flutter_reactive_form.dart';

class MyForm extends FormGroup {
  MyForm() {
    addControl('username', FormControl<String>(value: '', validators: [Validators.required]));
    addControl('email', FormControl<String>(value: '', validators: [Validators.required, Validators.email]));
    addControl('password', FormControl<String>(value: '', validators: [Validators.required, Validators.minLength(6)]));
  }
}

3. 在 UI 中使用表单

在 UI 中,你可以使用 ReactiveFormReactiveTextField 来绑定表单字段。

import 'package:flutter/material.dart';
import 'package:flutter_reactive_form/flutter_reactive_form.dart';

class MyFormPage extends StatelessWidget {
  final MyForm form = MyForm();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Reactive Form Example')),
      body: ReactiveForm(
        formGroup: form,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              ReactiveTextField<String>(
                formControlName: 'username',
                decoration: InputDecoration(labelText: 'Username'),
                validationMessages: {
                  'required': (error) => 'Username is required',
                },
              ),
              SizedBox(height: 16),
              ReactiveTextField<String>(
                formControlName: 'email',
                decoration: InputDecoration(labelText: 'Email'),
                validationMessages: {
                  'required': (error) => 'Email is required',
                  'email': (error) => 'Please enter a valid email',
                },
              ),
              SizedBox(height: 16),
              ReactiveTextField<String>(
                formControlName: 'password',
                decoration: InputDecoration(labelText: 'Password'),
                obscureText: true,
                validationMessages: {
                  'required': (error) => 'Password is required',
                  'minLength': (error) => 'Password must be at least 6 characters',
                },
              ),
              SizedBox(height: 24),
              ElevatedButton(
                onPressed: () {
                  if (form.valid) {
                    // Form is valid, process the data
                    print('Form is valid');
                    print('Username: ${form.control('username').value}');
                    print('Email: ${form.control('email').value}');
                    print('Password: ${form.control('password').value}');
                  } else {
                    // Form is invalid, show errors
                    print('Form is invalid');
                  }
                },
                child: Text('Submit'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

4. 处理表单提交

onPressed 回调中,你可以检查表单的有效性,并处理表单数据。如果表单无效,你可以显示错误信息或执行其他操作。

5. 自定义验证器

flutter_reactive_form 支持自定义验证器。你可以通过 ValidatorFn 来创建自定义的验证逻辑。

ValidatorFn customValidator = (control) {
  if (control.value != 'expectedValue') {
    return {'customError': true};
  }
  return null;
};

// 使用自定义验证器
addControl('customField', FormControl<String>(value: '', validators: [customValidator]));

6. 动态表单字段

你可以在运行时动态添加或移除表单字段。

form.addControl('newField', FormControl<String>(value: ''));
form.removeControl('newField');

7. 表单状态监听

你可以监听表单的状态变化,例如表单的有效性、字段的值变化等。

form.statusChanges.listen((status) {
  print('Form status: $status');
});

form.control('username').valueChanges.listen((value) {
  print('Username changed: $value');
});

8. 表单重置

你可以通过 reset 方法重置表单字段的值和状态。

form.reset();
回到顶部