From 4d7792e44f870a813435569195de694f53b1d4a0 Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Fri, 31 Mar 2023 18:14:38 -0700 Subject: [PATCH] M3 DatePicker landscape header text style is now TextTheme.headlineSmall (#123732) M3 DatePicker landscape header text style is now TextTheme.headlineSmall --- .../flutter/lib/src/material/date_picker.dart | 52 ++++++++++++------- .../test/material/date_picker_test.dart | 30 +++++++++++ .../test/material/date_range_picker_test.dart | 30 +++++++++++ 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index 1c7c394d8361f..2a20d9be8c1ff 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -477,21 +477,28 @@ class _DatePickerDialogState extends State with RestorationMix final DatePickerThemeData defaults = DatePickerTheme.defaults(context); final TextTheme textTheme = theme.textTheme; - // Constrain the textScaleFactor to the largest supported value to prevent - // layout issues. - final double textScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 1.3); + // There's no M3 spec for a landscape layout input (not calendar) + // date picker. To ensure that the date displayed in the input + // date picker's header fits in landscape mode, we override the M3 + // default here. + TextStyle? headlineStyle; + if (useMaterial3) { + headlineStyle = datePickerTheme.headerHeadlineStyle ?? defaults.headerHeadlineStyle; + switch (_entryMode.value) { + case DatePickerEntryMode.input: + case DatePickerEntryMode.inputOnly: + if (orientation == Orientation.landscape) { + headlineStyle = textTheme.headlineSmall; + } + case DatePickerEntryMode.calendar: + case DatePickerEntryMode.calendarOnly: + // M3 default is OK. + } + } else { + headlineStyle = orientation == Orientation.landscape ? textTheme.headlineSmall : textTheme.headlineMedium; + } final Color? headerForegroundColor = datePickerTheme.headerForegroundColor ?? defaults.headerForegroundColor; - final TextStyle? headlineStyle = useMaterial3 - ? (datePickerTheme.headerHeadlineStyle ?? defaults.headerHeadlineStyle)?.copyWith( - color: headerForegroundColor, - ) - // Material2 has support for landscape and the current M3 spec doesn't - // address this layout, so handling it separately here. - : (orientation == Orientation.landscape - ? textTheme.headlineSmall?.copyWith(color: headerForegroundColor) - : textTheme.headlineMedium?.copyWith(color: headerForegroundColor)); - - final String dateText = localizations.formatMediumDate(_selectedDate.value); + headlineStyle = headlineStyle?.copyWith(color: headerForegroundColor); final Widget actions = Container( alignment: AlignmentDirectional.centerEnd, @@ -599,13 +606,16 @@ class _DatePickerDialogState extends State with RestorationMix ? localizations.datePickerHelpText : localizations.datePickerHelpText.toUpperCase() ), - titleText: dateText, + titleText: localizations.formatMediumDate(_selectedDate.value), titleStyle: headlineStyle, orientation: orientation, isShort: orientation == Orientation.landscape, entryModeButton: entryModeButton, ); + // Constrain the textScaleFactor to the largest supported value to prevent + // layout issues. + final double textScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 1.3); final Size dialogSize = _dialogSize(context) * textScaleFactor; final DialogTheme dialogTheme = theme.dialogTheme; return Dialog( @@ -2660,10 +2670,16 @@ class _InputDateRangePickerDialog extends StatelessWidget { final DatePickerThemeData datePickerTheme = DatePickerTheme.of(context); final DatePickerThemeData defaults = DatePickerTheme.defaults(context); + // There's no M3 spec for a landscape layout input (not calendar) + // date range picker. To ensure that the date range displayed in the + // input date range picker's header fits in landscape mode, we override + // the M3 default here. + TextStyle? headlineStyle = (orientation == Orientation.portrait) + ? datePickerTheme.headerHeadlineStyle ?? defaults.headerHeadlineStyle + : Theme.of(context).textTheme.headlineSmall; + final Color? headerForegroundColor = datePickerTheme.headerForegroundColor ?? defaults.headerForegroundColor; - final TextStyle? headlineStyle = (datePickerTheme.headerHeadlineStyle ?? defaults.headerHeadlineStyle)?.copyWith( - color: headerForegroundColor, - ); + headlineStyle = headlineStyle?.copyWith(color: headerForegroundColor); final String dateText = _formatDateRange(context, selectedStartDate, selectedEndDate, currentDate!); final String semanticDateText = selectedStartDate != null && selectedEndDate != null diff --git a/packages/flutter/test/material/date_picker_test.dart b/packages/flutter/test/material/date_picker_test.dart index 6caebc5398b49..6240ceae103d6 100644 --- a/packages/flutter/test/material/date_picker_test.dart +++ b/packages/flutter/test/material/date_picker_test.dart @@ -1387,6 +1387,36 @@ void main() { expect(find.byType(TextField), findsNothing); }); }); + + group('Landscape input-only date picker headers use headlineSmall', () { + // Regression test for https://github.com/flutter/flutter/issues/122056 + + // Common screen size roughly based on a Pixel 1 + const Size kCommonScreenSizePortrait = Size(1070, 1770); + const Size kCommonScreenSizeLandscape = Size(1770, 1070); + + Future showPicker(WidgetTester tester, Size size) async { + addTearDown(tester.view.reset); + tester.view.physicalSize = size; + tester.view.devicePixelRatio = 1.0; + initialEntryMode = DatePickerEntryMode.input; + await prepareDatePicker(tester, (Future date) async { }, useMaterial3: true); + } + + testWidgets('portrait', (WidgetTester tester) async { + await showPicker(tester, kCommonScreenSizePortrait); + expect(tester.widget(find.text('Fri, Jan 15')).style?.fontSize, 32); + await tester.tap(find.text('Cancel')); + await tester.pumpAndSettle(); + }); + + testWidgets('landscape', (WidgetTester tester) async { + await showPicker(tester, kCommonScreenSizeLandscape); + expect(tester.widget(find.text('Fri, Jan 15')).style?.fontSize, 24); + await tester.tap(find.text('Cancel')); + await tester.pumpAndSettle(); + }); + }); } class _RestorableDatePickerDialogTestWidget extends StatefulWidget { diff --git a/packages/flutter/test/material/date_range_picker_test.dart b/packages/flutter/test/material/date_range_picker_test.dart index a423bdd6facad..a696937f9dbc0 100644 --- a/packages/flutter/test/material/date_range_picker_test.dart +++ b/packages/flutter/test/material/date_range_picker_test.dart @@ -108,6 +108,36 @@ void main() { await callback(range); } + group('Landscape input-only date picker headers use headlineSmall', () { + // Regression test for https://github.com/flutter/flutter/issues/122056 + + // Common screen size roughly based on a Pixel 1 + const Size kCommonScreenSizePortrait = Size(1070, 1770); + const Size kCommonScreenSizeLandscape = Size(1770, 1070); + + Future showPicker(WidgetTester tester, Size size) async { + addTearDown(tester.view.reset); + tester.view.physicalSize = size; + tester.view.devicePixelRatio = 1.0; + initialEntryMode = DatePickerEntryMode.input; + await preparePicker(tester, (Future range) async { }, useMaterial3: true); + } + + testWidgets('portrait', (WidgetTester tester) async { + await showPicker(tester, kCommonScreenSizePortrait); + expect(tester.widget(find.text('Jan 15 – Jan 25, 2016')).style?.fontSize, 32); + await tester.tap(find.text('Cancel')); + await tester.pumpAndSettle(); + }); + + testWidgets('landscape', (WidgetTester tester) async { + await showPicker(tester, kCommonScreenSizeLandscape); + expect(tester.widget(find.text('Jan 15 – Jan 25, 2016')).style?.fontSize, 24); + await tester.tap(find.text('Cancel')); + await tester.pumpAndSettle(); + }); + }); + testWidgets('Save and help text is used', (WidgetTester tester) async { helpText = 'help'; saveText = 'make it so';