-
Notifications
You must be signed in to change notification settings - Fork 28.7k
Baseline-align CupertinoTextField placeholder #166952
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
@LongCatIsLooong just confirming this is the correct approach. |
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.
Nice use of a RenderObjectWidget here, this looks good. I think you might be able to make this slightly cleaner user slots, see my comment below. Otherwise LGTM.
textDirection: | ||
widget.textDirection ?? Directionality.maybeOf(context) ?? TextDirection.ltr, | ||
child: _BaselineAlignedStack( | ||
children: <Widget>[if (placeholder != null) placeholder, editableText], |
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 it is known that this widget will only ever take a maximum of two children (placeholder and editableText), then this can be implemented as "slots" rather than a list of children. See SlottedMultiChildRenderObjectWidget. Sorry for not pointing you in that direction in the first place if so!
double? getDistanceToBaseline(TextBaseline baseline, {bool onlyReal = false}) { | ||
final RenderBox? editableText = _editableTextChild; | ||
if (editableText == null) { | ||
return super.getDistanceToBaseline(baseline, onlyReal: onlyReal); | ||
} | ||
final double? editableTextBaseline = editableText.getDistanceToBaseline( | ||
baseline, | ||
onlyReal: onlyReal, | ||
); | ||
if (editableTextBaseline == null) { | ||
return super.getDistanceToBaseline(baseline, onlyReal: onlyReal); | ||
} | ||
final _BaselineAlignedStackParentData editableTextParentData = | ||
editableText.parentData! as _BaselineAlignedStackParentData; | ||
return editableTextParentData.offset.dy + editableTextBaseline; | ||
} |
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.
Is it the case that the placeholder must be smaller than the editableText? Maybe leave a comment about this somewhere if it makes sense.
I was thinking about the case where the placeholder is much larger than the editableText, so that aligning the placeholder to the editableText's baseline makes the placeholder overflow out the top. But I'm assuming that's not possible.
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, that was also confusing for me. I saw 3 options:
- EditableText baseline
- Placeholder baseline
- The max of 1) and 2)
The current implementation is 1), but I honestly do not know which to go with, I need further guidance here. I can document this in the docs for CupertinoTextField.placeholder and/or CupertinoTextField.placeholderStyle.
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.
Ok I say keep it 1 actually. Looking at your screenshots up in the PR description, I feel like the EditableText baseline is supposed to be "the" baseline for the field. If someone makes the placeholder so big that it overflows, then that's their own fault.
We can always reconsider if someone opens an issue with a good argument.
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 👍
Looks good with slots, thanks for changing that.
: 0; | ||
|
||
final double offsetYAdjustment = math.max(0, currentPlaceholderY); | ||
editableTextParentData.offset = Offset(0, offsetYAdjustment); |
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 and below: what if the text is right aligned?
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.
Does this mean TextAlign.right
or TextDirection.rtl
? Not entirely sure but it seems to work well with both:
TextAlign.right
text.align.right.mov
TextDirection.rtl
text.direction.rtl.mov
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.
TextAlign.right. I suspect it looks right in your test app because the text field was given a tight width constraint.
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 the sample app:
Sample code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class TextFieldApp extends StatelessWidget {
const TextFieldApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController commentController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: CupertinoTextField(
textAlign: TextAlign.right,
minLines: 5,
maxLines: 8,
textAlignVertical: TextAlignVertical.bottom,
controller: commentController,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.done,
prefix: Icon(CupertinoIcons.add_circled),
placeholder: "Placeholder",
),
),
),
),
);
}
}
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.
Ah right the Expanded
uses FlexFit.tight
by default, which means this RenderBox always get a tight width constraint.
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.
Hmm so this implementation only works if the incoming width constraint is tight. Can you add an assert for that?
final double currentPlaceholderY = | ||
editableTextBaselineValue != null && placeholderBaselineValue != null | ||
? editableTextBaselineValue - placeholderBaselineValue | ||
: 0; |
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 the placeholder doesn't have a baseline (can this happen?), shouldn't it be center aligned?
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 don't think there can be a non-null placeholder with a null baseline (I think because CupertinoTextField.placeholder is a string). Maybe I can add an assert for that.
if (editableText != null) { | ||
final Size editableTextSize = layoutChild(editableText, constraints); | ||
width = math.max(width, editableTextSize.width); | ||
height = math.max(height, editableTextSize.height); |
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 not right. The text is baseline aligned so you can't simply take the max height.
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.
Changed the calculations... I'm not entirely sure if it's correct.
: 0; | ||
|
||
final double offsetYAdjustment = math.max(0, currentPlaceholderY); | ||
editableTextParentData.offset = Offset(0, offsetYAdjustment); |
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.
TextAlign.right. I suspect it looks right in your test app because the text field was given a tight width constraint.
required BoxConstraints constraints, | ||
required Size editableTextSize, | ||
required Size? placeholderSize, | ||
required double editableTextBaseline, |
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: instead of passing in the sizes and baseline, you can pass in a layoutChild / getBaseline callback like other RenderBoxes do. Search for ChildLayouter
for examples.
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 did this before, but in performLayout
the child has to be laid out before getting the baseline. But performLayout
calls getDistanceToBaseline
vs. computeDryLayout
which calls getDryBaseline
instead. So we would need a way to tell _computeSize
to either compute the baseline or the dry baseline.
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.
Yeah you can pass in that as a callback too. If you search for ChildLayouter
I think there're examples where a RenderBox's layout algorithm also depends on the baseline location.
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 nits.
required BoxConstraints constraints, | ||
required Size editableTextSize, | ||
required Size? placeholderSize, | ||
required double editableTextBaseline, |
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.
Yeah you can pass in that as a callback too. If you search for ChildLayouter
I think there're examples where a RenderBox's layout algorithm also depends on the baseline location.
: 0; | ||
|
||
final double offsetYAdjustment = math.max(0, currentPlaceholderY); | ||
editableTextParentData.offset = Offset(0, offsetYAdjustment); |
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.
Ah right the Expanded
uses FlexFit.tight
by default, which means this RenderBox always get a tight width constraint.
: 0; | ||
|
||
final double offsetYAdjustment = math.max(0, currentPlaceholderY); | ||
editableTextParentData.offset = Offset(0, offsetYAdjustment); |
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.
Hmm so this implementation only works if the incoming width constraint is tight. Can you add an assert for that?
flutter/flutter@85564cb...60050a0 2025-05-24 [email protected] Roll Skia from 0834eea9de33 to 91dc88dc70e5 (1 revision) (flutter/flutter#169414) 2025-05-24 [email protected] Roll Dart SDK from 0a6b16a55b9e to 7dab9bffe1f7 (1 revision) (flutter/flutter#169406) 2025-05-24 [email protected] Correct calculation for CupertinoTextSelectionToolbar vertical position (flutter/flutter#169308) 2025-05-24 [email protected] Baseline-align CupertinoTextField placeholder (flutter/flutter#166952) 2025-05-24 [email protected] Roll Dart SDK from 8354914ef97b to 0a6b16a55b9e (3 revisions) (flutter/flutter#169403) 2025-05-24 [email protected] Start removing Observatory support and references (flutter/flutter#169216) 2025-05-23 [email protected] Roll Skia from f42bb59753fe to 0834eea9de33 (3 revisions) (flutter/flutter#169393) 2025-05-23 [email protected] Roll Skia from 956fd8b14e22 to f42bb59753fe (2 revisions) (flutter/flutter#169379) 2025-05-23 [email protected] [Engine] Fast blurring algorithm for RSuperellipse (flutter/flutter#169187) 2025-05-23 [email protected] Add a page describing best CI practices for `flutter`-org repos (flutter/flutter#169364) 2025-05-23 [email protected] Revert "Mark web_tool_tests_1_2 as bringup." (flutter/flutter#169361) 2025-05-23 [email protected] Add changelog section for 3.32.0 and 3.32.1, and add note for ndk checking cherry pick (flutter/flutter#169369) 2025-05-23 [email protected] Roll Dart SDK from 085f110ecf33 to 8354914ef97b (1 revision) (flutter/flutter#169349) 2025-05-23 [email protected] Remove handling of the legacy `.flutter-plugins` file in `PluginHandler.kt`. (flutter/flutter#169317) 2025-05-23 [email protected] Use baseUri of WebAssetServer for reload_scripts.json (flutter/flutter#169267) 2025-05-23 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Use pub workspace (#168662)" (flutter/flutter#169357) 2025-05-23 [email protected] Use pub workspace (flutter/flutter#168662) 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 To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
| Before | After | | --- | --- | | <img width="348" alt="non baseline aligned placeholder" src="https://github.com/user-attachments/assets/62f779cc-1d16-41f3-92f8-a2de16a04895" /> | <img width="348" alt="baseline aligned placeholder" src="https://github.com/user-attachments/assets/e43d200f-ef47-4d10-a74a-a2c6998b44f5" /> | Fixes [Can't align placeholder of CupertinoTextField with min lines to top](flutter#138794)
… fidelity updates (#169708) This PR does the following: ## Show large title mid-transition if automaticallyImplyLeading is false (see #169708 (comment)) ## Correct color for the CupertinoSearchTextField placeholder (see #163020 (comment)) | Dark mode | Light mode | | --- | --- | | <img width="381" alt="placeholder color dark mode" src="https://github.com/user-attachments/assets/e37e23fa-9f4f-495e-8f02-b9c38a4faffb" /> | <img width="381" alt="placeholder color light mode" src="https://github.com/user-attachments/assets/16e24a61-2528-44e0-9afa-8431487cf5ff" /> | And also: - Removes flaky mid-transition goldens - Fixes a CupertinoTextField crash caused by #166952 where size > constraints Fixes [Improve fidelity of CupertinoSliverNavigationBar.search and CupertinoSearchTextField](#163020)
* master: (125 commits) Roll Fuchsia Linux SDK from XtPp9bBW49iDJ0wbA... to -eo2JqnJBauuGSzoW... (flutter#169424) Roll Skia from 91dc88dc70e5 to 443f5257f382 (1 revision) (flutter#169422) Roll Skia from 0834eea9de33 to 91dc88dc70e5 (1 revision) (flutter#169414) Roll Dart SDK from 0a6b16a55b9e to 7dab9bffe1f7 (1 revision) (flutter#169406) Correct calculation for CupertinoTextSelectionToolbar vertical position (flutter#169308) Baseline-align CupertinoTextField placeholder (flutter#166952) Roll Dart SDK from 8354914ef97b to 0a6b16a55b9e (3 revisions) (flutter#169403) Start removing Observatory support and references (flutter#169216) Roll Skia from f42bb59753fe to 0834eea9de33 (3 revisions) (flutter#169393) Roll Skia from 956fd8b14e22 to f42bb59753fe (2 revisions) (flutter#169379) [Engine] Fast blurring algorithm for RSuperellipse (flutter#169187) Add a page describing best CI practices for `flutter`-org repos (flutter#169364) Revert "Mark web_tool_tests_1_2 as bringup." (flutter#169361) Add changelog section for 3.32.0 and 3.32.1, and add note for ndk checking cherry pick (flutter#169369) Roll Dart SDK from 085f110ecf33 to 8354914ef97b (1 revision) (flutter#169349) Remove handling of the legacy `.flutter-plugins` file in `PluginHandler.kt`. (flutter#169317) Use baseUri of WebAssetServer for reload_scripts.json (flutter#169267) Reverts "Use pub workspace (flutter#168662)" (flutter#169357) Use pub workspace (flutter#168662) Reverts "[Reland2] Implements UISceneDelegate dynamically w/ FlutterLaunchEngine (flutter#169276)" (flutter#169347) ...
…r#9318) flutter/flutter@85564cb...60050a0 2025-05-24 [email protected] Roll Skia from 0834eea9de33 to 91dc88dc70e5 (1 revision) (flutter/flutter#169414) 2025-05-24 [email protected] Roll Dart SDK from 0a6b16a55b9e to 7dab9bffe1f7 (1 revision) (flutter/flutter#169406) 2025-05-24 [email protected] Correct calculation for CupertinoTextSelectionToolbar vertical position (flutter/flutter#169308) 2025-05-24 [email protected] Baseline-align CupertinoTextField placeholder (flutter/flutter#166952) 2025-05-24 [email protected] Roll Dart SDK from 8354914ef97b to 0a6b16a55b9e (3 revisions) (flutter/flutter#169403) 2025-05-24 [email protected] Start removing Observatory support and references (flutter/flutter#169216) 2025-05-23 [email protected] Roll Skia from f42bb59753fe to 0834eea9de33 (3 revisions) (flutter/flutter#169393) 2025-05-23 [email protected] Roll Skia from 956fd8b14e22 to f42bb59753fe (2 revisions) (flutter/flutter#169379) 2025-05-23 [email protected] [Engine] Fast blurring algorithm for RSuperellipse (flutter/flutter#169187) 2025-05-23 [email protected] Add a page describing best CI practices for `flutter`-org repos (flutter/flutter#169364) 2025-05-23 [email protected] Revert "Mark web_tool_tests_1_2 as bringup." (flutter/flutter#169361) 2025-05-23 [email protected] Add changelog section for 3.32.0 and 3.32.1, and add note for ndk checking cherry pick (flutter/flutter#169369) 2025-05-23 [email protected] Roll Dart SDK from 085f110ecf33 to 8354914ef97b (1 revision) (flutter/flutter#169349) 2025-05-23 [email protected] Remove handling of the legacy `.flutter-plugins` file in `PluginHandler.kt`. (flutter/flutter#169317) 2025-05-23 [email protected] Use baseUri of WebAssetServer for reload_scripts.json (flutter/flutter#169267) 2025-05-23 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Use pub workspace (#168662)" (flutter/flutter#169357) 2025-05-23 [email protected] Use pub workspace (flutter/flutter#168662) 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 To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Fixes Can't align placeholder of CupertinoTextField with min lines to top