Sitelet https://github.com/flutter/flutter/pull/123108
Skip to content

Fix FormField implementation components TextFormField, DropdownButtonFormField, CupertinoTextFormFieldRow. The problem that their onChange() method cannot be executed when the parent Form component calls the reset() method - #123108

Closed
yeleibo wants to merge 12 commits into
flutter:masterfrom
yeleibo:fix_FormField_onChange_unexecuted

Conversation

@yeleibo

@yeleibo yeleibo commented Mar 21, 2023

Copy link
Copy Markdown

fixes #123009

code sample ```
import 'package:flutter/cupertino.dart';
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(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(useMaterial3: true),
      home: Example(),
    );
  }
}

class Example extends StatelessWidget {
  final _formKey = GlobalKey<FormState>();
  Example({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Form(
          key: _formKey,
          child: Column(
            children: [
              CupertinoTextFormFieldRow(onChanged: (value) {
                print("CupertinoTextFormFieldRow widget onChange execute");
              }),
              TextFormField(onChanged: (value) {
                print("TextFormField widget onChange execute");
              }),
              DropdownButtonFormField(
                items: const [
                  DropdownMenuItem(
                    value: "item1",
                    child: Text("item1"),
                  )
                ],
                onChanged: (value) {
                  print("DropdownButtonFormField widget onChange execute");
                },
              ),
              ElevatedButton(
                  onPressed: () {
                    _formKey.currentState?.reset();
                  },
                  child: const Text("reset")),
            ],
          )),
    );
  }
}

…ieldRow widget reset function

Fix the implementation of FormField components: TextFormField, DropdownButtonFormField, and CupertinoTextFormFieldRow. Address the issue where their onChange() method does not get executed when the parent Form component calls reset().
@flutter-dashboard flutter-dashboard Bot added a: text input Entering text in a text field or keyboard related problems p: cupertino_ui cupertino_ui package in flutter/packages p: material_ui material_ui package in flutter/packages framework flutter/packages/flutter repository. See also f: labels. labels Mar 21, 2023
@flutter-dashboard

Copy link
Copy Markdown

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat (don't just cc him here, he won't see it! He's on Discord!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@google-cla

google-cla Bot commented Mar 21, 2023

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@yeleibo yeleibo changed the title change TextFormField, DropdownButtonFormField, and CupertinoTextFormF… Fix the implementation of FormField components: TextFormField, DropdownButtonFormField, and CupertinoTextFormFieldRow. Address the issue where their onChange() method does not get executed when the parent Form component calls reset() Mar 21, 2023
@yeleibo yeleibo changed the title Fix the implementation of FormField components: TextFormField, DropdownButtonFormField, and CupertinoTextFormFieldRow. Address the issue where their onChange() method does not get executed when the parent Form component calls reset() Fix FormField implementation components TextFormField, DropdownButtonFormField, CupertinoTextFormFieldRow. The problem that their onChange() method cannot be executed when the parent Form component calls the reset() method Mar 21, 2023

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! Are you able to add a test for this?

}
_cupertinoTextFormFieldRow.onChanged?.call(_effectiveController!.text);


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Too many newlines here.

Comment thread packages/flutter/lib/src/material/dropdown.dart
@yeleibo

yeleibo commented Apr 25, 2023

Copy link
Copy Markdown
Author

Thanks for the PR! Are you able to add a test for this?
Yes, tests have been added.
The Linux docs_test action check is not passing, but I'm not sure why.

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the tests! I think the docs failures have to do with macros that don't exist. I commented about that below.

/// initialize its [TextEditingController.text] with [initialValue].
final TextEditingController? controller;

/// {@macro flutter.material.TextField.onChanged}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This macro doesn't exist, did you mean flutter.material.editableText.onChanged? Or you could create this macro if you want the entire TextField.onChanged docs.

/// initialize its [TextEditingController.text] with [initialValue].
final TextEditingController? controller;

/// {@macro flutter.material.CupertinoFormRow.onChanged}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This macro also doesn't seem to exist.

testWidgets('CupertinoTextFormFieldRow onChanged to initialValue when Form reset', (WidgetTester tester) async {
final GlobalKey<FormFieldState<String>> stateKey = GlobalKey<FormFieldState<String>>();
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
String value='initialValue';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
String value='initialValue';
String value = 'initialValue';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And anywhere else you're not spacing this out needs to have spaces too.

Comment thread packages/flutter/test/cupertino/text_form_field_row_test.dart Outdated
@justinmc

justinmc commented May 8, 2023

Copy link
Copy Markdown
Contributor

Looks like there are still docs test failures, it mentions [onSubmitted].

@yeleibo

yeleibo commented May 11, 2023

Copy link
Copy Markdown
Author

Looks like there are still docs test failures, it mentions [onSubmitted].

I used {@macro flutter.widgets.editableText.onChanged} in the comment of the onChanged property of a TextFormFieldRow. However, {@template flutter.widgets.editableText.onChanged} references the onSubmitted property of its own widget (which is indicated in the comment as [onSubmitted]), while my widget does not have an onSubmitted property, so the docs failures
image

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nits here. Thanks for getting all of the checks to pass.


/// Called when the user initiates a change to the TextField's
/// value: when they have inserted or deleted text or form reset.
final ValueChanged<String>? onChanged;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Add a newline after this.


/// Called when the user initiates a change to the TextField's
/// value: when they have inserted or deleted text or form reset.
final ValueChanged<String>? onChanged;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Newline here too.

await tester.enterText(find.byType(CupertinoTextField), 'changedValue');
expect(stateKey.currentState!.value,'changedValue');
expect(value, 'changedValue');
// form to reset

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to: "Reset form."


expect(value, isNull);
expect(stateKey.currentState!.value, equals('One'));
//value chang to Two

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Change value to "Two".

await tester.pumpAndSettle();
expect(value, equals('Two'));
expect(stateKey.currentState!.value, equals('Two'));
// form to reset

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Reset form.

await tester.enterText(find.byType(TextField), 'changedValue');
expect(stateKey.currentState!.value,'changedValue');
expect(value, 'changedValue');
// form to reset

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Reset form."


expect(stateKey.currentState!.value, 'initialValue');
expect(value, 'initialValue');
//change value to 'changedValue'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Change value to "changedValue".

_effectiveController!.text = widget.initialValue!;
});
}
_cupertinoTextFormFieldRow.onChanged?.call(_effectiveController!.text);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the value didn't actually change, should onChanged still be called?

final DropdownButtonFormField<T> dropdownButtonFormField = widget as DropdownButtonFormField<T>;
assert(dropdownButtonFormField.onChanged != null);
dropdownButtonFormField.onChanged!(widget.initialValue);
super.reset();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In CupertinoTextFormFieldRow, reset calls super on the first line. Should this one also call it on the first line?

// manipulated, no setState call is needed here.
_effectiveController.text = widget.initialValue ?? '';
_textFormField.onChanged?.call(_effectiveController.text);
super.reset();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this one calls super on the last line again. Not sure which is right 🤷 .

@justinmc justinmc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nits here. Thanks for getting all of the checks to pass.

@Hixie

Hixie commented Jul 18, 2023

Copy link
Copy Markdown
Contributor

@yeleibo Thanks for the contribution! Will you be able to continue working on this? It's totally fine if not; we can take it over (though it might take longer). I'm just asking because I'm trying to clean out our review queue.

@bleroux

bleroux commented Sep 8, 2023

Copy link
Copy Markdown
Contributor

Closing this PR because I filed #134295 based on the work done here.
Thanks @yeleibo for initiating this work!

@bleroux bleroux closed this Sep 8, 2023
auto-submit Bot pushed a commit that referenced this pull request Sep 21, 2023
## Description

This PR fixes form fields in order to call the `onChange` callback when the form is reset.

This change is based on the work done in #123108.

I considered adding the `onChange` callback to the `FormField` superclass but it would break existing code because two of the three subclasses defines the `onChange` callback with `ValueChanged<String>?` type and the third one defines it with `ValueChanged<String?>?`. 

## Related Issue

Fixes #123009.

## Tests

Adds 3 tests.
Mairramer pushed a commit to Mairramer/flutter that referenced this pull request Oct 10, 2023
## Description

This PR fixes form fields in order to call the `onChange` callback when the form is reset.

This change is based on the work done in flutter#123108.

I considered adding the `onChange` callback to the `FormField` superclass but it would break existing code because two of the three subclasses defines the `onChange` callback with `ValueChanged<String>?` type and the third one defines it with `ValueChanged<String?>?`. 

## Related Issue

Fixes flutter#123009.

## Tests

Adds 3 tests.
chunhtai pushed a commit to chunhtai/packages that referenced this pull request Jun 12, 2026
## Description

This PR fixes form fields in order to call the `onChange` callback when the form is reset.

This change is based on the work done in flutter/flutter#123108.

I considered adding the `onChange` callback to the `FormField` superclass but it would break existing code because two of the three subclasses defines the `onChange` callback with `ValueChanged<String>?` type and the third one defines it with `ValueChanged<String?>?`. 

## Related Issue

Fixes flutter/flutter#123009.

## Tests

Adds 3 tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. p: cupertino_ui cupertino_ui package in flutter/packages p: material_ui material_ui package in flutter/packages

Projects

None yet

4 participants