import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('errorBuilder copyWith Bug')),
body: Center(
child: Padding(
padding: const EdgeInsets.all(32),
child: Form(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
decoration: const InputDecoration(
labelText: 'TextFormField',
errorText: 'Decoration error',
),
errorBuilder: (context, error) {
return Row(
children: [
const Icon(Icons.error, color: Colors.red, size: 16),
const SizedBox(width: 4),
Text(error, style: const TextStyle(color: Colors.red, fontSize: 12)),
],
);
},
validator: (value) => value!.isEmpty ? 'Field is required' : null,
),
const SizedBox(height: 24),
DropdownButtonFormField<String>(
decoration: const InputDecoration(
hintText: 'Select an option',
errorText: 'Decoration error',
),
errorBuilder: (context, error) {
return Row(
children: [
const Icon(Icons.error, color: Colors.red, size: 16),
const SizedBox(width: 4),
Text(error, style: const TextStyle(color: Colors.red, fontSize: 12)),
],
);
},
items: const [
DropdownMenuItem(value: 'one', child: Text('One')),
DropdownMenuItem(value: 'two', child: Text('Two')),
],
validator: (value) => value == null ? 'Selection required' : null,
onChanged: (value) {},
),
const SizedBox(height: 24),
Builder(
builder: (context) => ElevatedButton(
onPressed: () => Form.of(context).validate(),
child: const Text('Validate'),
),
),
],
),
),
),
),
),
);
}
}
Steps to reproduce
Expected results
Using
errorBuilderalongsidedecoration: InputDecoration(errorText: ...)should work — theerrorBuilderwidget should replace the decoration'serrorTextwhen validation triggers.Actual results
The app crashes with:
Without
errorBuilder(added at PR #162255), theerrorTextpath only callscopyWith(errorText: field.errorText)with a non-null value — which works fine. ButerrorBuilderintroduced a code path that needs to clearerrorTextviacopyWith(errorText: null)to replace it with anerrorwidget, andInputDecoration.copyWithsilently ignoresnull(treats it as "keep existing value"). This leaves botherroranderrorTextset on the decoration, triggering the assertion.i.e in
DropdownButtonFormField,errorBuildertries to clearerrorTextand useerrorinstead, butcopyWithignores it:Code sample
Code sample
Screenshots or Video
No response
Logs
No response
Flutter Doctor output
Doctor output