Skip to content

FormField not showing error when invalid #179794

@SamuelGadiel

Description

@SamuelGadiel

Steps to reproduce

  1. Create a flutter project;
  2. Paste the code below in the main.dart;
  3. Run the project;
  4. With the radio buttons unmarked, press the Validate button;
  5. Check that no error message was displayed;
  6. Check the logs for analysis

Expected results

We would expect that, when validating an invalid field, a message would be presented below the field.
The errorBuilder is optional. If we want to personalize how the error message is shown, we could use the errorBuilder to provide a custom widget for it.
But the errorBuilder wouldn't be required for the message to be shown.

Actual results

In both examples, with or without the errorBuilder, the error message is not displayed below the field

Code sample

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MyHomePage());
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _formKey = GlobalKey<FormState>();

  bool? _withoutErrorBuilder;
  bool? _withErrorBuilder;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Form(
          key: _formKey,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text('Field without errorBuilder'),
              FormField<bool>(
                initialValue: _withoutErrorBuilder,
                validator: (value) {
                  debugPrint('[Without errorBuilder] validator was called with: $value');
                  return value == null ? 'Select an option' : null;
                },
                builder: (state) {
                  return RadioGroup<bool>(
                    groupValue: _withoutErrorBuilder,
                    onChanged: (value) {
                      setState(() => _withoutErrorBuilder = value);
                      state.didChange(value);
                    },
                    child: const Row(
                      children: [
                        Expanded(child: RadioListTile<bool>(title: Text('Option 1'), value: true)),
                        Expanded(child: RadioListTile<bool>(title: Text('Option 2'), value: false)),
                      ],
                    ),
                  );
                },
              ),

              SizedBox(height: 64),

              Text('Field with errorBuilder'),
              FormField<bool>(
                initialValue: _withErrorBuilder,
                validator: (value) {
                  debugPrint('[With errorBuilder] validator was called with: $value');
                  return value == null ? 'Select an option' : null;
                },
                errorBuilder: (context, errorText) {
                  debugPrint('[With errorBuilder] errorBuilder was called with: $errorText');
                  return Text(errorText, style: const TextStyle(fontSize: 12));
                },
                builder: (state) {
                  return RadioGroup<bool>(
                    groupValue: _withErrorBuilder,
                    onChanged: (value) {
                      setState(() => _withErrorBuilder = value);
                      state.didChange(value);
                    },
                    child: const Row(
                      children: [
                        Expanded(child: RadioListTile<bool>(title: Text('Option 1'), value: true)),
                        Expanded(child: RadioListTile<bool>(title: Text('Option 2'), value: false)),
                      ],
                    ),
                  );
                },
              ),

              const SizedBox(height: 36),
              ElevatedButton(
                onPressed: () {
                  final isValid = _formKey.currentState!.validate();

                  debugPrint('[Without errorBuilder] value: $_withoutErrorBuilder');
                  debugPrint('[With errorBuilder] value: $_withErrorBuilder');
                  debugPrint('Form is valid: $isValid');
                },
                child: const Text('Validate'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

This print was taken after the button was pressed. No message was displayed below the field.
Image

Logs

Logs
I/flutter (25118): ---------------------- Validation ----------------------
I/flutter (25118): [Without errorBuilder] validator was called with: null
I/flutter (25118): [With errorBuilder] validator was called with: null
I/flutter (25118): 
I/flutter (25118): ---------------------- Result ----------------------
I/flutter (25118): [Without errorBuilder] value: null
I/flutter (25118): [With errorBuilder] value: null
I/flutter (25118): Form is valid: false

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.38.4, on macOS 15.6.1 24G90 darwin-arm64, locale pt-BR)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0-rc1)
[✓] Xcode - develop for iOS and macOS (Xcode 16.1)
[✓] Chrome - develop for the web
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work listfound in release: 3.38Found to occur in 3.38found in release: 3.40Found to occur in 3.40frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

Type

No type

Projects

Status

To do

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions