Skip to content

[FormBuilder]: AutovalidateMode Always and onUserInteraction break invalidate method #1484

Open
@GChanathip

Description

@GChanathip

Is there an existing issue for this?

  • I have searched the existing issues

Package/Plugin version

10.0.1

Platforms

  • Android
    iOS
    Linux
    MacOS
    Web
    Windows

Flutter doctor

Flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.29.0, on macOS 15.4 24E248 darwin-arm64, locale en-TH)
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0-rc2)
[!] Xcode - develop for iOS and macOS (Xcode 16.2)
    ! CocoaPods 1.15.2 out of date (1.16.2 is recommended).
        CocoaPods is a package manager for iOS or macOS platform code.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/to/platform-plugins
      To update CocoaPods, see https://guides.cocoapods.org/using/getting-started.html#updating-cocoapods
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.2)
[✓] VS Code (version 1.98.2)
[✓] Connected device (5 available)
    ! Error: Browsing on the local area network for Fw iPhone 15. Ensure the device is unlocked and attached with a
      cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
    ! Error: Browsing on the local area network for ChanathipiPad. Ensure the device is unlocked and attached with a
      cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
    ! Error: Browsing on the local area network for chanathip’s iPhone. Ensure the device is unlocked and attached
      with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
    ! Error: Browsing on the local area network for Jarawee’s iPhone. Ensure the device is unlocked and attached with
      a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
    ! Error: Browsing on the local area network for PAK. Ensure the device is unlocked and attached with a cable or
      associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
    ! Error: Browsing on the local area network for Intre . Ensure the device is unlocked and attached with a cable
      or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
    ! Error: Browsing on the local area network for BZ. Ensure the device is unlocked and attached with a cable or
      associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources

! Doctor found issues in 1 category.

Minimal code example

Code sample
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';

void main() {
  runApp(const MaterialApp(home: SignupForm()));
}

class SignupForm extends StatefulWidget {
  const SignupForm({super.key});

  @override
  State<SignupForm> createState() => _SignupFormState();
}

class _SignupFormState extends State<SignupForm> {
  final _formKey = GlobalKey<FormBuilderState>();
  final _emailFieldKey = GlobalKey<FormBuilderFieldState>();
  AutovalidateMode autovalidateMode = AutovalidateMode.disabled;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Signup Form')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: FormBuilder(
          key: _formKey,
          autovalidateMode: autovalidateMode,
          child: Column(
            children: [
              FormBuilderTextField(
                name: 'full_name',
                decoration: const InputDecoration(labelText: 'Full Name'),
                validator: FormBuilderValidators.compose([
                  FormBuilderValidators.required(),
                ]),
              ),
              const SizedBox(height: 10),
              FormBuilderTextField(
                key: _emailFieldKey,
                name: 'email',
                decoration: const InputDecoration(labelText: 'Email'),
                validator: FormBuilderValidators.compose([
                  FormBuilderValidators.required(),
                  FormBuilderValidators.email(),
                ]),
              ),
              const SizedBox(height: 10),
              FormBuilderTextField(
                name: 'password',
                decoration: const InputDecoration(labelText: 'Password'),
                obscureText: true,
                validator: FormBuilderValidators.compose([
                  FormBuilderValidators.required(),
                  FormBuilderValidators.minLength(6),
                ]),
              ),
              const SizedBox(height: 10),
              FormBuilderTextField(
                name: 'confirm_password',
                autovalidateMode: AutovalidateMode.onUserInteraction,
                decoration: InputDecoration(
                  labelText: 'Confirm Password',
                  suffixIcon: (_formKey.currentState?.fields['confirm_password']?.hasError ?? false)
                      ? const Icon(Icons.error, color: Colors.red)
                      : const Icon(Icons.check, color: Colors.green),
                ),
                obscureText: true,
                validator: (value) => _formKey.currentState?.fields['password']?.value != value ? 'No coinciden' : null,
              ),
              const SizedBox(height: 10),
              FormBuilderFieldDecoration<bool>(
                name: 'test',
                validator: FormBuilderValidators.compose([
                  FormBuilderValidators.required(),
                  FormBuilderValidators.equal(true),
                ]),
                // initialValue: true,
                decoration: const InputDecoration(labelText: 'Accept Terms?'),
                builder: (FormFieldState<bool?> field) {
                  return InputDecorator(
                    decoration: InputDecoration(
                      errorText: field.errorText,
                    ),
                    child: SwitchListTile(
                      title: const Text('I have read and accept the terms of service.'),
                      onChanged: field.didChange,
                      value: field.value ?? false,
                    ),
                  );
                },
              ),
              const SizedBox(height: 10),
              ElevatedButton(
                onPressed: () {
                  if (_formKey.currentState?.saveAndValidate() ?? false) {
                    if (true) {
                      // Either invalidate using Form Key
                      _formKey.currentState?.fields['email']?.invalidate('Email already taken.');
                      // OR invalidate using Field Key
                      // _emailFieldKey.currentState?.invalidate('Email already taken.');
                    }
                  } else {
                    setState(() {
                      autovalidateMode = AutovalidateMode.onUserInteraction;
                    });
                    debugPrint(_formKey.currentState?.value.toString());
                  }
                },
                child: const Text('Signup'),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Current Behavior

_formKey.currentState?.fields['email']?.invalidate('Email already taken.')
is not working when autovalidateMode = always or onUserInteraction

Expected Behavior

invalidate should show errorr correctly when autovalidateMode = always or onUserInteraction

Steps To Reproduce

from the example code that i provide

  • tap Signup button once to set autovalidateMode = onUserInteraction.
  • and then fill the form
  • tap signup button again.

Aditional information

i think the root cause is forceErrorText in thesuper classof FormBuilderField is null

Image

related issue #1281

Activity

added theissue type on Apr 9, 2025
GChanathip

GChanathip commented on Apr 11, 2025

@GChanathip
Author

can we just return false when _customErrorText != null ?

Image
deandreamatias

deandreamatias commented on Apr 11, 2025

@deandreamatias
Collaborator

Hi! Thanks for report
I can't take a look deeply on this, but you report make sense.
If you trying to solve this, take in account the edge cases and implement some tests.

I will take a look on PR if you open it.
Thanks a lot

GChanathip

GChanathip commented on Apr 17, 2025

@GChanathip
Author

I was wrong. Right now, I still don't know the root cause.
I’ll look into it again when I have time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    Status

    Ready

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @deandreamatias@GChanathip

        Issue actions

          [FormBuilder]: AutovalidateMode Always and onUserInteraction break invalidate method · Issue #1484 · flutter-form-builder-ecosystem/flutter_form_builder