Skip to content

Commit a46bf19

Browse files
authored
Fix date picker flickering (flutter#167976)
## Description This PR fixes a `DatePicker` flickering issue observed on some devices with a small display. ### Before When the date picker input dialog is shown the virtual keyboard opens and reduces the available height. The current logic reacts by removing the picker `TextField`. Because there is no more `TextField` the virtual keyboard is hidden and the available height increases, at that moment the `TextField` is shown again. Due to autofocus the virtual keyboard opens which leads to the removal of the `TextField` and so on. https://github.com/user-attachments/assets/deb70f2e-1c8b-47a9-9db6-47bc521e4eb7 ### After When there is not enough vertical space for the input date picker dialog, the header is hidden instead of hiding the `TextField`. https://github.com/user-attachments/assets/14051ee9-3b9a-4467-a4a0-4ee91b4979ea ## Related Issue [Fixes DatePickerDialog text input causes keyboard to open and close on a 480p Android emulator](flutter#140311). ## Tests Adds 2 tests.
1 parent 04616f8 commit a46bf19

File tree

3 files changed

+62
-6
lines changed

3 files changed

+62
-6
lines changed

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -777,13 +777,21 @@ class _DatePickerDialogState extends State<DatePickerDialog> with RestorationMix
777777

778778
switch (orientation) {
779779
case Orientation.portrait:
780+
final bool isInputMode =
781+
_entryMode.value == DatePickerEntryMode.inputOnly ||
782+
_entryMode.value == DatePickerEntryMode.input;
783+
// When the portrait dialog does not fit vertically, hide the header when the entry mode
784+
// is input, or hide the picker when the entry mode is not input.
785+
final bool showHeader = isFullyPortrait || !isInputMode;
786+
final bool showPicker = isFullyPortrait || isInputMode;
787+
780788
return Column(
781789
mainAxisSize: MainAxisSize.min,
782790
crossAxisAlignment: CrossAxisAlignment.stretch,
783791
children: <Widget>[
784-
header,
792+
if (showHeader) header,
785793
if (useMaterial3) Divider(height: 0, color: datePickerTheme.dividerColor),
786-
if (isFullyPortrait) ...<Widget>[Expanded(child: picker), actions],
794+
if (showPicker) ...<Widget>[Expanded(child: picker), actions],
787795
],
788796
);
789797
case Orientation.landscape:
@@ -3175,10 +3183,8 @@ class _InputDateRangePickerDialog extends StatelessWidget {
31753183
return Column(
31763184
mainAxisSize: MainAxisSize.min,
31773185
crossAxisAlignment: CrossAxisAlignment.stretch,
3178-
children: <Widget>[
3179-
header,
3180-
if (isFullyPortrait) ...<Widget>[Expanded(child: picker), actions],
3181-
],
3186+
// When the portrait dialog does not fit vertically, hide the header.
3187+
children: <Widget>[if (isFullyPortrait) header, Expanded(child: picker), actions],
31823188
);
31833189
},
31843190
);

packages/flutter/test/material/date_picker_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,31 @@ void main() {
16621662
});
16631663
});
16641664

1665+
// Regression test for https://github.com/flutter/flutter/issues/140311.
1666+
testWidgets('Text field stays visible when orientation is portrait and height is reduced', (
1667+
WidgetTester tester,
1668+
) async {
1669+
addTearDown(tester.view.reset);
1670+
tester.view.physicalSize = const Size(720, 1280);
1671+
tester.view.devicePixelRatio = 1.0;
1672+
initialEntryMode = DatePickerEntryMode.input;
1673+
1674+
// Text field and header are visible by default.
1675+
await prepareDatePicker(tester, useMaterial3: true, (Future<DateTime?> range) async {
1676+
expect(find.byType(TextField), findsOneWidget);
1677+
expect(find.text('Select date'), findsOne);
1678+
});
1679+
1680+
// Simulate the portait mode on a device with a small display when the virtual
1681+
// keyboard is visible.
1682+
tester.view.viewInsets = const FakeViewPadding(bottom: 1000);
1683+
await tester.pumpAndSettle();
1684+
1685+
// Text field is visible and header is hidden.
1686+
expect(find.byType(TextField), findsOneWidget);
1687+
expect(find.text('Select date'), findsNothing);
1688+
});
1689+
16651690
// This is a regression test for https://github.com/flutter/flutter/issues/139120.
16661691
testWidgets('Dialog contents are visible - textScaler 0.88, 1.0, 2.0', (
16671692
WidgetTester tester,

packages/flutter/test/material/date_range_picker_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,31 @@ void main() {
12431243
expect(tester.takeException(), null);
12441244
});
12451245
});
1246+
1247+
// Regression test for https://github.com/flutter/flutter/issues/140311.
1248+
testWidgets('Text field stays visible when orientation is portrait and height is reduced', (
1249+
WidgetTester tester,
1250+
) async {
1251+
addTearDown(tester.view.reset);
1252+
tester.view.physicalSize = const Size(720, 1280);
1253+
tester.view.devicePixelRatio = 1.0;
1254+
initialEntryMode = DatePickerEntryMode.input;
1255+
1256+
// Text fields and header are visible by default.
1257+
await preparePicker(tester, useMaterial3: true, (Future<DateTimeRange?> range) async {
1258+
expect(find.byType(TextField), findsNWidgets(2));
1259+
expect(find.text('Select range'), findsOne);
1260+
});
1261+
1262+
// Simulate the portait mode on a device with a small display when the virtual
1263+
// keyboard is visible.
1264+
tester.view.viewInsets = const FakeViewPadding(bottom: 1000);
1265+
await tester.pumpAndSettle();
1266+
1267+
// Text fields are visible and header is hidden
1268+
expect(find.byType(TextField), findsNWidgets(2));
1269+
expect(find.text('Select range'), findsNothing);
1270+
});
12461271
});
12471272

12481273
testWidgets('DatePickerDialog is state restorable', (WidgetTester tester) async {

0 commit comments

Comments
 (0)