Skip to content

[General]: AutovalidateMode.always shouldn't focus to first field #1457

Closed
@DarenF-20C

Description

@DarenF-20C

Is there an existing issue for this?

  • I have searched the existing issues

Package/Plugin version

9.7.0

Platforms

  • Android
    iOS
    Linux
    MacOS
    Web
    Windows

Flutter doctor

Flutter doctor
[✓] Flutter (Channel stable, 3.27.1, on macOS 14.6.1 23G93 darwin-arm64, locale
    en-MY)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 16.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.1)
[✓] VS Code (version 1.96.2)
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!

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 FlutterExample());
}

class FlutterExample extends StatelessWidget {
  const FlutterExample({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const TheForm(),
              ),
            );
          },
          child: const Text('Go to Next Page'),
        ),
      ),
    );
  }
}

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

  @override
  State<TheForm> createState() => _TheFormState();
}

class _TheFormState extends State<TheForm> {
  final _formKey = GlobalKey<FormBuilderState>();
  bool _isValid = false;

  InputDecoration _defaultInputDecoration(String placeholder) =>
      InputDecoration(labelText: placeholder);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Form Page'),
      ),
      body: SafeArea(
        child: FormBuilder(
          key: _formKey,
          onChanged: () {
            setState(() {
              _isValid = _formKey.currentState!.isValid;
            });
          },
          child: Column(
            children: [
              FormBuilderTextField(
                name: 'name',
                decoration: _defaultInputDecoration('Name'),
                validator: FormBuilderValidators.required(),
                autovalidateMode: AutovalidateMode.always,
              ),
              FormBuilderTextField(
                name: 'email',
                decoration: _defaultInputDecoration('E-Mail'),
                validator: FormBuilderValidators.compose([
                  FormBuilderValidators.required(),
                  FormBuilderValidators.email(),
                ]),
                autovalidateMode: AutovalidateMode.always,
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(_isValid ? 'Form is valid' : 'Form is not valid'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Current Behavior

When set autovalidateMode: AutovalidateMode.always, the last field will be focus when enter the page which unexpected and logically weird.

Expected Behavior

When set autovalidateMode: AutovalidateMode.always, the first required field being focus when enter the page.
But the best, is allowing user to pass in whether to focus the field when it sets to required.

As the validate provides the control of focusOnInvalid which we can back to previous behaviour.

Steps To Reproduce

  1. run the code and able to see a home page with button
  2. press the button to next page.
  3. last field (email) being focus, but not the first.
  • Based on previous behaviour (upgraded from 7.8.0), it won't focus the field when entering page.

Aditional information

issue_formBuilder.mov

From the video, shows what is not expecting.

Activity

DarenF-20C

DarenF-20C commented on Jan 6, 2025

@DarenF-20C
Author

@deandreamatias, Can you help to take a look on this?

deandreamatias

deandreamatias commented on Jan 6, 2025

@deandreamatias
Collaborator

Hi!
I can replicated this issue.
Is related with other issues with AutoValidateMode and also with focus problems.
I will close this PR #1453 and after that, I will work in a new PR that solve all issues with AutoValidateMode.

Can follow the discussion on #1458

DarenF-20C

DarenF-20C commented on Jan 7, 2025

@DarenF-20C
Author

Thanks for bringing this up, and I really appreciate your quick response and fixes.

For the moment, is there any possible workaround for the current focus issue? As I know for abnormal focus issue, putting the autovalidatemode in each field instead of FormBuilder solves it as a workaround.

deandreamatias

deandreamatias commented on Jan 7, 2025

@deandreamatias
Collaborator

Maybe a way is set a key on a first field that have validator and force the focus when load the Form.
Some like that:

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter FormBuilder Example',
      debugShowCheckedModeBanner: false,
      localizationsDelegates: const [
        FormBuilderLocalizations.delegate,
        ...GlobalMaterialLocalizations.delegates,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: FormBuilderLocalizations.supportedLocales,
      home: const _ExamplePage(),
    );
  }
}

class _ExamplePage extends StatefulWidget {
  const _ExamplePage();

  @override
  State<_ExamplePage> createState() => _ExamplePageState();
}

class _ExamplePageState extends State<_ExamplePage> {
  final _formKey = GlobalKey<FormBuilderState>();
  final _firstFormFieldKey = GlobalKey<FormBuilderFieldState>();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _firstFormFieldKey.currentState?.focus();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Minimal code example')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: FormBuilder(
          key: _formKey,
          child: Column(
            children: [
              FormBuilderFilterChip<String>(
                key: _firstFormFieldKey,
                autovalidateMode: AutovalidateMode.always,
                decoration: const InputDecoration(
                  labelText: 'The language of my people',
                ),
                name: 'languages_filter',
                selectedColor: Colors.red,
                options: const [
                  FormBuilderChipOption(
                    value: 'Dart',
                    avatar: CircleAvatar(child: Text('D')),
                  ),
                  FormBuilderChipOption(
                    value: 'Kotlin',
                    avatar: CircleAvatar(child: Text('K')),
                  ),
                  FormBuilderChipOption(
                    value: 'Java',
                    avatar: CircleAvatar(child: Text('J')),
                  ),
                  FormBuilderChipOption(
                    value: 'Swift',
                    avatar: CircleAvatar(child: Text('S')),
                  ),
                  FormBuilderChipOption(
                    value: 'Objective-C',
                    avatar: CircleAvatar(child: Text('O')),
                  ),
                ],
                validator: FormBuilderValidators.compose([
                  FormBuilderValidators.minLength(1),
                  FormBuilderValidators.maxLength(3),
                ]),
              ),
              FormBuilderTextField(
                name: 'text',
                decoration: const InputDecoration(labelText: 'Text'),
                autovalidateMode: AutovalidateMode.always,
                validator: FormBuilderValidators.compose([
                  FormBuilderValidators.required(),
                  FormBuilderValidators.minLength(5),
                ]),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
changed the title [-][General]: `AutovalidateMode.always` shouldn't focus field at initial[/-] [+][General]: `AutovalidateMode.always` shouldn't focus to first field[/+] on Jan 7, 2025
deandreamatias

deandreamatias commented on Jan 8, 2025

@deandreamatias
Collaborator

@DarenF-20C the solution that I working is that not focus at all (#1460). Only validate all fields and show errors, but not focus on any field.

If want focus in any field, can force with keys.
With a field key

final textFieldKey = GlobalKey<FormBuilderFieldState>();
textFieldKey.currentState?.focus()

or with a form key

final formKey = GlobalKey<FormBuilderState>();
final textFieldName = 'name'
formKey.currentState?.fields[textFieldName]?.focus()

What do you thing? Is good enogth?

DarenF-20C

DarenF-20C commented on Jan 9, 2025

@DarenF-20C
Author

Yes! That solves my issue. Is it going to be release soon?

DarenF-20C

DarenF-20C commented on Jan 9, 2025

@DarenF-20C
Author

I’ve checked the changes and tested them, and they align with the expected default behaviour of AutovalidateMode.always:

  1. It displays errors for all fields.
  2. It doesn’t focus on any field by default.

If focus needed, may use validate() or manually focus on a field to adjust this behaviour.

deandreamatias

deandreamatias commented on Jan 9, 2025

@deandreamatias
Collaborator

So, I think that you tested the changes on PR branch (feature/1458-improve-autovalidate-modes) right?

The estimated release date for this version is 1 week max.
I want to work on other improvements for the autovalidate mode to make it more robust.

It will be released first as a prerelease, on 10.0.0-dev.2

I don't have a release date for 10.0.0 as stable, because it's a big change and I want to check everything to avoid inconvenience to users, even though it is a breaking change

DarenF-20C

DarenF-20C commented on Jan 10, 2025

@DarenF-20C
Author

Yeah I tested on that PR to check the behaviour, the PR mets what I need. 🥂 🎉

Sure. Would like to have the fix on dev.2 first. Really appreciate your help on this issue. 🙏

DarenF-20C

DarenF-20C commented on Jan 13, 2025

@DarenF-20C
Author

Hi, Kindly follow up if the dev.2 release able to roll out soon. 👍

deandreamatias

deandreamatias commented on Jan 13, 2025

@deandreamatias
Collaborator
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

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Participants

      @deandreamatias@DarenF-20C

      Issue actions

        [General]: `AutovalidateMode.always` shouldn't focus to first field · Issue #1457 · flutter-form-builder-ecosystem/flutter_form_builder