Skip to content

FormState.reset() doesn't reset TextFormField if its parent rebuilds #180056

@Flamingloon

Description

@Flamingloon

Steps to reproduce

  1. Wrap your UI inside a Form widget and assign it a GlobalKey.
  2. Add a TextFormField inside the Form, and attach a TextEditingController initialized with an empty value.
  3. Update the value of the TextFormField by modifying the controller’s text and calling setState()
  4. Call _formKey.currentState!.reset() to reset the form fields to their initial state.

Expected results

That TextFormField should revert to the initial empty value.

Actual results

TextFormField stays at the value before setState is called.

Code sample

Minimal repro...
import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(home: FormDemoScreen()));
}

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

  @override
  State<FormDemoScreen> createState() => _FormDemoState();
}

class _FormDemoState extends State<FormDemoScreen> {
  final _formKey = GlobalKey<FormState>();
  final _textFieldController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Form(
        key: _formKey,
        child: Column(
          children: [
            TextFormField(
              controller: _textFieldController,
              decoration: const InputDecoration(labelText: "Type something here - it won't reset!"),
              onChanged: (String value) {
                // However, commenting this out will make the reset work:
                setState(() {});
              },
            ),
        
            FormField<bool>(
              initialValue: false,
              builder: (FormFieldState<bool> state) {
                return CheckboxListTile(
                  title: const Text('This does reset though!'),
                  value: state.value ?? false,
                  onChanged: state.didChange,
                );
              },
            ),

            ElevatedButton(
              onPressed: () => _formKey.currentState?.reset(),
              child: const Text('Reset Form'),
            ),
          ],
        ),
      ),
    );
  }
}
Detailed 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(
      title: 'Form Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const FormDemoScreen(),
    );
  }
}

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

  @override
  State<FormDemoScreen> createState() => _FormDemoState();
}

class _FormDemoState extends State<FormDemoScreen> {
  final _formKey = GlobalKey<FormState>();
  final _nameController = TextEditingController();
  final _emailController = TextEditingController();

  final ValueKey<String> _key = const ValueKey("value");

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Form Demo')),
      body: Form(
        key: _formKey,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            children: [
              Text('Hello: ${_nameController.text}'),
              // Text fields (already FormField widgets)
              TextFormField(
                controller: _emailController,
                decoration: const InputDecoration(labelText: 'Email'),
                validator: (value) =>
                    value?.isEmpty ?? true ? 'Required' : null,
              ),
              TextFormField(
                key: _key,
                controller: _nameController,
                decoration: const InputDecoration(labelText: 'Name'),
                onChanged: (String value) {
                  setState(() {});
                },
                validator: (value) =>
                    value?.isEmpty ?? true ? 'Required' : null,
              ),

              // Radio buttons wrapped in FormField
              FormField<String>(
                initialValue: null,
                builder: (FormFieldState<String> state) {
                  return Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text('Gender:'),
                      RadioListTile<String>(
                        title: const Text('Male'),
                        value: 'male',
                        groupValue: state.value,
                        onChanged: state.didChange,
                      ),
                      RadioListTile<String>(
                        title: const Text('Female'),
                        value: 'female',
                        groupValue: state.value,
                        onChanged: state.didChange,
                      ),
                      RadioListTile<String>(
                        title: const Text('Other'),
                        value: 'other',
                        groupValue: state.value,
                        onChanged: state.didChange,
                      ),
                    ],
                  );
                },
              ),

              // Checkboxes wrapped in FormField
              FormField<bool>(
                initialValue: false,
                builder: (FormFieldState<bool> state) {
                  return CheckboxListTile(
                    title: const Text('Subscribe to Newsletter'),
                    value: state.value ?? false,
                    onChanged: state.didChange,
                  );
                },
              ),

              FormField<bool>(
                initialValue: false,
                builder: (FormFieldState<bool> state) {
                  return CheckboxListTile(
                    title: const Text('Agree to Terms & Conditions'),
                    value: state.value ?? false,
                    onChanged: state.didChange,
                  );
                },
              ),

              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  _formKey.currentState?.reset();
                },
                child: const Text('Reset Form'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.32.8, on macOS 15.5 24F74 darwin-arm64, locale en-IN) [752ms]
    • Flutter version 3.32.8 on channel stable at /Users/sahil.t/fvm/versions/stable
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision edada7c56e (5 months ago), 2025-07-25 14:08:03 +0000
    • Engine revision ef0cd00091
    • Dart version 3.8.1
    • DevTools version 2.45.1

[!] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [3.9s]
    • Android SDK at /Users/sahil.t/Library/Android/sdk
    • Platform android-36, build-tools 35.0.0
    • Java binary at: /Users/sahil.t/Library/Java/JavaVirtualMachines/jbr-17.0.14/Contents/Home/bin/java
      This JDK is specified in your Flutter configuration.
      To change the current JDK, run: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment JBR-17.0.14+1-1367.22-nomod (build 17.0.14+1-b1367.22)
    ! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses

[!] Xcode - develop for iOS and macOS (Xcode 16.4) [2.1s]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16F6
    ! CocoaPods 1.15.2 out of date (1.16.2 is recommended).
        CocoaPods is a package manager for iOS or macOS platform code.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/to/platform-plugins
      To update CocoaPods, see https://guides.cocoapods.org/using/getting-started.html#updating-cocoapods

[✓] Chrome - develop for the web [9ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2025.1) [8ms]
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.6+-13391695-b895.109)

[✓] VS Code (version 1.106.3) [7ms]
    • VS Code at /Applications/Visual Studio Code 3.app/Contents
    • Flutter extension version 3.124.0

[✓] Connected device (4 available) [10.6s]
    • ios            • iOS 18.5 22F76
    • iPhone 16 Plus (mobile)      • com.apple.CoreSimulator.SimRuntime.iOS-18-5 (simulator)
    • macOS (desktop)                • macos                                • darwin-arm64   • macOS 15.5 24F74 darwin-arm64
    • Chrome (web)                   • chrome                               • web-javascript • Google Chrome 143.0.7499.110

[✓] Network resources [495ms]
    • All expected network resources are available.

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work lista: text inputEntering text in a text field or keyboard related problemsd: api docsIssues with https://api.flutter.dev/d: examplesSample code and demosf: material designflutter/packages/flutter/material repository.frameworkflutter/packages/flutter repository. See also f: labels.r: fixedIssue is closed as already fixed in a newer versionteam-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