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
Fix EditableText misplaces caret when selection is invalid #123777
Fix EditableText misplaces caret when selection is invalid #123777
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this! I say do it and go ahead with updating the failing tests. It looks like there are only 2 failing in the framework tests check?
I'll also run the Google tests on this.
), | ||
); | ||
|
||
// Update text with an invalid selection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment isn't exactly what you're doing, is it old?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can make this comment more self explanatory, but it was done on purpose because TextEditingController.test
setter explicitly set the selection offset to -1 (which is the root of the issue addressed in this PR):
flutter/packages/flutter/lib/src/widgets/editable_text.dart
Lines 224 to 230 in f98499f
set text(String newText) { | |
value = value.copyWith( | |
text: newText, | |
selection: const TextSelection.collapsed(offset: -1), | |
composing: TextRange.empty, | |
); | |
} |
Because TextEditingController.text
is a public API, I did not focus on trying to change its implementation as it will probably be a breaking change and also because there might be no fix that would work in all cases. What can be done at the TextEditingController.text
level is probably to enhance its documentation to discourage its use and to recommand using TextEditingController.value
instead.
While writing this, I wonder if we can add a method to TextEditingController
, with a String parameter, which will set the text using the given value AND set the selection collapsed at the end of the text?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, got it: my comment is true for the moment but it would be wrong if the PR is merged.
I updated it, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
// If this field is focused and the selection is invalid, place the cursor at | ||
// the end. Does not rely on _handleFocusChanged because it makes selection | ||
// handles visible on Android. | ||
widget.controller.selection = TextSelection.collapsed(offset: _value.text.length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems reasonable to me. There have been some problems with invalid selection. The engine often will set the selection to -1, -1
, to mean "no selection" (and elsewhere, sometimes a null
selection is used to mean no selection). If the field is focused though, it makes sense to set the cursor to the end of the field as an improvement on those kinds of bugs.
I see a lot more tests failures: 22 test failures locally |
5042b1c
to
649709d
Compare
852a339
to
10221e8
Compare
@justinmc |
// handles visible on Android. | ||
// Unregister as a listener to the text controller while making the change. | ||
widget.controller.removeListener(_didChangeTextEditingValue); | ||
widget.controller.selection = _adjustedSelectionWhenFocused()!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I find it a little strange that _adjustedSelectionWhenFocused returns a nullable value and we use !
here. Like you have to have knowledge about the if
statement above and the internals of _adjustedSelectionWhenFocused in order to use that !
.
I can't think of a better way to do it though. If nothing jumps out at you as a way to improve that, I say don't worry about it and leave it as-is.
@@ -6503,8 +6503,8 @@ void main() { | |||
variant: KeySimulatorTransitModeVariant.all() | |||
); | |||
|
|||
// Regressing test for https://github.com/flutter/flutter/issues/78219 | |||
testWidgets('Paste does not crash when the section is inValid', (WidgetTester tester) async { | |||
// Regression test for https://github.com/flutter/flutter/issues/78219 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
// Should we remove this test, because if this PR is approved it won't be possible | ||
// to have an invalid selection for the controller if it is attached to a focused | ||
// TextField. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to delete the whole test! Making a bug impossible is even better than having a test to catch it.
), | ||
); | ||
|
||
// Update text with an invalid selection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM after you delete that test
f4477f7
to
7c6b71c
Compare
e384c4b
to
923c036
Compare
CC @Renzo-Olivares Can you run these Google tests locally and see if they pass? |
Great job guys. I tested this on my test project, it is working fine. |
…23777) Fix EditableText misplaces caret when selection is invalid
…23777) Fix EditableText misplaces caret when selection is invalid
Description
EditableTextState._handleFocusChanged
adjusts the selection when a text field is focused. But if the selection becomes invalid (for instance after setting TextController.text) on an already focused field the position won’t be adjusted. The issue can be very visible whenTextField.textAlign
is set toTextAlign.center
(video using the Android emulator):120631_Before.mp4
Code sample for the repro
Implementation
AFAIK, there are several ways to fix this issue:
Not showing the caret when the selection is invalid. This is described in
RenderEditable
should not show the caret when the selection is invalid (i.e.(-1, -1)
) #79495. Unfortunately a PR for it was reverted (see [RenderEditable] Dont paint caret when selection is invalid #79607) due to a Google internal test failure.Changing the selection to a valid one when TextController selection is invalid and the field is focused. this is the solution implemented in this PR. It breaks several tests which expect that the selection stays invalid.
Adding an assert to throw a detailed error message. The impact on existing tests will probably be worth than with solution 2 because for some of these tests It might be difficult to make them complied with this new requirement (for solution 2, tests can be updated by updating some
expect
calls).Related Issue
Fixes #120631
Tests
Adds 1 test.
Will need to update several existing tests if becoming a real PR.