Skip to content

Commit 29810cd

Browse files
authored
fix: Update time picker dialog input size (flutter#163184)
fix: Update time picker dialog input size Fixes: flutter#162796 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
1 parent 38eafac commit 29810cd

File tree

2 files changed

+85
-30
lines changed

2 files changed

+85
-30
lines changed

packages/flutter/lib/src/material/time_picker.dart

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,7 +2324,8 @@ class _TimePickerDialogState extends State<TimePickerDialog> with RestorationMix
23242324
static const Size _kTimePickerPortraitSize = Size(310, 468);
23252325
static const Size _kTimePickerLandscapeSize = Size(524, 342);
23262326
static const Size _kTimePickerLandscapeSizeM2 = Size(508, 300);
2327-
static const Size _kTimePickerInputSize = Size(312, 216);
2327+
static const Size _kTimePickerInputSize = Size(312, 252);
2328+
static const double _kTimePickerInputMinimumHeight = 216;
23282329

23292330
// Absolute minimum dialog sizes, which is the point at which it begins
23302331
// scrolling to fit everything in.
@@ -2606,33 +2607,45 @@ class _TimePickerDialogState extends State<TimePickerDialog> with RestorationMix
26062607
restorationId: 'time_picker_scroll_view_vertical',
26072608
child: AnimatedContainer(
26082609
width: allowedSize.width,
2609-
height: allowedSize.height,
26102610
duration: _kDialogSizeAnimationDuration,
26112611
curve: Curves.easeIn,
2612+
constraints: BoxConstraints(
2613+
minHeight: _kTimePickerInputMinimumHeight,
2614+
maxHeight: allowedSize.height,
2615+
),
26122616
child: Column(
2617+
mainAxisSize: MainAxisSize.min,
2618+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
26132619
crossAxisAlignment: CrossAxisAlignment.start,
26142620
children: <Widget>[
2615-
Expanded(
2616-
child: Form(
2617-
key: _formKey,
2618-
autovalidateMode: _autovalidateMode.value,
2619-
child: _TimePicker(
2620-
time: widget.initialTime,
2621-
onTimeChanged: _handleTimeChanged,
2622-
helpText: widget.helpText,
2623-
cancelText: widget.cancelText,
2624-
confirmText: widget.confirmText,
2625-
errorInvalidText: widget.errorInvalidText,
2626-
hourLabelText: widget.hourLabelText,
2627-
minuteLabelText: widget.minuteLabelText,
2628-
restorationId: 'time_picker',
2629-
entryMode: _entryMode.value,
2630-
orientation: widget.orientation,
2631-
onEntryModeChanged: _handleEntryModeChanged,
2632-
switchToInputEntryModeIcon: widget.switchToInputEntryModeIcon,
2633-
switchToTimerEntryModeIcon: widget.switchToTimerEntryModeIcon,
2634-
),
2635-
),
2621+
Builder(
2622+
builder: (BuildContext context) {
2623+
final Widget child = Form(
2624+
key: _formKey,
2625+
autovalidateMode: _autovalidateMode.value,
2626+
child: _TimePicker(
2627+
time: widget.initialTime,
2628+
onTimeChanged: _handleTimeChanged,
2629+
helpText: widget.helpText,
2630+
cancelText: widget.cancelText,
2631+
confirmText: widget.confirmText,
2632+
errorInvalidText: widget.errorInvalidText,
2633+
hourLabelText: widget.hourLabelText,
2634+
minuteLabelText: widget.minuteLabelText,
2635+
restorationId: 'time_picker',
2636+
entryMode: _entryMode.value,
2637+
orientation: widget.orientation,
2638+
onEntryModeChanged: _handleEntryModeChanged,
2639+
switchToInputEntryModeIcon: widget.switchToInputEntryModeIcon,
2640+
switchToTimerEntryModeIcon: widget.switchToTimerEntryModeIcon,
2641+
),
2642+
);
2643+
if (_entryMode.value != TimePickerEntryMode.input &&
2644+
_entryMode.value != TimePickerEntryMode.inputOnly) {
2645+
return Flexible(child: child);
2646+
}
2647+
return child;
2648+
},
26362649
),
26372650
actions,
26382651
],

packages/flutter/test/material/time_picker_test.dart

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ void main() {
7272

7373
testWidgets('Material2 - Dialog size - input mode', (WidgetTester tester) async {
7474
const TimePickerEntryMode entryMode = TimePickerEntryMode.input;
75-
const Size timePickerInputSize = Size(312, 216);
75+
const Size timePickerInputSize = Size(312, 252);
7676
const Size dayPeriodPortraitSize = Size(52, 80);
7777
const EdgeInsets padding = EdgeInsets.fromLTRB(8, 18, 8, 8);
7878
final double height = timePickerInputSize.height + padding.vertical;
@@ -81,7 +81,9 @@ void main() {
8181
await mediaQueryBoilerplate(tester, entryMode: entryMode, materialType: MaterialType.material2);
8282

8383
width = timePickerInputSize.width + padding.horizontal;
84-
expect(tester.getSize(find.byWidget(getMaterialFromDialog(tester))), Size(width, height));
84+
Size size = tester.getSize(find.byWidget(getMaterialFromDialog(tester)));
85+
expect(size.width, width);
86+
expect(size.height, lessThan(height));
8587

8688
await tester.tap(find.text(okString)); // dismiss the dialog
8789
await tester.pumpAndSettle();
@@ -93,7 +95,9 @@ void main() {
9395
materialType: MaterialType.material2,
9496
);
9597
width = timePickerInputSize.width - dayPeriodPortraitSize.width - 12 + padding.horizontal + 16;
96-
expect(tester.getSize(find.byWidget(getMaterialFromDialog(tester))), Size(width, height));
98+
size = tester.getSize(find.byWidget(getMaterialFromDialog(tester)));
99+
expect(size.width, width);
100+
expect(size.height, lessThan(height));
97101
});
98102

99103
testWidgets('Material2 - respects MediaQueryData.alwaysUse24HourFormat == true', (
@@ -163,7 +167,7 @@ void main() {
163167
final ThemeData theme = ThemeData();
164168
const TimePickerEntryMode entryMode = TimePickerEntryMode.input;
165169
const double textScaleFactor = 1.0;
166-
const Size timePickerMinInputSize = Size(312, 216);
170+
const Size timePickerMinInputSize = Size(312, 252);
167171
const Size dayPeriodPortraitSize = Size(52, 80);
168172
const EdgeInsets padding = EdgeInsets.all(24.0);
169173
final double height = timePickerMinInputSize.height * textScaleFactor + padding.vertical;
@@ -172,7 +176,9 @@ void main() {
172176
await mediaQueryBoilerplate(tester, entryMode: entryMode, materialType: MaterialType.material3);
173177

174178
width = timePickerMinInputSize.width - (theme.useMaterial3 ? 32 : 0) + padding.horizontal;
175-
expect(tester.getSize(find.byWidget(getMaterialFromDialog(tester))), Size(width, height));
179+
Size size = tester.getSize(find.byWidget(getMaterialFromDialog(tester)));
180+
expect(size.width, width);
181+
expect(size.height, lessThan(height));
176182

177183
await tester.tap(find.text(okString)); // dismiss the dialog
178184
await tester.pumpAndSettle();
@@ -185,7 +191,9 @@ void main() {
185191
);
186192

187193
width = timePickerMinInputSize.width - dayPeriodPortraitSize.width - 12 + padding.horizontal;
188-
expect(tester.getSize(find.byWidget(getMaterialFromDialog(tester))), Size(width, height));
194+
size = tester.getSize(find.byWidget(getMaterialFromDialog(tester)));
195+
expect(size.width, width);
196+
expect(size.height, lessThan(height));
189197
});
190198

191199
testWidgets('Material3 - respects MediaQueryData.alwaysUse24HourFormat == true', (
@@ -594,6 +602,24 @@ void main() {
594602
expect(find.text('Cancel'), findsOneWidget);
595603
});
596604

605+
testWidgets(
606+
'Material3 - large actions label should not overflow in input mode',
607+
(WidgetTester tester) async {
608+
await startPicker(
609+
tester,
610+
(TimeOfDay? time) {},
611+
entryMode: TimePickerEntryMode.input,
612+
materialType: MaterialType.material3,
613+
cancelText: 'Very very very long cancel text',
614+
confirmText: 'Very very very long confirm text',
615+
);
616+
617+
// Verify that no overflow errors occur.
618+
expect(tester.takeException(), isNull);
619+
},
620+
variant: TargetPlatformVariant.mobile(),
621+
);
622+
597623
testWidgets('respects MediaQueryData.alwaysUse24HourFormat == false', (
598624
WidgetTester tester,
599625
) async {
@@ -2325,11 +2351,15 @@ class _TimePickerLauncher extends StatefulWidget {
23252351
required this.onChanged,
23262352
this.entryMode = TimePickerEntryMode.dial,
23272353
this.restorationId,
2354+
this.cancelText,
2355+
this.confirmText,
23282356
});
23292357

23302358
final ValueChanged<TimeOfDay?> onChanged;
23312359
final TimePickerEntryMode entryMode;
23322360
final String? restorationId;
2361+
final String? cancelText;
2362+
final String? confirmText;
23332363

23342364
@override
23352365
_TimePickerLauncherState createState() => _TimePickerLauncherState();
@@ -2346,7 +2376,11 @@ class _TimePickerLauncherState extends State<_TimePickerLauncher> with Restorati
23462376
onPresent: (NavigatorState navigator, Object? arguments) {
23472377
return navigator.restorablePush(
23482378
_timePickerRoute,
2349-
arguments: <String, String>{'entry_mode': widget.entryMode.name},
2379+
arguments: <String, String>{
2380+
'entry_mode': widget.entryMode.name,
2381+
if (widget.cancelText != null) 'cancel_text': widget.cancelText!,
2382+
if (widget.confirmText != null) 'confirm_text': widget.confirmText!,
2383+
},
23502384
);
23512385
},
23522386
);
@@ -2363,13 +2397,17 @@ class _TimePickerLauncherState extends State<_TimePickerLauncher> with Restorati
23632397
final TimePickerEntryMode entryMode = TimePickerEntryMode.values.firstWhere(
23642398
(TimePickerEntryMode element) => element.name == args['entry_mode'],
23652399
);
2400+
final String? cancelText = args['cancel_text'] as String?;
2401+
final String? confirmText = args['confirm_text'] as String?;
23662402
return DialogRoute<TimeOfDay>(
23672403
context: context,
23682404
builder: (BuildContext context) {
23692405
return TimePickerDialog(
23702406
restorationId: 'time_picker_dialog',
23712407
initialTime: const TimeOfDay(hour: 7, minute: 0),
23722408
initialEntryMode: entryMode,
2409+
cancelText: cancelText,
2410+
confirmText: confirmText,
23732411
);
23742412
},
23752413
);
@@ -2426,6 +2464,8 @@ Future<Offset?> startPicker(
24262464
String? restorationId,
24272465
ThemeData? theme,
24282466
MaterialType? materialType,
2467+
String? cancelText,
2468+
String? confirmText,
24292469
}) async {
24302470
await tester.pumpWidget(
24312471
MaterialApp(
@@ -2436,6 +2476,8 @@ Future<Offset?> startPicker(
24362476
onChanged: onChanged,
24372477
entryMode: entryMode,
24382478
restorationId: restorationId,
2479+
cancelText: cancelText,
2480+
confirmText: confirmText,
24392481
),
24402482
),
24412483
);

0 commit comments

Comments
 (0)