Skip to content

Repository files navigation

LogoFrame

Pub VersionLicenseIssuesCodecovStars

Getting Started with FForm 🌟

Step 1: Installation

First things first, let's get the FForm package into your Flutter project. Add FForm to your pubspec.yaml file under dependencies:

dependencies:
fform: ^latest_version

Don't forget to run flutter pub get in your terminal to install the package.

Overview

FForm is a high-level Flutter package designed to make form creation and management a breeze, with simplified field validation. It offers two main components: FFormField and FFormBuilder, that together bring ease and flexibility to your form handling in Flutter apps.

🧱 Core (Logic and Form Model)

  • FForm — the base class for forms, managing fields, validation, and state.
  • FFormField<T, E> — a generic form field supporting values, errors, and reactions to changes.
  • FFormException — the base class for exceptions that define validation errors.
  • FFormObserver — a static observer that monitors events across all forms (e.g., for debugging, logging, side-effects).

🧩 Widget (UI Binding Widgets)

  • FFormBuilder — binds the form to the UI, updating the interface on changes.
  • FFormProvider — provides access to the form through BuildContext.

🎯 Mixin (Additional Behavior for Fields)

  • KeyedField — adds a unique key for identifying the field in the tree.
  • AsyncField — supports asynchronous validation of the field.
  • CachedField — stores the previous value for reuse.
  • FocusedField — tracks focus, allowing reactions to focus gain/loss.

Structure

Why It Rocks 🎸

  • State Management Simplified: Automatically handles the state of both individual form fields and the form as a whole.
  • Built-in Validation with a Twist: Supports on-the-fly validation and error handling for each field, ensuring a smooth user experience.
  • Flexibility at Its Finest: Supports any data type for field values and validation errors thanks to generics.
  • Reactive Forms for the Win: Leverages streams for tracking form state changes, ensuring your UI is always in sync.
  • Multiple Forms, No Problem: Create multiple forms with custom fields and validation rules, all managed seamlessly by FForm.
  • Custom Exceptions for Custom Needs: Define custom exceptions for form fields to handle complex validation rules and error messages with ease.
  • AsyncValidator: Supports asynchronous validation for form fields, allowing you to validate data against external sources or APIs.
  • CachedField: Provides cached value for field, used to manage the state of the widget and access it in the widget tree.
  • FFormObserver: Allows you to observe the form state and trigger side effects based on the form's state changes.

Previews

Usage Examples

FFormField

FFormField is a base class for all form fields, supporting values, on-the-fly validation, and change handling. It provides a set of getters and methods to manage the field state, including checking the field's validity, retrieving the current value, and handling exceptions.

Example

enumEmailError {
empty,
not;
@overrideStringtoString() {
switch (this) {
case empty:return'emailEmpty';
case not:return'invalidFormatEmail';
default:return'invalidFormatEmail';
}
}
}
classEmailFieldextendsFFormField<String, EmailError> {
EmailField({requiredString value}) :super(value);
@overrideEmailError?validator(value) {
if (value.isEmpty) returnEmailError.empty;
returnnull;
}
}

FForm

FForm is a base class for creating custom form classes with specific fields and validation rules. It provides a set of getters and methods to manage the form state, including checking the form's validity, retrieving answers, and handling exceptions.

Example

This is a simple example of how to create a form with a single field. You can extend the FForm class to create custom forms with specific fields and validation rules.

classLoginFormextendsFForm {
EmailField email;
LoginForm({
requiredthis.email,
}):super(fields: [email]);
}

This is a more complex example of how to create a form with multiple fields. You can extend the FForm class to create custom forms with specific fields and validation rules.

classFormextendsFForm {
List<Form> forms;
Form({
requiredthis.forms,
}):super(subForms: forms);
}

FFormBuilder

FFormBuilder is a widget that constructs and manages the form state, utilizing streams to refresh the UI dynamically as data changes. It provides a builder function that takes the form and returns a widget tree based on the form's state.

Example

This is an example of how to use FFormBuilder to create a form with a single field. The builder function takes the form as a parameter and returns a widget tree based on the form's state.

void_submit() {
if(_form.check()) { // .isValid or .isInvalid start rebuild in FFormBuilder and returned booleanprint('Form Valid');
};
}
@overrideWidgetbuild(BuildContext context) {
returnFFormBuilder<LoginForm>(
form: _form,
builder: (context, form, child) {
EmailField email = form.email; // or FFormProvider.of<LoginForm>(context).get<NameField>()returnColumn(
children: [
TextField(
key: email.key,
controller: _emailController,
decoration:InputDecoration(
labelText:'Email',
errorText: email.exception.toString(),
),
),
ElevatedButton(
onPressed: _submit,
child:constText('Submit'),
),
],
);
},
);
}

You can use ListenableBuilder to rebuild only the field that has changed, but you can use FFormProvider to rebuild all fields in the form.

void_submit() {
if(_form.check()) { // .isValid or .isInvalid start rebuild in FFormBuilder and returned booleanprint('Form Valid');
};
}
@overrideWidgetbuild(BuildContext context) {
returnListenableBuilder<LoginForm>(
listenable: _form,
builder: (context, form, child) {
EmailField email = form.email; // or FFormProvider.of<LoginForm>(context).get<NameField>()returnColumn(
children: [
TextField(
key: email.key,
controller: _emailController,
decoration:InputDecoration(
labelText:'Email',
errorText: email.exception.toString(),
),
),
ElevatedButton(
onPressed: _submit,
child:constText('Submit'),
),
],
);
},
);
}

FFormProvider

FFormProvider is a widget that allows you to access the form in the widget tree without passing it as a parameter.

Example

FFormBuilder<LoginForm>(
form: _form,
builder: (context, form) {
FFormProvider.of<LoginForm>(context).email; // or form.email;FFormProvider.of<LoginForm>(context).get<NameField>(); // or form.get<NameField>();returnYourForm();
},
)

FFormException

FFormException is a base class for creating custom exceptions for form fields. It allows you to define custom validation rules and error messages for form fields, enabling you to handle complex validation scenarios with ease.

Example

You can create a custom exception class that extends FFormException to define specific validation rules and error messages for a form field.

classPasswordValidationExceptionextendsFFormException {
finalbool isMinLengthValid;
finalbool isSpecialCharValid;
finalbool isNumberValid;
PasswordValidationException({
requiredthis.isMinLengthValid,
requiredthis.isSpecialCharValid,
requiredthis.isNumberValid,
});
@overrideboolget isValid => isMinLengthValid && isSpecialCharValid && isNumberValid;
}
classPasswordFieldextendsFFormField<String, PasswordValidationException> {
PasswordField(String value) :super(value);
@overridePasswordValidationException?validator(String value) {
final validator =FFormValidator(value);
returnPasswordValidationException(
isMinLengthValid: validator.isMinLength(8),
isSpecialCharValid: validator.isHaveSpecialChar,
isNumberValid: validator.isHaveNumber,
);
}
}

FFormObserver

FFormObserver is a widget that allows you to observe the form state and trigger side effects based on the form's state changes. It provides a builder function that takes the form as a parameter and returns a widget tree based on the form's state.

Example

classMyFFormObserverextendsFFormObserver {
@overridevoidcheck(FForm form) {
if (kDebugMode) {
print('Form has been checked and is ${form.isValid ? 'valid' : 'invalid'}');
}
}
}

FFormField mixins

And you can add KeyedField mixin to get a unique key for identifying the form field widget.

classEmailFieldextendsFFormField<String, EmailError> withKeyedField {
EmailField({requiredString value}) :super(value);
@overrideEmailError?validator(value) {
if (value.isEmpty) returnEmailError.empty;
returnnull;
}
}
// and get GlobalKey -> form.email.key 

And you can use AsyncValidator

classEmailFieldextendsFFormField<String, EmailError> withAsyncField<String, EmailError> {
EmailField({requiredString value}) :super(value);
@overrideEmailError?validator(value) {
if (value.isEmpty) returnEmailError.empty;
returnnull;
}
@overrideFuture<EmailError?> asyncValidator(value) async {
awaitFuture.delayed(Duration(seconds:1));
if (!value.contains('@')) returnEmailError.not;
returnnull;
}
}
// final field = EmailField();// if(await field.check()) {// // }

Cached value for field

classEmailFieldextendsFFormField<String, EmailError> withCachedField<String, EmailError> {
EmailField({requiredString value}) :super(value);
@overrideEmailError?validator(value) {
if (value.isEmpty) returnEmailError.empty;
returnnull;
}
}

Focused Field

classEmailFieldextendsFFormField<String, EmailError> withFocusField<String, EmailError> {
EmailField({requiredString value}) :super(value);
@overrideEmailError?validator(value) {
if (value.isEmpty) returnEmailError.empty;
returnnull;
}
}
// final field = EmailField();// if(field.check()) {// ....// } else {// field.focus.requestFocus();// }

FFormStatus

FFormStatus is an enum that represents the various states of a form (FForm) during its lifecycle. It helps track the form's status, such as whether it's idle, processing, successfully validated, or has encountered errors.

Enum Values

  • initial: The default state of the form before any action is taken.
  • loading: Indicates that the form is currently processing, such as during validation or submission.
  • success: Indicates that the form has successfully completed its operation with no validation errors.
  • exception: Indicates that the form has encountered errors, such as validation failures.

Example

switch(_form.status) {
FFormStatus.initial =>print('initial'),
FFormStatus.loading =>print('loading'),
FFormStatus.success =>print('success'),
FFormStatus.exception =>print('exception'),
};

Examples

Codecov

Codecov

About

FForm is a high-level Flutter package designed to make form creation and management a breeze, with simplified field validation. It offers two main components: FFormField and FFormBuilder, that together bring ease and flexibility to your form handling in Flutter apps.

Topics

Resources

Contributing

Stars

11 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages