Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fixes initial validation with AutovalidateMode.always on first build #156708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

Mairramer
Copy link
Contributor

@Mairramer Mairramer commented Oct 14, 2024

Fixes #142701
This PR fixes an issue where on the first build AutovalidateMode.always was not called.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@github-actions github-actions bot added the framework flutter/packages/flutter repository. See also f: labels. label Oct 14, 2024
Copy link
Contributor

@nate-thegrate nate-thegrate left a comment

Choose a reason for hiding this comment

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

Thanks for making this fix!

@github-actions github-actions bot added the f: material design flutter/packages/flutter/material repository. label Oct 16, 2024
Copy link
Contributor

@nate-thegrate nate-thegrate left a comment

Choose a reason for hiding this comment

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

I'm a huge fan of this change—I feel that the current fix is an effective solution for #142701 and is probably better than a fix that involves using a post-frame callback.

As far as how to approach a breaking change that gives an overall improvement, there are several different paths we could take, so it'd be good to get another reviewer's input here (which might take up to 2 weeks, so thanks in advance for your patience).

setState(() {
hourHasError.value = newHour == null;
});
hourHasError.value = newHour == null;
Copy link
Contributor

@nate-thegrate nate-thegrate Oct 16, 2024

Choose a reason for hiding this comment

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

This looks like a clear improvement: FormState.validate guarantees a rebuild, so I don't believe this setState is necessary.


However: I'm a bit worried about the implications here.

There might be a bunch of Flutter projects outside of this codebase that build a Form with a similar structure; sadly, migrating the code here isn't going to help them out.

(I personally feel that many different aspects of the Form widget could be improved, but they'd require significant time investment and would definitely qualify as "breaking changes". I've also found that it's not very hard to just ignore Form entirely by using a state management package like provider or riverpod.)


Assuming we go through with this change: much like how theme data has useMaterial3, it'd be nice if Form had something that allowed for a migration period.

Copy link
Contributor

Choose a reason for hiding this comment

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

I've been eyeing Form for a rewrite for a long time. I wish I could just write a whole new class with a new name, but the name "Form" is so good I think we'd have to do something like userMaterial3.

Copy link
Contributor

@nate-thegrate nate-thegrate left a comment

Choose a reason for hiding this comment

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

Thanks very much for the updates!

In my opinion, this change is worth it regardless of whether it qualifies as "breaking", but hopefully we can do it in a non-breaking way :)

Copy link
Contributor

@justinmc justinmc left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! Still some questions but I think we should definitely fix this bug.

setState(() {
hourHasError.value = newHour == null;
});
hourHasError.value = newHour == null;
Copy link
Contributor

Choose a reason for hiding this comment

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

I've been eyeing Form for a rewrite for a long time. I wish I could just write a whole new class with a new name, but the name "Form" is so good I think we'd have to do something like userMaterial3.

@nate-thegrate
Copy link
Contributor

Unfortunately, my disliking toward certain aspects of the Form API is giving me some "analysis paralysis" as I consider how we might proceed with this PR.

@Mairramer thanks again for all the improvements you've made here! I'll probably be ready to re-engage sometime later this week.

@Mairramer
Copy link
Contributor Author

Mairramer commented Oct 21, 2024

Unfortunately, my disliking toward certain aspects of the Form API is giving me some "analysis paralysis" as I consider how we might proceed with this PR.

@Mairramer thanks again for all the improvements you've made here! I'll probably be ready to re-engage sometime later this week.

I understand you. Over the weekend I was analyzing several parts of the Form code that do not seem to have the quality standard that we expect from Flutter.

Copy link
Contributor

@nate-thegrate nate-thegrate left a comment

Choose a reason for hiding this comment

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

Thanks for your patience! I'm looking forward to getting this fix merged.


I was analyzing several parts of the Form code that do not seem to have the quality standard that we expect from Flutter.

Amen to that. The widget wasn't poorly designed, but this file was created 8 years ago, and much has changed since then.

Copy link
Contributor

@nate-thegrate nate-thegrate left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for the fix!

@nate-thegrate

This comment was marked as resolved.

engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 21, 2024
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Nov 21, 2024
Roll Flutter from 8536b96 to 93d772c (37 revisions)

flutter/flutter@8536b96...93d772c

2024-11-21 [email protected] Added additional logging to `_listCoreDevices` (flutter/flutter#159275)
2024-11-21 [email protected] Roll Flutter Engine from 78b87f3fe023 to d1a08064e193 (1 revision) (flutter/flutter#159280)
2024-11-21 [email protected] Shut down DevTools and DDS processes if flutter_tools is killed by a signal (flutter/flutter#159238)
2024-11-21 [email protected] Remove `RepaintBoundary` that is no longer needed. (flutter/flutter#159232)
2024-11-21 [email protected] Try a speculative fix for Gradle OOMs. (flutter/flutter#159234)
2024-11-21 [email protected] On-device Widget Inspector button exits widget selection (flutter/flutter#158219)
2024-11-21 [email protected] Roll Flutter Engine from 523d381893c8 to 78b87f3fe023 (2 revisions) (flutter/flutter#159270)
2024-11-21 [email protected] Remove `firebase_abstract_method_smoke_test` (flutter/flutter#159145)
2024-11-21 [email protected] Roll Packages from e95f6d8 to 913b99e (7 revisions) (flutter/flutter#159268)
2024-11-21 [email protected] Roll Flutter Engine from 69c325513a65 to 523d381893c8 (3 revisions) (flutter/flutter#159263)
2024-11-21 [email protected] [flutter_tools] opt iOS/macOS apps out of Metal API validation via migrator, update templates in repo. (flutter/flutter#159228)
2024-11-21 [email protected] Scribe Android handwriting text input (flutter/flutter#148784)
2024-11-21 [email protected] Terminate non-detached test devices on `flutter run` completion (flutter/flutter#159170)
2024-11-21 [email protected] Un-skip tests that use `flutter build apk`. (flutter/flutter#159231)
2024-11-20 [email protected] Roll Flutter Engine from 2d32cf3a7971 to 69c325513a65 (1 revision) (flutter/flutter#159229)
2024-11-20 [email protected] Roll Flutter Engine from 3828681d1f86 to 2d32cf3a7971 (3 revisions) (flutter/flutter#159226)
2024-11-20 [email protected] Roll Flutter Engine from 3245c8976976 to 3828681d1f86 (1 revision) (flutter/flutter#159217)
2024-11-20 [email protected] Add `--dry-run` to `dev/bots/test.dart`. (flutter/flutter#158956)
2024-11-20 [email protected] Add docs for setting up Android Studio to auto format Kotlin code (flutter/flutter#159209)
2024-11-20 [email protected] Roll Flutter Engine from 80d77505fdde to 3245c8976976 (1 revision) (flutter/flutter#159214)
2024-11-20 [email protected] Fix: The enableFeedback property of InkWell cannot be set to a nullabâ�¦ (flutter/flutter#158907)
2024-11-20 [email protected] Roll Flutter Engine from 3f19207e820e to 80d77505fdde (1 revision) (flutter/flutter#159210)
2024-11-20 [email protected] Roll Packages from fc4adc7 to e95f6d8 (6 revisions) (flutter/flutter#159201)
2024-11-20 [email protected] Remove dependency on [Target] and instead operate on [Architecture] (flutter/flutter#159196)
2024-11-20 [email protected] Fix git command in Quality-Assurance.md (flutter/flutter#155146)
2024-11-20 [email protected] Roll Flutter Engine from 7eb87547cbc6 to 3f19207e820e (4 revisions) (flutter/flutter#159176)
2024-11-20 [email protected] Make `runner` non-nullable as it always is. (flutter/flutter#159156)
2024-11-19 [email protected] Update Material 3 `CircularProgressIndicator` for new visual style (flutter/flutter#158104)
2024-11-19 [email protected] Roll Flutter Engine from 4ff696b555dc to 7eb87547cbc6 (3 revisions) (flutter/flutter#159168)
2024-11-19 [email protected] Add platform-android label for all flutter_tools *android* files (flutter/flutter#159166)
2024-11-19 [email protected] Roll Flutter Engine from d5820a638885 to 4ff696b555dc (1 revision) (flutter/flutter#159164)
2024-11-19 [email protected] Reland Add UI Benchmarks (flutter/flutter#153368)
2024-11-19 [email protected] fix lint usage of `task` inside `resolve_dependecies.gradle` file (flutter/flutter#158022)
2024-11-19 [email protected] Roll Flutter Engine from cff1e751f853 to d5820a638885 (5 revisions) (flutter/flutter#159155)
2024-11-19 [email protected] Removing redundant backticks in `flutter\packages\flutter_tools\gradle\gradle.kts` (flutter/flutter#159051)
2024-11-19 [email protected] Fixes initial validation with AutovalidateMode.always on first build (flutter/flutter#156708)
2024-11-19 [email protected] Introduce new Material 3 `Slider` shapes (flutter/flutter#152237)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

...
@stan-at-work
Copy link

stan-at-work commented Nov 26, 2024

Does this issue, fixes this bug currently present in v3.24.5.

The form for some reason triggers an setState when the underlying widgets are beeing build (for the first time i think).

It happens because the FormFieldState.didChange is beeing called from a TextEditingController.text= call.
The TextEditingController.text= is beeing triggered from the build() method of CoreDropdown<CoreFieldType>
but i don't think that's a problem. (calling ctrl.text = in the build method)


flutter: ══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
flutter: The following assertion was thrown while dispatching notifications for TextEditingController:
flutter: setState() or markNeedsBuild() called during build.
flutter: This Form widget cannot be marked as needing to build because the framework is already in the
flutter: process of building widgets. A widget can be marked as needing to be built during the build phase
flutter: only if one of its ancestors is currently building. This exception is allowed because the framework
flutter: builds parent widgets before children, which means a dirty descendant will always be built.
flutter: Otherwise, the framework might not visit this widget during this build phase.
flutter: The widget on which setState() or markNeedsBuild() was called was:
flutter:   Form-[LabeledGlobalKey<FormState>#9ba50]
flutter: The widget which was currently being built when the offending call was made was:
flutter:   CoreDropdown<CoreFieldType>
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0      Element.markNeedsBuild.<anonymous closure>
flutter: #1      Element.markNeedsBuild
flutter: #2      State.setState
flutter: #3      FormState._forceRebuild
flutter: #4      FormState._fieldDidChange
flutter: #5      FormFieldState.didChange
flutter: #6      _TextFormFieldState.didChange
flutter: #7      _TextFormFieldState._handleControllerChanged
flutter: #8      ChangeNotifier.notifyListeners
flutter: #9      ValueNotifier.value=
flutter: #10     TextEditingController.value=
flutter: #11     TextEditingController.text=
flutter: #12     CoreDropdownState.build
flutter: #13     StatefulElement.build
flutter: #14     ComponentElement.performRebuild
flutter: #15     StatefulElement.performRebuild
flutter: #16     Element.rebuild
flutter: #17     StatefulElement.update
flutter: #18     Element.updateChild
flutter: #19     ComponentElement.performRebuild
flutter: #20     Element.rebuild
flutter: #21     ProxyElement.update
flutter: #22     Element.updateChild
flutter: #23     Element.updateChildren
flutter: #24     MultiChildRenderObjectElement.update
flutter: #25     Element.updateChild
flutter: #26     ComponentElement.performRebuild
flutter: #27     Element.rebuild
flutter: #28     StatelessElement.update
flutter: #29     Element.updateChild
flutter: #30     ComponentElement.performRebuild
flutter: #31     StatefulElement.performRebuild
flutter: #32     Element.rebuild
flutter: #33     StatefulElement.update
flutter: #34     Element.updateChild
flutter: #35     ComponentElement.performRebuild
flutter: #36     StatefulElement.performRebuild
flutter: #37     Element.rebuild
flutter: #38     StatefulElement.update
flutter: #39     Element.updateChild
flutter: #40     ComponentElement.performRebuild
flutter: #41     StatefulElement.performRebuild
flutter: #42     Element.rebuild
flutter: #43     StatefulElement.update
flutter: #44     Element.updateChild
flutter: #45     ComponentElement.performRebuild
flutter: #46     StatefulElement.performRebuild
flutter: #47     Element.rebuild
flutter: #48     StatefulElement.update
flutter: #49     Element.updateChild
flutter: #50     ComponentElement.performRebuild
flutter: #51     StatefulElement.performRebuild
flutter: #52     Element.rebuild
flutter: #53     StatefulElement.update
flutter: #54     Element.updateChild
flutter: #55     ComponentElement.performRebuild
flutter: #56     StatefulElement.performRebuild
flutter: #57     Element.rebuild
flutter: #58     BuildScope._tryRebuild
flutter: #59     BuildScope._flushDirtyElements
flutter: #60     BuildOwner.buildScope
flutter: #61     WidgetsBinding.drawFrame
flutter: #62     RendererBinding._handlePersistentFrameCallback
flutter: #63     SchedulerBinding._invokeFrameCallback
flutter: #64     SchedulerBinding.handleDrawFrame
flutter: #65     SchedulerBinding._handleDrawFrame
flutter: #69     _invoke (dart:ui/hooks.dart:314:10)
flutter: #70     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:419:5)
flutter: #71     _drawFrame (dart:ui/hooks.dart:283:31)
flutter: (elided 3 frames from dart:async)
flutter:
flutter: The TextEditingController sending notification was:
flutter:   TextEditingController#d8dc9(TextEditingValue(text: ┤Lead├, selection: TextSelection.invalid,
flutter:   composing: TextRange(start: -1, end: -1)))
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════
flutter: Another exception was thrown: setState() or markNeedsBuild() called during build.

@nate-thegrate
Copy link
Contributor

@stan-at-work unfortunately I don't think so—this PR did have to deal with the same problem, but it was with regards to initializing the widget, not with didUpdateWidget calls.

@Mairramer
Copy link
Contributor Author

@nate-thegrate @stan-at-work
Today, I spent some time experimenting with the Form and tried refactoring the code. I explored two approaches: one using ValueNotifier with a Mixin and another using only a Mixin. In both implementations, I managed to perform validation during the first build without relying on WidgetsBinding.instance.addPostFrameCallback. It’s still not entirely clear to me, but it seems that the RestorationMixin is responsible for many of these issues, since it extends the State itself.

@nate-thegrate Do you think this could be related or is it just a coincidence?

@stan-at-work
Copy link

@nate-thegrate @stan-at-work

Today, I spent some time experimenting with the Form and tried refactoring the code. I explored two approaches: one using ValueNotifier with a Mixin and another using only a Mixin. In both implementations, I managed to perform validation during the first build without relying on WidgetsBinding.instance.addPostFrameCallback. It’s still not entirely clear to me, but it seems that the RestorationMixin is responsible for many of these issues, since it extends the State itself.

@nate-thegrate Do you think this could be related or is it just a coincidence?

Would you be able to share what you made ?

@Mairramer
Copy link
Contributor Author

Mairramer commented Nov 26, 2024

Here are some changes:
https://gist.github.com/Mairramer/0ccf3eb24750c9b1a9802d7efb1134f1
I recovered from vscode history, it may be wrong in some parts.

There's a lot of stuff in there that's just for testing purposes, as I'm trying to find these blind spots that cause errors in other widgets.

@stan-at-work
Copy link

Here are some changes: https://gist.github.com/Mairramer/0ccf3eb24750c9b1a9802d7efb1134f1 I recovered from vscode history, it may be wrong in some parts.

There's a lot of stuff in there that's just for testing purposes, as I'm trying to find these blind spots that cause errors in other widgets.

Thanks

@reidbaker reidbaker mentioned this pull request Dec 13, 2024
11 tasks
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 12, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 6, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.

AutovalidateMode.always on Form not working as expected
4 participants