Skip to content

Add submit implementation to Form widget#107530

Closed
casvanluijtelaar wants to merge 17 commits into
flutter:masterfrom
casvanluijtelaar:add_submit_to_form
Closed

Add submit implementation to Form widget#107530
casvanluijtelaar wants to merge 17 commits into
flutter:masterfrom
casvanluijtelaar:add_submit_to_form

Conversation

@casvanluijtelaar

@casvanluijtelaar casvanluijtelaar commented Jul 13, 2022

Copy link
Copy Markdown

This pull request adds the submit method to FormState allowing you to get all the childs FormFieldStatevalues as a Map<String, dynamic> through either Form.of(context).submit() or formKey.currentState.submit().

to achieve this a submissionKey is added to the FormField base class.

fixes #107528

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

@flutter-dashboard flutter-dashboard Bot added a: text input Entering text in a text field or keyboard related problems f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. labels Jul 13, 2022
@casvanluijtelaar casvanluijtelaar changed the title Add submit to form Add submit implementation to Form widget Jul 13, 2022
Comment thread packages/flutter/lib/src/widgets/form.dart Outdated
Comment thread packages/flutter/lib/src/widgets/form.dart Outdated
Comment thread packages/flutter/lib/src/widgets/form.dart Outdated
Comment thread packages/flutter/lib/src/widgets/form.dart Outdated
@pedromassango pedromassango requested a review from justinmc July 13, 2022 15:14
casvanluijtelaar and others added 7 commits July 13, 2022 17:15
Co-authored-by: Pedro Massango <pedromassango.developer@gmail.com>
Co-authored-by: Pedro Massango <pedromassango.developer@gmail.com>
Co-authored-by: Pedro Massango <pedromassango.developer@gmail.com>
Co-authored-by: Pedro Massango <pedromassango.developer@gmail.com>
@casvanluijtelaar

Copy link
Copy Markdown
Author

@pedromassango failed test seems unrelated to this PR

@LongCatIsLooong LongCatIsLooong 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.

Following your example in the issue (#107528), it doesn't feel it's that difficult to aggregate a bunch of TextFormFields? Developers would have to create a TextEditingController for each of them anyways so you could do

{ 
  'name': nameController.text,
  'email': emailController.text,
}

in the submit method?

Comment thread packages/flutter/lib/src/widgets/form.dart Outdated
Comment thread packages/flutter/lib/src/widgets/form.dart Outdated
Comment thread packages/flutter/lib/src/widgets/form.dart Outdated
@casvanluijtelaar

casvanluijtelaar commented Aug 17, 2022

Copy link
Copy Markdown
Author

Following your example in the issue (#107528), it doesn't feel it's that difficult to aggregate a bunch of TextFormFields? Developers would have to create a TextEditingController for each of them anyways so you could do

{ 
  'name': nameController.text,
  'email': emailController.text,
}

in the submit method?

here is a real world example that (in my opinion) hows how much better this approach scales and improves reusability. https://gist.github.com/casvanluijtelaar/a6db35fb984cb41b1c99b45d7bf570bd

@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.

@casvanluijtelaar I've modified your gist to show what you'd have to do to achieve (mostly) the same thing without this PR: https://gist.github.com/justinmc/1ed6f6a2faa3bf5dc0a6ce83119afeeb

Does that cover your use case or am I missing anything? From that it looks like the hardest part is just getting access to all of the FormFields that belong to the Form. In my example I had to keep track of all of their keys myself.

What if we just made that part easier by exposing all of the FormFieldStates or their keys on FormState? I kind of wonder why that wasn't done originally when Form was written...

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.

Can you undo these formatting changes? I think it should be the way it was if I'm not mistaken. Here and elsewhere.

@casvanluijtelaar casvanluijtelaar Aug 18, 2022

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@casvanluijtelaar I've modified your gist to show what you'd have to do to achieve (mostly) the same thing without this PR: https://gist.github.com/justinmc/1ed6f6a2faa3bf5dc0a6ce83119afeeb

Does that cover your use case or am I missing anything? From that it looks like the hardest part is just getting access to all of the FormFields that belong to the Form. In my example I had to keep track of all of their keys myself.

What if we just made that part easier by exposing all of the FormFieldStates or their keys on FormState? I kind of wonder why that wasn't done originally when Form was written...

more or less, yes. this PR tries to take advantage of the formSates ability to interact with all child Form Fields. without having to to the boilerplate of keeping track of them yourself. My original idea was to just do this as a method extension, but since the _fields aren't exposed I had to clone the form implementation and add it myself. hence the PR ;)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

and as said, your example would scale a lot harder with a more complex UI.

@casvanluijtelaar casvanluijtelaar requested review from LongCatIsLooong and removed request for pedromassango August 18, 2022 09:10
@casvanluijtelaar

Copy link
Copy Markdown
Author

failing tests seem unrelated to this change

@LongCatIsLooong

Copy link
Copy Markdown
Contributor

Could you try syncing the branch to HEAD?

@LongCatIsLooong

Copy link
Copy Markdown
Contributor

In case there're lazily built FormFields (e.g., in a lazy list) that haven't been built, calling submit would omit these fields. I think that is undesirable in a lot of cases?

Co-authored-by: Pedro Massango <pedromassango.developer@gmail.com>
@casvanluijtelaar

Copy link
Copy Markdown
Author

In case there're lazily built FormFields (e.g., in a lazy list) that haven't been built, calling submit would omit these fields. I think that is undesirable in a lot of cases?

interesting case, a formfield that hasn't been built so hasn't been interacted with doesn't matter, they are skipped anyway. when a formfield has some state it will be cached in the form. So we can still get that data. Any issue cases I am missing?

@LongCatIsLooong

Copy link
Copy Markdown
Contributor

Could you fix the error:

dartdoc:stdout: Generating docs for library widgets from package:flutter/widgets.dart...
dartdoc:stderr:   error: unresolved doc reference [FormState.submit]
dartdoc:stderr:     from widgets.FormField.submissionKey: (file:///tmp/flutter%20sdk/packages/flutter/lib/src/widgets/form.dart:375:17)
dartdoc:stderr:     in documentation inherited from widgets.FormField.submissionKey: (file:///tmp/flutter%20sdk/packages/flutter/lib/src/widgets/form.dart:375:17)

@LongCatIsLooong

Copy link
Copy Markdown
Contributor

After looking at https://gist.github.com/casvanluijtelaar/a6db35fb984cb41b1c99b45d7bf570bd .I think the API seems to be a bit limited to be used for JSON serialization:

  1. it can only be used to construct flat dictionaries, no nested structures or ordered collections. This may force the communication to use a flat, suboptimal schema.
  2. the value is of type T and there's no way to specify how T should be serialized to JSON.
  3. fields that have yet been built won't be included in the dictionary. This is often unexpected and the receiver may have to implement special handling for it.

Also

              FileField(
                submissionKey: 'file',
                hintText: 'important_file.pdf',
                initialValue: post?.file,
                validator: FeedFormValidators.fileValidator,
              ),

doesn't seem to be that much simpler than this:

             final fileFieldController = ....;
              FileField(
                controller: fieldFieldController,
                hintText: 'important_file.pdf',
                initialValue: post?.file,
                validator: FeedFormValidators.fileValidator,
              ),

, but with the controller approach you could do this:

{
  "attachments": { 
    "file" :  _fileToJSON(fieldFieldController),
  }
}

@casvanluijtelaar

Copy link
Copy Markdown
Author

good points yes, and it would be difficult to implement these ideas with this approach, maybe we should look at exposing the Form _fields so we can build our own implementations around it and make better use of Forms powerful features.

@flutter-dashboard

Copy link
Copy Markdown

This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold.

For more guidance, visit Writing a golden file test for package:flutter.

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

@justinmc

Copy link
Copy Markdown
Contributor

@casvanluijtelaar Checking in on your plans for this PR. Anything we can help with?

@casvanluijtelaar

Copy link
Copy Markdown
Author

@casvanluijtelaar Checking in on your plans for this PR. Anything we can help with?

The work in this PR is good to go. I'd like to see it merged :). But if it's decided this isn't a good match I'd like to see _fields exposed so we can implement these interfaces ourselves.

@jaumard

jaumard commented Dec 1, 2022

Copy link
Copy Markdown
Contributor

I like what I see in this PR cause I was trying to achieve the same, instead of keeping myself references to all TextEditingController and even more complex form with files and dropbox where it start to be boilerplate and repetitive if you have multiple forms.

Having a submit method would help with that. Or as suggested at least expose _fields so we can do this kind of logic on our side so we can have that logic in one place and not do it for each form we need.

@LongCatIsLooong

Copy link
Copy Markdown
Contributor

For reasons stated in #107530 (comment), I think using _fields for serialization could lead to hard-to-debug issues. Feel free to reopen this (or create a new PR) if you have new ideas to make it easier to work with Forms!.

@kwill39

kwill39 commented Jun 3, 2023

Copy link
Copy Markdown

After looking at https://gist.github.com/casvanluijtelaar/a6db35fb984cb41b1c99b45d7bf570bd .I think the API seems to be a bit limited to be used for JSON serialization:

  1. it can only be used to construct flat dictionaries, no nested structures or ordered collections. This may force the communication to use a flat, suboptimal schema.
  2. the value is of type T and there's no way to specify how T should be serialized to JSON.
  3. fields that have yet been built won't be included in the dictionary. This is often unexpected and the receiver may have to implement special handling for it.

Also

              FileField(
                submissionKey: 'file',
                hintText: 'important_file.pdf',
                initialValue: post?.file,
                validator: FeedFormValidators.fileValidator,
              ),

doesn't seem to be that much simpler than this:

             final fileFieldController = ....;
              FileField(
                controller: fieldFieldController,
                hintText: 'important_file.pdf',
                initialValue: post?.file,
                validator: FeedFormValidators.fileValidator,
              ),

, but with the controller approach you could do this:

{
  "attachments": { 
    "file" :  _fileToJSON(fieldFieldController),
  }
}

Upon reading and taking the time to consider the above points, I'm left wondering if the solution that this PR proposes is in fact suboptimal.

  1. I agree that the result would be a flat dictionary. However, I'm not convinced that this would be an issue for the developer. It would help me better understand the concern if an example scenario were provided showcasing where this would be an issue.

  2. You're absolutely correct on this point. In the author's example, they call fromJson with the result of submit. I think this would be considered a mistake on the developer's part to assume that the result of submit is serialized. A form field's value ("T") shouldn't need to be serialized by Flutter. That would be left to the developer. The submit button only gives you a dictionary of keys and values that represent the form fields. It does not guarantee the resulting dictionary contains serialized values. Creating serialized values would be the responsibility of the developer once they have the dictionary result.

  3. That's a good point. I'm curious as to how Flutter currently handles this when FormState.save() is called. I imagine that onSaved isn't called for the FormField widgets that haven't been built, seemingly leaving the developer in a similar scenario as this solution—similar, but not the same. Perhaps documentation could address this concern by stating that only the values of FormFields that have been built will be returned.

As for the use of controllers, they certainly work beautifully for many scenarios, but not all. Some FormFields are not able to use them. For example, DropdownButtonFormField doesn't have a controller since controllers are not part of the FormField api. Additionally, controllers tend to introduce a lot of boilerplate code which can feel disruptive to the developer experience when used only for the sake of accessing the value of a form field.

Truly, it would be a poor argument to state that the solution proposed by this PR is good simply because it follows the same design, for retrieving form field values, as other popular Flutter form libraries on pub.dev. However, the fact that these libraries have a fair amount of developers showing support for them does highlight a desire path created by the Flutter community for an improved form building experience, and the solution this PR proposes would indeed improve the form interface by making it significantly easier to retrieve form values. As a result, I think introducing a solution like this to the Flutter SDK is worth reconsidering so long as we can refactor it to address the concerns pointed out by @LongCatIsLooong.

@kwill39

kwill39 commented Jun 11, 2023

Copy link
Copy Markdown

Hey @LongCatIsLooong 👋, I hope all is well! I'm just checking in to see if you were able to take a look at the comment I made. I'm hoping to see if we still can work to address the concerns you raised with this PR.

@kwill39

kwill39 commented Jun 15, 2023

Copy link
Copy Markdown

@jaumard, it looks like @LongCatIsLooong might be assisting with different issues on the Flutter GitHub at the moment. Would you be willing to consider reopening this issue after reading my above comments? I'd like to see if we could still address the concerns, in this PR, that @LongCatIsLooong brought up.

@LongCatIsLooong

LongCatIsLooong commented Jun 15, 2023

Copy link
Copy Markdown
Contributor

Sorry for the late reply. Exposing _fields under a descriptive name (ideally something that tells people this property may not be a comprehensive list of fields, some fields could be omitted) sounds reasonable to me. Then you could build convenience extensions on top of the property.

@kwill39

kwill39 commented Jun 16, 2023

Copy link
Copy Markdown

Upon first glance at the _fields property, I was concerned that it might cause unexpected bugs if we exposed the underlying Set, but after taking a closer look this time, I think it might actually be fine. It doesn't look like anyone is creating a PR for #110760; so, perhaps I will.

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 f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[proposal] Provide a way to retrieve the data of all textfields within a Form widget

6 participants