Skip to content

TextFormField & DropdownButtonFormField crash when using errorBuilder with decoration's errorText #183378

Description

@AhmedLSayed9

Steps to reproduce

  1. Run the sample code.
  2. Press "Validate" to trigger validation errors.

Expected results

Using errorBuilder alongside decoration: InputDecoration(errorText: ...) should work — the errorBuilder widget should replace the decoration's errorText when validation triggers.

Actual results

The app crashes with:

Declaring both error and errorText is not supported.

Without errorBuilder (added at PR #162255), the errorText path only calls copyWith(errorText: field.errorText) with a non-null value — which works fine. But errorBuilder introduced a code path that needs to clear errorText via copyWith(errorText: null) to replace it with an error widget, and InputDecoration.copyWith silently ignores null (treats it as "keep existing value"). This leaves both error and errorText set on the decoration, triggering the assertion.

i.e in DropdownButtonFormField, errorBuilder tries to clear errorText and use error instead, but copyWith ignores it:

// dropdown.dart
final Widget? error =
    field.errorText != null && errorBuilder != null
        ? errorBuilder(state.context, field.errorText!)
        : null;
final String? errorText = error == null ? field.errorText : null;

effectiveDecoration = effectiveDecoration.copyWith(
  error: error,
  errorText: errorText, // null when errorBuilder is used — copyWith ignores it
  hintText: hintText,
);

Code sample

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 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'),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Screenshots or Video

No response

Logs

No response

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.35.4, on macOS 26.3.1 25D2128 darwin-arm64, locale en-EG)
    • Flutter version 3.35.4 on channel stable
    • Framework revision d693b4b9db (6 months ago), 2025-09-16 14:27:41 +0000
    • Engine revision c298091351
    • Dart version 3.9.2
    • DevTools version 2.48.0

[✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.3)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.3)
[✓] VS Code (version 1.109.5)
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: text inputEntering text in a text field or keyboard related problemsfound in release: 3.41Found to occur in 3.41has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-text-inputOwned by Text Input teamtriaged-text-inputTriaged by Text Input team

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions