Skip to content

Commit 6bba08c

Browse files
authored
Feat: Add opportunity to change CupertinoTextField suffix alignment (#154601)
Fixes #152482
1 parent b05246d commit 6bba08c

File tree

2 files changed

+100
-21
lines changed

2 files changed

+100
-21
lines changed

packages/flutter/lib/src/cupertino/text_field.dart

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ class CupertinoTextField extends StatefulWidget {
239239
this.prefixMode = OverlayVisibilityMode.always,
240240
this.suffix,
241241
this.suffixMode = OverlayVisibilityMode.always,
242+
this.crossAxisAlignment = CrossAxisAlignment.center,
242243
this.clearButtonMode = OverlayVisibilityMode.never,
243244
this.clearButtonSemanticLabel,
244245
TextInputType? keyboardType,
@@ -367,6 +368,7 @@ class CupertinoTextField extends StatefulWidget {
367368
this.prefixMode = OverlayVisibilityMode.always,
368369
this.suffix,
369370
this.suffixMode = OverlayVisibilityMode.always,
371+
this.crossAxisAlignment = CrossAxisAlignment.center,
370372
this.clearButtonMode = OverlayVisibilityMode.never,
371373
this.clearButtonSemanticLabel,
372374
TextInputType? keyboardType,
@@ -516,6 +518,13 @@ class CupertinoTextField extends StatefulWidget {
516518
/// Has no effect when [suffix] is null.
517519
final OverlayVisibilityMode suffixMode;
518520

521+
/// Controls the vertical alignment of the [prefix] and the [suffix] widget in relation to content.
522+
///
523+
/// Defaults to [CrossAxisAlignment.center].
524+
///
525+
/// Has no effect when both the [prefix] and [suffix] are null.
526+
final CrossAxisAlignment crossAxisAlignment;
527+
519528
/// Show an iOS-style clear button to clear the current text entry.
520529
///
521530
/// Can be made to appear depending on various text states of the
@@ -1213,28 +1222,31 @@ class _CupertinoTextFieldState extends State<CupertinoTextField> with Restoratio
12131222
(true, true) => widget.suffix ?? _buildClearButton(),
12141223
(false, true) => _buildClearButton(),
12151224
};
1216-
return Row(children: <Widget>[
1217-
// Insert a prefix at the front if the prefix visibility mode matches
1218-
// the current text state.
1219-
if (prefixWidget != null) prefixWidget,
1220-
// In the middle part, stack the placeholder on top of the main EditableText
1221-
// if needed.
1222-
Expanded(
1223-
child: Stack(
1224-
// Ideally this should be baseline aligned. However that comes at
1225-
// the cost of the ability to compute the intrinsic dimensions of
1226-
// this widget.
1227-
// See also https://github.com/flutter/flutter/issues/13715.
1228-
alignment: AlignmentDirectional.center,
1229-
textDirection: widget.textDirection,
1230-
children: <Widget>[
1231-
if (placeholder != null) placeholder,
1232-
editableText,
1233-
],
1225+
return Row(
1226+
crossAxisAlignment: widget.crossAxisAlignment,
1227+
children: <Widget>[
1228+
// Insert a prefix at the front if the prefix visibility mode matches
1229+
// the current text state.
1230+
if (prefixWidget != null) prefixWidget,
1231+
// In the middle part, stack the placeholder on top of the main EditableText
1232+
// if needed.
1233+
Expanded(
1234+
child: Stack(
1235+
// Ideally this should be baseline aligned. However that comes at
1236+
// the cost of the ability to compute the intrinsic dimensions of
1237+
// this widget.
1238+
// See also https://github.com/flutter/flutter/issues/13715.
1239+
alignment: AlignmentDirectional.center,
1240+
textDirection: widget.textDirection,
1241+
children: <Widget>[
1242+
if (placeholder != null) placeholder,
1243+
editableText,
1244+
],
1245+
),
12341246
),
1235-
),
1236-
if (suffixWidget != null) suffixWidget
1237-
]);
1247+
if (suffixWidget != null) suffixWidget,
1248+
],
1249+
);
12381250
},
12391251
);
12401252
}

packages/flutter/test/cupertino/text_field_test.dart

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8067,6 +8067,73 @@ void main() {
80678067
);
80688068
});
80698069

8070+
testWidgets(
8071+
'CrossAxisAlignment start positions the prefix and suffix at the top of the field',
8072+
(WidgetTester tester) async {
8073+
await tester.pumpWidget(
8074+
const CupertinoApp(
8075+
home: Center(
8076+
child: CupertinoTextField(
8077+
padding: EdgeInsets.zero, // Preventing delta position.dy
8078+
prefix: Icon(CupertinoIcons.add),
8079+
suffix: Icon(CupertinoIcons.clear),
8080+
crossAxisAlignment: CrossAxisAlignment.start,
8081+
),
8082+
),
8083+
),
8084+
);
8085+
8086+
final CupertinoTextField cupertinoTextField = tester.widget<CupertinoTextField>(
8087+
find.byType(CupertinoTextField),
8088+
);
8089+
8090+
expect(find.widgetWithIcon(CupertinoTextField, CupertinoIcons.clear), findsOneWidget);
8091+
expect(find.widgetWithIcon(CupertinoTextField, CupertinoIcons.add), findsOneWidget);
8092+
expect(cupertinoTextField.crossAxisAlignment, CrossAxisAlignment.start);
8093+
8094+
final double editableDy = tester.getTopLeft(find.byType(EditableText)).dy;
8095+
final double prefixDy = tester.getTopLeft(find.byIcon(CupertinoIcons.add)).dy;
8096+
final double suffixDy = tester.getTopLeft(find.byIcon(CupertinoIcons.clear)).dy;
8097+
8098+
expect(prefixDy, editableDy);
8099+
expect(suffixDy, editableDy);
8100+
},
8101+
);
8102+
8103+
testWidgets(
8104+
'CrossAxisAlignment end positions the prefix and suffix at the bottom of the field',
8105+
(WidgetTester tester) async {
8106+
await tester.pumpWidget(
8107+
const CupertinoApp(
8108+
home: Center(
8109+
child: CupertinoTextField(
8110+
padding: EdgeInsets.zero, // Preventing delta position.dy
8111+
prefix: SizedBox.square(dimension: 48, child: Icon(CupertinoIcons.add)),
8112+
suffix: SizedBox.square(dimension: 48, child: Icon(CupertinoIcons.clear)),
8113+
crossAxisAlignment: CrossAxisAlignment.end,
8114+
),
8115+
),
8116+
),
8117+
);
8118+
8119+
final CupertinoTextField cupertinoTextField = tester.widget<CupertinoTextField>(
8120+
find.byType(CupertinoTextField),
8121+
);
8122+
8123+
expect(find.widgetWithIcon(CupertinoTextField, CupertinoIcons.clear), findsOneWidget);
8124+
expect(find.widgetWithIcon(CupertinoTextField, CupertinoIcons.add), findsOneWidget);
8125+
expect(cupertinoTextField.crossAxisAlignment, CrossAxisAlignment.end);
8126+
8127+
8128+
final double editableDy = tester.getTopLeft(find.byType(EditableText)).dy;
8129+
final double prefixDy = tester.getTopLeft(find.byIcon(CupertinoIcons.add)).dy;
8130+
final double suffixDy = tester.getTopLeft(find.byIcon(CupertinoIcons.clear)).dy;
8131+
8132+
expect(prefixDy, lessThan(editableDy));
8133+
expect(suffixDy, lessThan(editableDy));
8134+
},
8135+
);
8136+
80708137
testWidgets('text selection style 1', (WidgetTester tester) async {
80718138
final TextEditingController controller = TextEditingController(
80728139
text: 'Atwater Peel Sherbrooke Bonaventure\nhi\nwassssup!',

0 commit comments

Comments
 (0)