Repository navigation
FormField not showing error when invalid #179794
Copy link
Copy link
Open
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listfound in release: 3.38Found to occur in 3.38Found to occur in 3.38found in release: 3.40Found to occur in 3.40Found to occur in 3.40frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onteam-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team
Description
Steps to reproduce
- Create a flutter project;
- Paste the code below in the
main.dart; - Run the project;
- With the radio buttons unmarked, press the
Validatebutton; - Check that no error message was displayed;
- 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.

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: falseFlutter 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!Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listfound in release: 3.38Found to occur in 3.38Found to occur in 3.38found in release: 3.40Found to occur in 3.40Found to occur in 3.40frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onteam-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team
Activity
MuhammadMashoodSiddiquie commentedon Dec 15, 2025
I see the issue persists, Inside the builder we have state which is getting the error text correctly from validator, but the errorBuilder doesn't get the correct value, Below is the code with the state.errorText as well.
I am using Flutter 3.38.3, Dart 3.10.1.
Following the code attached FYI:
darshankawar commentedon Dec 15, 2025
Thanks for the report. Seeing the same behavior as reported with latest sdk versions.
bleroux commentedon Dec 15, 2025
FormField is a generic widget, it does not have an opinionated way to render UI. It is up to the code provided through
FormField.builderto decide how to render the error (for instance, by creating a Column to display the error text below or above the form field content).Subclasses such as
TextFieldFormFieldandDropdownMenuFormFieldcomply with the Material specification which states where the error should be shown. Those widgets exposes the errorBuilder property which make it possible to customise the rendering of the error.MuhammadMashoodSiddiquie commentedon Dec 15, 2025
@bleroux
So why do we have the errorBuilder inside formField widget?, If the case is to handle the UI of error in builder so what's the purpose of having errorBuilder inside the FormField?
bleroux commentedon Dec 15, 2025
Adding
errorBuildertoFormFieldwas probably a mistake. Adding it at theFormFieldlevel was convenient, but it would have been better to not add it toFormBuilderand declare it only on subclasses when needed.@justinmc WDYT of deprecating
FormField.errorBuilderin favor of local parameters inTextFormFieldandDropdownButtonFormFieldconstructors?SamuelGadiel commentedon Dec 17, 2025
Thing is, either by using the
errorBuilderor not, the message does not automatically appear when there is an invalid state.The
errorBuildercould come in hand to personalize how the error is displayed, but it isn't necessary for the error message to be presented in the first place.Even without the
errorBuilderthe error message should appear, but that doesn't happen.Yeah, we have
TextFormFieldandDropdownButtonFormField, but can also build custom form fields, like i did in the example with the radio buttons. In these situations, building custom fields, we need to rely onFormField.And we would expect that by using
FormFieldthe error message would appear when in invalid state and, as a bonus, that we could be able to personalize how the message is shown (again, these are two separated things).FormFieldhas the base structure for this, it's expected to work like this, following the structure from all of the other fields.We shouldn't put it in a
Columnand present the error manually. This shouldn't be the way to go.For me, the correct path is for the
FormFieldto show the error message, just like all the other fields, and to allow for the user to personalize it with theerrorBuilderit they want it to.justinmc commentedon Jan 2, 2026
I agree that having errorBuilder in FormField but not having it do anything is a confusing API design. I didn't realize it at the time of #162255.
Maybe errorBuilder (or its return value) should be passed into
builder? I think that would work for TextFormField.bleroux commentedon Jan 13, 2026
I tried this solution, it means changing the signature of
FormFieldBuildertypedef (to add an optional parameter forerrorBuilder). The problem is that it requires subclasses to update the signature of the function passed to initialize theFormField.builderfield. It would impact anyFormFieldsublasses.