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
CupertinoTextField vertical alignment #34723
CupertinoTextField vertical alignment #34723
Conversation
1b0211e
to
d21f322
Compare
24826bc
to
bc17df9
Compare
bc17df9
to
376bdd6
Compare
599c480
to
a5f79b4
Compare
/// * [TextField.textAlignVertical], which is passed on to the [InputDecorator]. | ||
/// * [InputDecorator.textAlignVertical], which defines the alignment of | ||
/// prefix, input, and suffix, within the [InputDecorator]. | ||
class TextAlignVertical { |
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 just moved this to alignment.dart so that it's accessible by both Cupertino and Material.
@@ -898,7 +914,26 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK | |||
ignoring: !enabled, | |||
child: Container( | |||
decoration: effectiveDecoration, | |||
child: _addTextDependentAttachments(paddedEditable, textStyle, placeholderStyle), | |||
child: TextSelectionGestureDetector( |
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.
The Align widget is what does the vertical alignment. I had to move the TextSelectionGestureDetector here in order to get the entire input to be interactive in all cases like it should be.
// Because TextSelectionGestureDetector listens to taps that happen on | ||
// widgets in front of it, tapping the clear button will also trigger this | ||
// handler here. If this is the case, return. | ||
if (_clearing) { |
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 is hacky. Is there any other way to prevent taps on the clear button from triggering the TextSelectionGestureDetector? I'm back to the confusing gesture arena problem of multiple widgets getting the same event.
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.
Ideally we want to eliminate gestures that start (with a down event) in either the text field's prefix or the clear button suffix. The TextSelectionGestureDetector would have to ignore gestures that start within the corresponding render boxes, per hitTest.
This probably isn't academic. One could use a Slider as prefix for example.
6a17d5f
to
94c8ddd
Compare
// Tapping anywhere inside focuses it. | ||
expect(focusNode.hasFocus, false); | ||
await tester.tapAt(tester.getTopLeft(find.byType(CupertinoTextField))); | ||
await tester.pumpAndSettle(const Duration(milliseconds: 400)); // timer error if no duration |
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.
If I didn't add this Duration, then I got failures due to "A Timer is still pending even after the widget tree was disposed." Any other way to fix that? Am I doing something weird here?
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 might be the 300ms doubleTap timer from packages/flutter/lib/src/widgets/text_selection.dart. Pump and settle only waits for 100ms, so the timer may survive to the end of the test. Assuming that's correct, you could explicitly add a second 300 ms pump (after a no-args pumpAndSettle) to ensure that the double tap timer has expired.
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.
That seems to be it, thanks for the tip.
94c8ddd
to
47c87a4
Compare
…for interactivity
47c87a4
to
b8c6750
Compare
1af3afa
to
082bd29
Compare
Changed so that only the clear button is opaque to tap events. See comments below. |
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 with some feedback about the details.
void _handleSingleTapUp(TapUpDetails details) { | ||
// Because TextSelectionGestureDetector listens to taps that happen on | ||
// widgets in front of it, tapping the prefix or suffix will also trigger | ||
// this handler here. If this is the case, ignore the tap. |
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 handler here => this handler.
If this is the case ... => If the the prefix or suffix widget recognizes the up event, then prevent the text field from seeing it.
if (widget.textAlignVertical != null) { | ||
return widget.textAlignVertical; | ||
} | ||
return _hasDecoration ? TextAlignVertical.center : TextAlignVertical.top; |
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.
A comment about backwards compatibility might be helpful here
@@ -742,7 +784,10 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with AutomaticK | |||
// Insert a prefix at the front if the prefix visibility mode matches | |||
// the current text state. | |||
if (_showPrefixWidget(text)) { | |||
rowChildren.add(widget.prefix); | |||
rowChildren.add(Container( |
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.
KeyedSubtree instead of Container? Here and below.
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.
We talked and decided to remove this behavior on the prefix and suffix, so for now only the clear button will be opaque to tap events, and I'll get rid of these containers.
@@ -633,3 +633,44 @@ class _MixedAlignment extends AlignmentGeometry { | |||
return null; | |||
} | |||
} | |||
|
|||
/// The vertical alignment of text within an input. |
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.
input => input field or input box? Here and below.
/// The vertical alignment of text within an input. | ||
/// | ||
/// A single [y] value that can range from -1.0 to 1.0. -1.0 aligns to the top | ||
/// of the input so that the top of the first line of text fits within the input |
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.
If you used "input box" then:
of the input box, so that the top of the first line of text fits within the box
etc
assert(y >= -1.0 && y <= 1.0); | ||
|
||
/// A value ranging from -1.0 to 1.0 that defines the topmost and bottommost | ||
/// locations of the top and bottom of the input text box. |
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.
"the input text box", NICE. Maybe: a text input box (here and elsewhere).
/// locations of the top and bottom of the input text box. | ||
final double y; | ||
|
||
/// Aligns a TextField's input Text with the topmost location within the |
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.
within the => within a
TextField => TextField's input box.
@@ -650,6 +651,12 @@ void main() { | |||
+ tester.getSize(find.byIcon(CupertinoIcons.add)).width | |||
+ 6.0, | |||
); | |||
|
|||
// Tapping on the prefix isn't received by the CupertinoTextField. |
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.
Tapping on the prefix doesn't cause the CupertinoTextField to take the keyboard focus.
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.
Removing this test after the functionality was removed.
@@ -3087,6 +3102,9 @@ void main() { | |||
), | |||
), | |||
); | |||
|
|||
tester.binding.window.physicalSizeTestValue = null; |
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.
Were tests failing because we hadn't reset these values?
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.
Yes! It took me awhile to track it down to this.
// Tapping anywhere inside focuses it. | ||
expect(focusNode.hasFocus, false); | ||
await tester.tapAt(tester.getTopLeft(find.byType(CupertinoTextField))); | ||
await tester.pumpAndSettle(const Duration(milliseconds: 400)); // timer error if no duration |
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 might be the 300ms doubleTap timer from packages/flutter/lib/src/widgets/text_selection.dart. Pump and settle only waits for 100ms, so the timer may survive to the end of the test. Assuming that's correct, you could explicitly add a second 300 ms pump (after a no-args pumpAndSettle) to ensure that the double tap timer has expired.
CupertinoTextField now supports vertical alignment via the textAlignVertical parameter.
* commit 'a0c47e2216a2b50b70c478001cf5652f31a783af': (187 commits) Do not use ideographic baseline for RenderPargraph baseline (flutter#35493) Add type to StreamChannel in generated test code. (flutter#35367) Fix RenderFittedBox when child.size.isEmpty (flutter#35487) add APK build time benchmarks (flutter#35481) fix Selection handles position is off (flutter#34665) Move usage flutter create tests into memory filesystem. (flutter#35160) Include tags in SemanticsNode debug properties (flutter#35491) Re-apply 'Add currentSystemFrameTimeStamp to SchedulerBinding' (flutter#35492) CupertinoTextField vertical alignment (flutter#34723) Mark update-packages as non-experimental (flutter#35467) fix default artifacts to exclude ios and android (flutter#35303) Roll engine ffba2f6..7d3e722 (59 commits) (flutter#35489) Update macrobenchmarks README and app name (flutter#35477) mark windows and macos chrome dev mode as flaky (flutter#35495) Use the new service protocol message names (flutter#35482) Manual roll of engine 45b66b7...ffba2f6 (flutter#35464) more ui-as-code (flutter#35393) update reassemble doc (flutter#35164) New parameter for RawGestureDetector to customize semantics mapping (flutter#33936) Add --target support for Windows and Linux (flutter#34660) ...
CupertinoTextField now supports vertical alignment via the textAlignVertical parameter.
Description
This PR adds the
textAlignVertical
parameter to CupertinoTextField. It works the same as TextField with the slight differences that 1. including any prefix or suffix causes alignment to default to the center, and 2. the text centers itself within the vertical space of the prefix/suffix. Both of these are existing behaviors that have been preserved by this PR.Related
See #34355, which added
textAlignVertical
for TextField.Closes #33785