Giter Club home page Giter Club logo

formz's Introduction

๐Ÿ“ Formz

Very Good Ventures Very Good Ventures

Developed with ๐Ÿ’™ by Very Good Ventures ๐Ÿฆ„

ci coverage pub package License: MIT style: very good analysis


A unified form representation in Dart. Formz aims to simplify form representation and validation in a generic way.

Create a FormzInput

import 'package:formz/formz.dart';

// Define input validation errors
enum NameInputError { empty }

// Extend FormzInput and provide the input type and error type.
class NameInput extends FormzInput<String, NameInputError> {
  // Call super.pure to represent an unmodified form input.
  const NameInput.pure() : super.pure('');

  // Call super.dirty to represent a modified form input.
  const NameInput.dirty({String value = ''}) : super.dirty(value);

  // Override validator to handle validating a given input value.
  @override
  NameInputError? validator(String value) {
    return value.isEmpty ? NameInputError.empty : null;
  }
}

Interact with a FormzInput

const name = NameInput.pure();
print(name.value); // ''
print(name.isValid); // false
print(name.error); // NameInputError.empty
print(name.displayError); // null

const joe = NameInput.dirty(value: 'joe');
print(joe.value); // 'joe'
print(joe.isValid); // true
print(joe.error); // null
print(name.displayError); // null

Validate Multiple FormzInput Items

const validInputs = <FormzInput>[
  NameInput.dirty(value: 'jan'),
  NameInput.dirty(value: 'jen'),
  NameInput.dirty(value: 'joe'),
];

print(Formz.validate(validInputs)); // true

const invalidInputs = <FormzInput>[
  NameInput.dirty(),
  NameInput.dirty(),
  NameInput.dirty(),
];

print(Formz.validate(invalidInputs)); // false

Automatic Validation

class LoginForm with FormzMixin {
  LoginForm({
    this.username = const Username.pure(),
    this.password = const Password.pure(),
  });

  final Username username;
  final Password password;

  @override
  List<FormzInput> get inputs => [username, password];
}

void main() {
  print(LoginForm().isValid); // false
}

Caching validation results

For cases where the validator method has an expensive implementation, consider using the FormzInputErrorCacheMixin mixin to cache the error result and improve performance.

import 'package:formz/formz.dart';

enum EmailValidationError { invalid }

class Email extends FormzInput<String, EmailValidationError>
    with FormzInputErrorCacheMixin {
  Email.pure([super.value = '']) : super.pure();

  Email.dirty([super.value = '']) : super.dirty();

  static final _emailRegExp = RegExp(
    r'^[a-zA-Z\d.!#$%&โ€™*+/=?^_`{|}~-]+@[a-zA-Z\d-]+(?:\.[a-zA-Z\d-]+)*$',
  );

  @override
  EmailValidationError? validator(String value) {
    return _emailRegExp.hasMatch(value) ? null : EmailValidationError.invalid;
  }
}

formz's People

Contributors

alestiago avatar dependabot[bot] avatar enyo avatar felangel avatar jorgecoca avatar marci002 avatar marcossevilla avatar renancaraujo avatar toavina23 avatar tomarra avatar zepfietje avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

formz's Issues

example/lib/main.dart

The example formz/example/lib/main.dart, which is also the example on pub.dev/formz, does not seem to behave as expected: If I leave the fields for Email and Password empty, the input in _onSubmit should be rejected, since empty fields do not pass the validator of either Email or Password. However, at program startup or after each _resetForm call, empty text fields are not rejected.

One could solve the problem by changing all default values of the MyFormState constructor from pure to dirty:

MyFormState({
  Email? email,
  this.password = const Password.dirty(),
  this.status = FormzSubmissionStatus.initial,
}) : email = email ?? Email.dirty();

But I am not sure if this is the right approach. Could you please check this?

Null Values are valid on entry but cause build time error an unspecific analyzer error

Right now the .pure constructor requires that value being passed in not be null. But that isn't valid. A Dropdown for instance, or a date etc. all are absolutely valid to be null and in fact in most cases of form entry, the default value SHOULD be null (i.e. NO ENTRY)

Even on a text field, NULL is NO ENTRY. When they type, they may enter '', but that's still an entry.

Please remove the assert on formz.dart line 93 as it isn't valid because as written a date field for instance would require a value initially, which there's lots of cases where this isn't true (ie: Sent On field. Well if it hasn't been sent, then the currect value is NULL (no entry) not some arbitrary date and it shouldn't require it to be filled in by default)

The getter 'isSubmissionFailure' isn't defined for the type 'FormzStatus'.

I was following tutorial https://bloclibrary.dev/#/flutterlogintutorial

but when I reach step https://bloclibrary.dev/#/flutterlogintutorial?id=login-form
I got this error

The getter 'isSubmissionFailure' isn't defined for the type 'FormzStatus'.
Try importing the library that defines 'isSubmissionFailure', correcting the name to the name of an existing getter, or defining a getter or field named 'isSubmissionFailure'

alt error_image

I tried to follow code as close as possible restarted vsocde etc.
Fun fact: when I open example_code it is not showing this error.

Here is the repo with commit for reproducing error above

Example of how to edit existing form values?

First up, thanks for this great package. I use it a lot in my projects and find it really nice to work with.

I'm hoping for some help in how to implement the ability to create a new FormModel populated with pre-existing values. This is so I can use the same form for both 'create' and 'update' scenarios.

Here's my code for the 'create' scenario with empty default values:

class MyForm with FormzMixin {
final NameInput name;
final AgeInput age;

MyForm({
this.name = NameInput.pure(),
this.name = AgeInput.pure(),
})

@override
  List<FormzInput> get inputs => [name, age,];
}

This all works fine, but how can I pass in values to the .pure() constructors if I am in 'update' or 'edit' mode? Any pointers to an existing example or other help would be much appreciated.

Different types of error

FormzStatus has very limited use cases. My case I need to identify different types of error to show different UI. submissionFailure seems not exapandale. Any ideas?

Add `isInProgressOrSuccess` to `FormzSubmissionStatusX` extension

Use case

In many cases this is the flow in cubit / bloc when operating with form inputs:

Future<void> submit() async {
  if (state.status.isInProgress || state.status.isSuccess || !state.isValid) {
    return;
  }

  emit(state.copyWith(status: FormzSubmissionStatus.inProgress));
  try {
    await repository.doSomething();
    emit(state.copyWith(status: FormzSubmissionStatus.success));
  } catch (e) {
    emit(state.copyWith(failure: '$e', status: FormzSubmissionStatus.failure));
  }
}

And this happens on UI:

final isInProgress = context.select<MyCubit, bool>(
  (cubit) => cubit.state.status.isInProgress || cubit.state.status.isSuccess,
);

return ElevatedButton(
  onPressed: isInProgress ? null : () => context.read<MyCubit>().submit(),
  child: const Text('Submit'),
);

Proposal

Instead of cubit.state.status.isInProgress || cubit.state.status.isSuccess it would be much easier to write cubit.state.status.isInProgressOrSuccess.

Including myself I only check FormzSubmissionStatusX.isInProgress which can lead to unexpetcted behaviour since in case of succes emit(state.copyWith(status: FormzSubmissionStatus.success)); the ElevatedButton will be clickable which can happen since flutter animates the screens in and out or worse when CircularProgressIndicator is used in case of isInProgress which will be visible for some frames. (Usually when staus becomes succes then form page is closed and still visible while page fades away)

When developer tries to write isI or progr then IDE's autocomplete will show isInProgressOrSuccess along side isInProgress and they will read the docs or just read the changelog.

Note: this proposal should be aplied to 0.5.0-dev.1 I hope it will make to stable version since #48 refactor is awesome and logical.

docs: provide interactive demonstration on README

Is your feature request related to a problem? Please describe.
Inspired by #95, users should be able to run the example to quickly evaluate the package.

Describe the solution you'd like
I would like users to be able to try out an application using formz with a single click. Ideally, without cloning and having to run the example themselves.

The solution I'l like is to have an image within the README that when clicked opens a hosted page with the running example.

Describe alternatives you've considered
Another option is to wait until pub.dev support hosting the examples for users to interact with.

Additional context

A validator of type Future

Hello,

I'm trying to use Formz to validate a phonenumber using the https://pub.dev/packages/libphonenumber library.

But libphonenumber's validation function returns a Future, so I was unable to use it in the validator() function of Formz

import 'package:formz/formz.dart';
import 'package:libphonenumber/libphonenumber.dart';

enum PhoneNumberValidationError { invalid }

class PhoneNumber extends FormzInput<String, PhoneNumberValidationError> {
  const PhoneNumber.pure() : super.pure('');

  const PhoneNumber.dirty([String value = '']) : super.dirty(value);

  static final _phoneNumberRegex = RegExp(r'^[+]{1}[0-9]{1,4}[-\s\./0-9]*$');

  @override
  PhoneNumberValidationError validator(String value) {
    return PhoneNumberUtil.isValidPhoneNumber(phoneNumber: "", isoCode: 'US').then((value){
      return null;
    });
  }
}

error: A value of type 'Future<Null>' can't be returned from method 'validator' because it has a return type of 'PhoneNumberValidationError'. (return_of_invalid_type at [datoraid] lib/models/phone_number.dart:16)

Validate if the form is pure 0.5.0+1

I would like you to add to the FormMixin a method to calculate if the all fields embedded in the inputs field are pure. A good idea to know if the form is pure is by iterating over the inputs field and checking each one of its status.

bool get isPure => inputs.every((e) => e.isPure);

Thanks for the update to 0.5.0+, it was very important to split the statuses of the form field and the general status of the form submission.

Validation with external information

Hi and thank you for lib
How to validate input with external information like checking username availability , or checking password confirmation?

Question: How Create Password Confirm with formz

I'm create a register page and there need has a confirm password field. How is the better aprouch to do this with formz, because validation depends with external value and I don't now the better pattern to use.

I has two inputs

import 'package:formz/formz.dart';
import 'package:validators/validators.dart';

enum PasswordFieldValidationError { empty, small }

class PasswordField extends FormzInput<String, PasswordFieldValidationError> {
  const PasswordField.pure() : super.pure('');
  const PasswordField.dirty([String value = '']) : super.dirty(value);

  @override
  PasswordFieldValidationError validator(String value) {
    if (value.isEmpty) return PasswordFieldValidationError.empty;
    if (!isLength(value, 6)) return PasswordFieldValidationError.small;
    return null;
  }
}
import 'package:formz/formz.dart';

enum PasswordFieldValidationError { empty, match}

class PasswordField extends FormzInput<String, PasswordFieldValidationError> {
  const PasswordField.pure() : super.pure('');
  const PasswordField.dirty([String value = '']) : super.dirty(value);

  @override
  PasswordFieldValidationError validator(String value) {
    final otherPassword = 'How I get this?' // I don't how do this
    if (value.isEmpty) return PasswordFieldValidationError.empty;
    if (value != otherPassword) return PasswordFieldValidationError.match;
    return null;
  }
}

This package did extrymily userfull to help-me separete the logic and view, so I would like understend the better pattern before work around

really thanks!

Performance-friendly validation

Use case

Currently FormzInput.isValid / FormzInput.isNotValid / FormzInput.error / FormzInput.displayError / FormzInput.validator / Formz .validate methods / getters always recalculate the error which is waste of cpu cycle.

FormzInput is @immutable abstract class and it's validator method should return same result no matter how many times called at any point in time (pure function) since the validation logic can be expensive for instance a long regexp.

Proposal

  • Error should be calculated at construct time
  • Error should be lazy calaculated

Determining when to show meaningful errors

Currently, the error property on pure, invalid instances of FormzInput is non-null, indicating what the validation error was. Perhaps Formz could expose an additional getter for UI purposes to indicate that the validation error, while correct, is typically ignored for pure forms from a UI perspective. Ignoring validation errors on unmodified (pure) fields typically creates a more pleasant UI experience, and a mechanism like this might better facilitate that.

abstract class FormzInput<T, E> {
  E? get displayError = pure ? null : error;
  E? get error => validator(value);
  ...

Other approaches could also solve the same problem, I'm sure.

Cannot find private constructor for FormzInput.pure/FormzInput.dirty in newest Dart?

I updated my to flutter version 2 today and started getting the following error message:

Couldn't find constructor 'FormzInput.'.
const FormzInput.dirty(T value) : this.
(value, false)

By removing the read only file protection of the cached package and re-naming the anonymous constructor, things start working again.

Is this a change in dart that you will have to take into account, or is this a bug?

FormzStatus is not accessible in formz: ^0.5.0+1

Describe the bug
FormzStatus is not accessible or present in formz: ^0.5.0+1. When I revert back to the version 4+, it is working just fine

To Reproduce
Steps to reproduce the behavior:

  1. update pubspec.yaml to the said version of any existing or new project
  2. FormzStatus will have an error in VSCode showing Undefined class 'FormzStatus'.

Expected behavior
To not have this error

Screenshots
FormzStatus with v0.5.0+1
image
FormzStatus with v0.4.0+1
image

Additional context

[โˆš] Flutter (Channel stable, 3.7.5, on Microsoft Windows [Version 10.0.19044.2604], locale en-IN)
    โ€ข Flutter version 3.7.5 on channel stable at C:\Users\hi\src\flutter
    โ€ข Upstream repository https://github.com/flutter/flutter.git
    โ€ข Framework revision c07f788888 (3 weeks ago), 2023-02-22 17:52:33 -0600
    โ€ข Engine revision 0f359063c4
    โ€ข Dart version 2.19.2
    โ€ข DevTools version 2.20.1

[โˆš] Windows Version (Installed version of Windows is version 10 or higher)

[โˆš] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    โ€ข Android SDK at C:\Users\hi\AppData\Local\Android\sdk
    โ€ข Platform android-33, build-tools 33.0.0
    โ€ข Java binary at: C:\src\Android\jre\bin\java
    โ€ข Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    โ€ข All Android licenses accepted.

[โˆš] Chrome - develop for the web
    โ€ข Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[โˆš] Visual Studio - develop for Windows (Visual Studio Community 2022 17.4.4)
    โ€ข Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    โ€ข Visual Studio Community 2022 version 17.4.33213.308
    โ€ข Windows 10 SDK version 10.0.22000.0

[โˆš] Android Studio (version 2021.3)
    โ€ข Android Studio at C:\src\Android
    โ€ข Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    โ€ข Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    โ€ข Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[โˆš] VS Code (version 1.76.1)
    โ€ข VS Code at C:\Users\hi\AppData\Local\Programs\Microsoft VS Code
    โ€ข Flutter extension version 3.60.0

[โˆš] Connected device (4 available)
    โ€ข sdk gphone64 x86 64 (mobile) โ€ข emulator-5554 โ€ข android-x64    โ€ข Android 13 (API 33) (emulator)
    โ€ข Windows (desktop)            โ€ข windows       โ€ข windows-x64    โ€ข Microsoft Windows [Version 10.0.19044.2604]
    โ€ข Chrome (web)                 โ€ข chrome        โ€ข web-javascript โ€ข Google Chrome 111.0.5563.64
    โ€ข Edge (web)                   โ€ข edge          โ€ข web-javascript โ€ข Microsoft Edge 110.0.1587.69

[โˆš] HTTP Host Availability
    โ€ข All required HTTP hosts are available

โ€ข No issues found!

Issue for initializing DateTime pure FormzInput

Hello,
I have a date time text field in my form and I have to initialize this FormzInput with the Pure constructor like this :

image

As you can see DateTime.now is not a constant value, I can't see how i can do it with this library.

Do you have an idea ?

Thanks

FormzSubmissionStatus

This is my class and constructor
1

whenever i pass the arguments in Formz.validate function i am getting error in status variable.

2

error
The argument type 'bool' can't be assigned to the parameter type 'FormzSubmissionStatus?'.

Validate form on submit

Hey guys,

Thanks for the awesome package.

I was following the implementation of form validation but I am having a problem trying to fit in clients' requirements: not to validate field until the login button is pressed. After the button is pressed I should show the errorText under the textField. I am using Cubit for that particular screen but so far I was not able to do anything that goes smoothly and without many side effects. Any idea how to achieve it?

toJson fromJson support

When using Formz along with hydrated bloc we can not hydrate state. We need to add toJson fromJson on every input.

It would be great if formz input supports it.

After submissionFailure not able to change existing value in the field

So it goes like this: I am having a basic login flow: email and password, both validated with Formz. After I enter the wrong pass or wrong email API call is returning error and I am showing it to the user. Field values are staying as it is and then I want to change only one field, let's say email is misspelled, and my API is failing again because the untouched field is being pure hence not valid.

I was not able to play the login example to see is it happening there as well, but my code implementation is totally the same.

Thanks in advance!

Remove or separate submission status from FormzStatus

Formz currently (and incorrectly) conflates submission status with validity and purity. Issue #45 discusses separating purity from validity.

Ideally, formz should remove the submission status entirely, since it does not make sense for individual form fields to track the submission status for the entire form, and formz does not provide the notion of a form body containing multiple fields currently.

I do not understand purity vs. validity.

This may be related to #45. I am new to this package so let me know if I am misunderstanding. But why is purity linked to validity? Can't a FormzInput be both pure and valid? I am not understanding why status presents these as mutually-exclusive options.

Thanks!

Allow override validator error

For now, FormzInput provides error from the validator method but sometimes need to provide error from the database as firebase for example. Of course, it is possible to create separate error values in the state but I noticed that Formz is much cleaner. I have my own class like in the example below it works as expected.

abstract class FormzInput<T, E> {
  const FormzInput._({required this.value, E? error, this.isPure = true})
      : _error = error;
  const FormzInput.pure({required T value}) : this._(value: value);
  const FormzInput.dirty({required T value, E? error})
      : this._(value: value, error: error, isPure: false);

  final T value;
  final E? _error;
  final bool isPure;

  bool get isValid => error == null ;
  bool get isNotValid => !isValid;
  E? get displayError => isPure ? null : error;
  E? get error => _error ?? validator(value); // <- ignoring validator error when `_error` is not `null`.

  E? validator(T value);
  
  //... 
}

In my example dirty constructor does not request provide error from the user, it just ignores if it is null, and does not affect old user code. By creating a new form users can decide how to initialize a dirty constructor with the error or without error.

[QUESTION] Formz is validating a textfield with text as empty unless textfield is changed

Please, I have been having this issue for a while. I use flutter formz with bloc. I have the username and password textfields. If a user enters a wrong password, but with a correct username, i want the user to only change the password and not have to change the username or touch the username textfield. But formz seems to need both textfields to be edited on error. Please how do I solve this?
image

I am using the flutter_bloc login example from the felaangel repo.

For the login bloc

  void _onUsernameChanged(
    LoginUsernameChanged event,
    Emitter<LoginState> emit,
  ) {
    final username = LoginUsername.dirty(event.username);
    emit(
      state.copyWith(
        username: username,
        status: Formz.validate([username]),
      ),
    );
  }

  void _onPasswordChanged(
    LoginPasswordChanged event,
    Emitter<LoginState> emit,
  ) {
    final password = LoginPassword.dirty(event.password);
    emit(
      state.copyWith(
        password: password,
        status: Formz.validate([password]),
      ),
    );
  }

  Future<void> _onSubmitted(
    LoginSubmitted event,
    Emitter<LoginState> emit,
  ) async {
    final username = LoginUsername.dirty(state.username.value);
    final password = LoginPassword.dirty(state.password.value);
    final status = Formz.validate([username, password]);
    emit(
        state.copyWith(username: username, password: password, status: status));
    if (state.status.isValidated) {
      emit(state.copyWith(status: FormzStatus.submissionInProgress));
      try {
        final token = await _authenticationRepository.loginUser(
          username: state.username.value,

          password: state.password.value,
        );
        emit(state.copyWith(status: FormzStatus.submissionSuccess));
        emit(LoginLoading());
        emit(LoggedIn(token: token));
      } catch (e) {
        emit(state.copyWith(status: FormzStatus.submissionFailure));
        emit(LoginFailure(error: e.toString()));
      }
    }
  }

Login State

class LoginState extends Equatable {
  const LoginState({
    this.status = FormzStatus.pure,
    this.username = const LoginUsername.pure(),
    this.password = const LoginPassword.pure(),
  });

  final FormzStatus status;
  final LoginUsername username;
  final LoginPassword password;

  LoginState copyWith({
    FormzStatus? status,
    LoginUsername? username,
    LoginPassword? password,
  }) {
    return LoginState(
      status: status ?? this.status,
      username: username ?? this.username,
      password: password ?? this.password,
    );
  }

  @override
  List<Object> get props => [status, username, password];
}

Issue error text from Formz

Keep in mind I'm pretty new to development so perhaps there is an obvious solution I'm missing.

I would like to have an option for different error messages. For example maybe a description field could give an error message that says "this field can't be blank" and another that says something about the length of the input if it is too long.

enum DescriptionValidationError { empty, tooLong }

class Description extends FormzInput<String, DescriptionValidationError> {
  const Description.pure() : super.pure('');
  const Description.dirty([String value = '']) : super.dirty(value);

  @override
  DescriptionValidationError validator(String value) {
    if (value.isEmpty) {
      return DescriptionValidationError.empty;
    } else if (value.length > 10) {
      return DescriptionValidationError.tooLong;
    } else {
      return null;
    }
  }
}

Is there a way in my textfield widget to say
errorText: state.description.errorText

I can do this in my widget but I'm not sure its really best to have that logic in the widget - it feels like it should be in the class that extends formz?

return BlocBuilder<InputCubit, InputState>(
      buildWhen: (previous, current) =>
          previous.description != current.description,
      builder: (context, state) {
        String errorText;
        switch (state.description.error) {
          case DescriptionValidationError.empty:
            errorText = 'This cant be empty';
            break;
          case DescriptionValidationError.tooLong:
            errorText = 'Its too long';
            break;
          default:
            errorText = null;
            break;
        }
        return FormTextInput(
          formKey: Key('recipeInput_descriptionField_textField'),
          onChanged: (title) =>
              context.read<InputCubit>().descriptionInputChanged(title),
          labelText: 'Description',
          errorText: state.description.invalid ? errorText : null,
          maxLength: 160,
        );
      },
    );

Best,
Nick

Trigger validation when user clicks on submit

I have introduced Bloc and Formz for a Flutter application which I am currently doing.

What I was trying to achieve currently was a simple text field in a form that shows validation errors. I noticed that the behaviors of valid and invalid are working differently than one would expect.

If I have a property called fullname in a Bloc, I expect that these two would have the same behaviors:

  • state.fullName.valid
  • !state.fullName.invalid

But the results are different for both. Am I missing something here?

To Reproduce

Steps to reproduce the behavior:
We can follow @felangel 's flutter_login project for a reference to this.

https://github.com/felangel/bloc/tree/master/examples/flutter_login

Here, in the widgets of the login form, we can try switching the "errorText" values to have either of the values.

  • errorText: state.username.valid ? null: 'invalid username',
  • errorText: state.username.invalid ? 'invalid username' : null,

Expected behavior

I expect that the boolean returned by valid and invalid would be opposites of each other, which is not the case.

Screenshots
With:
errorText: state.password.valid ? null : 'invalid password',

Screenshot from 2022-04-19 10-06-39

With:
errorText: state.password.invalid ? 'invalid password' : null,

Screenshot from 2022-04-19 10-07-05

Additional context
I have a sign up form, which has a couple of text fields, and drop downs. The issue I am having is that the when using the valid property, the error text is being displayed to the user even before the user has entered anything (in the pure state).

When I am using the invalid property, this issue is solved. But, unlike the flutter_login example, we have a user requirement where we don't disable the submit button. And I need to trigger the validation error texts for the text fields once the submit is pressed. I have an event which emits this validation.


      emit(
        state.copyWith(
          status: Formz.validate(
            [
              state.fullName,
              state.email,
              state.phoneNumber,
            ],
          ),
        ),
      );

But this does not work. It would be of tremendous help if someone could show me a resolve for this issue.

Validation of 2 fields against each other

@felangel

This is related to issue #5

I am curious to how it would work out if there are 2 inputs that have to be validated against each other.

This method won't work anymore since 2 objects cannot co-create each other.

@felangel what would be a direction in this case?

The example of would be 2 time entries that need to be verified against each other.

Like start time and end time.

If I add the code example from above the the end_time field then it is only verified against start_time. But start_time is not verified against the initial field and I am struggling to find an elegant solution to solve this.

How to reset the status of the formz instance

Hello,

I followed an example in Login bloc instance and applied this to my custom form.

2 questions:

  1. Once I click submit the form and Formz.status jumps to isSubmissionSucessful it never goes back to initial state. How do I force the state to .Pure or .Initial?
  2. Can formz be used with builtin forms in flutter instead of only TextFields?

Thanks

mixin isDirty

mixin isDirty has a wrong description or implementation

/// Whether all of the [FormzInput] are dirty.

/// Whether all of the [FormzInput] are dirty.
bool get isDirty => !isPure;

Currently, it outputs if any FormzInput is dirty

how to compare two input value in the validator?

I just want to compare to input value. For example password and confirm password.
I want to compare in confirm password class.
How can I get the password input value in validator function in PasswordConfirm class?

enum PasswordConfirmValidationError { empty, passwordNotMatch }

class PasswordConfirm
    extends FormzInput<String, PasswordConfirmValidationError> {
  const PasswordConfirm.pure() : super.pure('');
  const PasswordConfirm.dirty([String value = '']) : super.dirty(value);

  @override
  PasswordConfirmValidationError validator(String value) {
    // how can I get a password input value? so I can do this.
    if (value?.isNotEmpty) {
      if(value == password) {
        return null;
      } else {
        return PasswordConfirmValidationError.passwordNotMatch;
      {
    } else {
       return PasswordConfirmValidationError.empty;
    }
    return null;
  }
}

Separate purity from validity

Formz currently (and incorrectly) conflates purity with validity.

Imagine a class which contains a username input field (which is un remarkable implementation of a FormzInput).

class RegistrationState {
  final UsernameInput username;
  bool get isBusy => ...
  bool get canSubmit =>
      Formz.validate([username]) == FormzStatus.valid && !isBusy;
}

Currently, the following test fails, despite the fact that unicorn is considered a valid string by the implementation of UsernameInput (not shown).

test('true when username is valid and not busy', () {
  const state = RegistrationState(
    username: UsernameInput.pure(value: 'unicorn'),
    isCheckingUsername: false,
    status: RegistrationStatus.editing,
  );
  expect(state.canSubmit, true);
});

The reason for the failure is because the value of Formz.validate([username]) is FormzStatus.pure, rather than FormzStatus.valid.

I believe FormzInput shoulds have a separate field which tracks the field's purity. Additionally, it may be suitable to have a displayError property which is null when the field is pure, as UI's commonly disregard errors on unmodified (but incorrect) fields.

abstract class FormzInput<T, E> {
  final E? get displayError => pure ? null : error;
  ...
}

How to perform different type of validation

  1. I want to validate 2 form fields i.e., Password & Confirm Password.
    -> I don't know how to do that so I couldn't try anything.
  2. I also want to validate a list (array of images) that whether it contains images or not and it should not contain more than 3 images.
    -> Below is the code the same for that. What is happening is when the first image is selected it is being stored in the array and displayed properly but when the second image is selected it is not being displayed (I cannot find out why). I tried to debug the code and find out the BlocBuilder was not being called after 2nd image is being selected.

Here is the class for image validation ->

import 'package:formz/formz.dart';

class SelectSubImage extends FormzInput<List<File>, String> {
  const SelectSubImage.pure() : super.pure(null);
  const SelectSubImage.dirty([List<File> value]) : super.dirty(value);

  @override
  String validator(List<File> value) {
    if (value == null || value.isEmpty) {
      return 'Please select atleast one sub image';
    } else if (value.length > 3) {
      return 'Only 3 images can be selected';
    }
    return null;
  }
}

Here is the event class ->

abstract class CreateAdEvent extends Equatable {
  const CreateAdEvent();

  @override
  List<Object> get props => [];
}

class CreateAdInitialEvent extends CreateAdEvent {
  const CreateAdInitialEvent();
}

class CreateAdSubImageAddedEvent extends CreateAdEvent {
  final File subImage;

  CreateAdSubImageAddedEvent(this.subImage);

  @override
  List<Object> get props => [subImage];
}

Here is the State class ->

class CreateAdState extends Equatable {
  final FormzStatus status;
  final SelectSubImage images;
  

  CreateAdState({
    this.status = FormzStatus.pure,
    this.images,
  });

  CreateAdState copyWith({
    CreateAdStatus status,
    SelectSubImage images,
  }) {
    return CreateAdState(
      status: status ?? this.status,
      images: images ?? this.images,
    );
  }

  @override
  List<Object> get props => [ status, images ];
}

Here is the bloc class ->

class CreateAdBloc extends Bloc<CreateAdEvent, CreateAdState> {
  CreateAdBloc(this._repository) : super(CreateAdState());
  final CreateAdRepository _repository;

  @override
  Stream<CreateAdState> mapEventToState(
    CreateAdEvent event,
  ) async* {
    if (event is CreateAdSubImageAddedEvent) {
      yield _mapSubImageAddedEventToState(event, state);
    }
  }

  CreateAdState _mapSubImageAddedEventToState(
      CreateAdSubImageAddedEvent event, CreateAdState state) {
    List<File> images =
        state.images == null ? new List<File>() : state.images.value;
    images.add(event.subImage);
    SelectSubImage subImages;
    if (state.images != null) {
      state.images.value.add(event.subImage);
    } else {
      subImages = SelectSubImage.dirty(images);
    }
    return state.copyWith(
        images: subImages, status: CreateAdStatus.contentChange);
  }
}

Here is the specific widget for the above bloc ->

class CreateAdSubImageSelection extends StatelessWidget {
  final AppTextStyle textStyle;

  const CreateAdSubImageSelection({Key key, this.textStyle}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CreateAdBloc, CreateAdState>(
      builder: (context, state) {
        List<File> images = state.images == null || state.images.value.isEmpty ? new List<File>() : state.images.value;
        return Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Text(
                  'Images',
                  textAlign: TextAlign.left,
                  style: textStyle,
                ),
              ],
            ),
            SizedBox(height: 8),
            Container(
              height: 140,
              child: ListView.separated(
                scrollDirection: Axis.horizontal,
                itemCount: images.length + 1,
                separatorBuilder: (context, index) {
                  return SizedBox(width: 20);
                },
                itemBuilder: (context, index) {
                  return GestureDetector(
                    onTap: () => _pickMainImage(context),
                    child: Container(
                      height: 140,
                      width: 140,
                      padding: EdgeInsets.zero,
                      child: Center(
                        child: images.length == index ? Icon(Icons.add) : Image.file(images[index]),
                      ),
                    ),
                  );
                },
              ),
            ),
          ],
        );
      }
    );
  }

  Future _pickMainImage(BuildContext context) async {
    final pickedFile =
    await ImagePicker().getImage(source: ImageSource.gallery);
    context
        .read<CreateAdBloc>()
        .add(CreateAdSubImageAddedEvent(File(pickedFile.path)));
  }
}

Could you please explain how to achieve these 2 cases?
TIA

Some fields do not require validation

Every time a FormZInput is defined we must add a validation error even if we never get to use it. Some fields do not require validation, but do require being inside the form to later verify if the field has changed its value.

Example:

// abstract class FormzInput<T, E>

enum StatusMemberInputError { none }

class StatusMemberInput extends FormzInput<bool, StatusMemberInputError> {
  const StatusMemberInput.pure({bool value = false}) : super.pure(value);

  const StatusMemberInput.dirty({bool value = false}) : super.dirty(value);

  @override
  StatusMemberInputError? validator(bool value) => null;
}

Need a good example

This package is very different from anyother form related package in a flutter community.
It looks very promising.

The main problem is the lack of examples of proper implementation with UI,
Kindly give us a sample of how this package will be used.

Pure for DateTime not working

Hello ,

`class DateInput extends FormzInput<DateTime, DateValidationError> {
const DateInput.pure() : super.pure(null);
const DateInput.dirty(DateTime value) : super.dirty(value);

@OverRide
DateValidationError? validator(DateTime value) {
return null;
}
}`

this code get me an error indicating that The argument type 'Null' can't be assigned to the parameter type 'DateTime'

How can I pure DateTime currently.

How to map the `isSubmissionFailure` with the reason?

Currently when we emit the submissionFailure, we cannot specify the reason for why it failed.

Example:

Considering a simple sign-up flow, there might be a case when the current user has already registered with the given email/username, in that case, we should be able to emit the submissionFailure with some sort of message to use that in the form.

If there exists this particular functionality, please add an example.

Validate field against another field's value

Currently dirty and validator method accept only an argument. It would be great if dirty and validator accept one more argument on the same type as first argument. My use case is to validate confirm password against password.

Support for null-safety

As a developer, I'd like this package to support null-safety features, so we can stay up to do date with Flutter's and Dart's API changes.

`Error: Couldn't find constructor 'FormzInput._'.` when running `flutter test`

So I'm using formz 0.4.0 in my project (great library, thanks!), but recently I'm starting to get these errors when running flutter test (I changed my Flutter directory to $FLUTTER for privacy reasons):

$FLUTTER/.pub-cache/hosted/pub.dartlang.org/formz-0.4.0/lib/formz.dart:96:36: Error: Couldn't find constructor 'FormzInput._'.
  const FormzInput.pure(T value) : this._(value);
                                   ^^^^
$FLUTTER/.pub-cache/hosted/pub.dartlang.org/formz-0.4.0/lib/formz.dart:99:37: Error: Couldn't find constructor 'FormzInput._'.
  const FormzInput.dirty(T value) : this._(value, false);

I suspect that this might be a bug in Dart, but I thought I'd ask here first. Do you have any idea about the cause and how to fix this?

These errors were produced on a macOS Big Sur machine, with Flutter 2.0.6 and Dart 2.12.3.

formkey.currentState!.validate() doesn't call FormzInput.validate()?

Hi!
I have a trouble with validation form fields.
Let's say I have firstName and lastName form field with initial value(from database)
so form input is pre-populated with value.

But I want to change just firstName field and it calls onChanged function.
And onChanged function calls FirstNamChanged bloc event like this

AccountInfoFormState _firstNameChangedToState(
      FirstNameChanged event, AccountInfoFormState state) {
    final firstName = FirstName.dirty(event.firstName);
    return state.copyWith(
      firstName: firstName,
      status: Formz.validate([
        firstName,
        state.lastName,
      ]),
    );
  }

I don't touch lastName field, so onChanged function is not called and so intial state of lastName is empty.
So Formz.validate([firstName, state.lastName]) will return invalidated.

How can I pass intial state to FormzInput ?

And I tried to submit the form with empty value. like this

if(formKey.currentState!.validate()) 

but it returns form is validated
It doesn't seems to call each FormzInputs' validator function

What am I missing?

Handle Boolean form fields

The library right now does not seem to have a way to handle boolean form fields since the super.dirty constructor takes strings only.

Does value argument for FormzInput.validator could be null ?

Im using null safety and have to force value to be non null when building FormzInput.validator but as T value field could not be null does this make sens to accept nullable value for field validator ?

maybe im misunderstanding every usage of validator.

Thank your for this package!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.