Skip to content

Commit 91a3f69

Browse files
Calendar font factor (#152341)
Increases the max text can be scaled for the date picker in calendar mode and input mode. Previously the max across the whole widget was 1.3. Due to the size of the widget, this was increased as much as possible with different values used in different places. Testing and screenshots where taken on the iPhone SE 3rd generation simulator, set at max font size, which is a value of 3.0. Android has a lower max font scale value of 2.0, and the iPhone SE is about the smallest phone with a lower pixel density ratio. Fixes internal issues b/316958515 and b/316959677 Also fixes #61334 Comparison for calendar mode in portrait and landscape: | Before | After | | -------- | ------- | | <img width="375" alt="Old-SE-Portrait-DayPicker" src="https://github.com/user-attachments/assets/4dd1735f-f4c7-4a0a-b8d3-e5ea84d2ba3c"> | <img width="376" alt="Screenshot 2024-07-25 at 1 25 41�PM" src="https://github.com/user-attachments/assets/a53d7d68-87ef-4b29-9479-36ef22bd6cc9"> | | <img width="375" alt="Old-SE-Portrait-YearPicker" src="https://github.com/user-attachments/assets/37c2965d-1ec0-429b-aa4d-37396f90cb74"> | <img width="377" alt="Screenshot 2024-07-25 at 1 26 38�PM" src="https://github.com/user-attachments/assets/2a00d90f-d523-4ff5-a1d7-e1bfafb245d3"> | | <img width="665" alt="Old-SE-Landscape-DayPicker" src="https://github.com/user-attachments/assets/1cc4cd26-d56a-4f35-88b1-1c13fa460c2f"> | <img width="665" alt="Screenshot 2024-07-25 at 1 25 52�PM" src="https://github.com/user-attachments/assets/729ac66c-d6b9-4a2a-8303-b5c9face0f62"> | | <img width="664" alt="Old-SE-Landscape-YearPicker" src="https://github.com/user-attachments/assets/f00a9ab8-1925-4c33-bfcc-31020b2858b8"> | <img width="666" alt="Screenshot 2024-07-25 at 1 26 47�PM" src="https://github.com/user-attachments/assets/d6116c20-4862-4e07-8ab4-fb8ecb71bfa5"> | The title text is smaller when the entry mode button is available: <img width="374" alt="Screenshot 2024-07-25 at 1 24 52�PM" src="https://github.com/user-attachments/assets/83305c11-97d5-4986-bf51-fe0be71f653e"> Adjustments were made to input mode as well, but they are simpler <img width="372" alt="Screenshot 2024-07-25 at 1 43 39�PM" src="https://github.com/user-attachments/assets/2440cf6f-160f-4689-978e-d0a3df2db102"> <img width="666" alt="Screenshot 2024-07-25 at 1 43 48�PM" src="https://github.com/user-attachments/assets/e8d8dbf3-c7d8-4668-9245-7b5036165e75"> Date range picker was not adjusted with this PR. It still has a max of 1.3.
1 parent a5609df commit 91a3f69

File tree

3 files changed

+194
-76
lines changed

3 files changed

+194
-76
lines changed

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

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ const double _yearPickerRowSpacing = 8.0;
4242
const double _subHeaderHeight = 52.0;
4343
const double _monthNavButtonsWidth = 108.0;
4444

45+
// 3.0 is the maximum scale factor on mobile phones. As of 07/30/24, iOS goes up
46+
// to a max of 3.0 text sxale factor, and Android goes up to 2.0. This is the
47+
// default used for non-range date pickers. This default is changed to a lower
48+
// value at different parts of the date pickers depending on content, and device
49+
// orientation.
50+
const double _kMaxTextScaleFactor = 3.0;
51+
52+
const double _kModeToggleButtonMaxScaleFactor = 2.0;
53+
54+
// The max scale factor of the day picker grid. This affects the size of the
55+
// individual days in calendar view. Due to them filling a majority of the modal,
56+
// which covers most of the screen, there's a limit in how large they can grow.
57+
// There is also less room vertically in landscape orientation.
58+
const double _kDayPickerGridPortraitMaxScaleFactor = 2.0;
59+
const double _kDayPickerGridLandscapeMaxScaleFactor = 1.5;
60+
61+
// 14 is a common font size used to compute the effective text scale.
62+
const double _fontSizeToScale = 14.0;
63+
4564
/// Displays a grid of days for a given month and allows the user to select a
4665
/// date.
4766
///
@@ -319,20 +338,30 @@ class _CalendarDatePickerState extends State<CalendarDatePicker> {
319338
assert(debugCheckHasMaterial(context));
320339
assert(debugCheckHasMaterialLocalizations(context));
321340
assert(debugCheckHasDirectionality(context));
341+
final double textScaleFactor = MediaQuery.textScalerOf(context).clamp(maxScaleFactor: _kMaxTextScaleFactor).scale(_fontSizeToScale) / _fontSizeToScale;
342+
// Scale the height of the picker area up with larger text. The size of the
343+
// picker has room for larger text, up until a scale facotr of 1.3. After
344+
// after which, we increase the height to add room for content to continue
345+
// to scale the text size.
346+
final double scaledMaxDayPickerHeight =
347+
textScaleFactor > 1.3 ? _maxDayPickerHeight + ((_maxDayPickerRowCount + 1) * ((textScaleFactor - 1) * 8)) : _maxDayPickerHeight;
322348
return Stack(
323349
children: <Widget>[
324350
SizedBox(
325-
height: _subHeaderHeight + _maxDayPickerHeight,
351+
height: _subHeaderHeight + scaledMaxDayPickerHeight,
326352
child: _buildPicker(),
327353
),
328354
// Put the mode toggle button on top so that it won't be covered up by the _MonthPicker
329-
_DatePickerModeToggleButton(
330-
mode: _mode,
331-
title: _localizations.formatMonthYear(_currentDisplayedMonthDate),
332-
onTitlePressed: () => _handleModeChanged(switch (_mode) {
333-
DatePickerMode.day => DatePickerMode.year,
334-
DatePickerMode.year => DatePickerMode.day,
335-
}),
355+
MediaQuery.withClampedTextScaling(
356+
maxScaleFactor: _kModeToggleButtonMaxScaleFactor,
357+
child: _DatePickerModeToggleButton(
358+
mode: _mode,
359+
title: _localizations.formatMonthYear(_currentDisplayedMonthDate),
360+
onTitlePressed: () => _handleModeChanged(switch (_mode) {
361+
DatePickerMode.day => DatePickerMode.year,
362+
DatePickerMode.year => DatePickerMode.day,
363+
}),
364+
),
336365
),
337366
],
338367
);
@@ -949,6 +978,9 @@ class _DayPickerState extends State<_DayPicker> {
949978
final DatePickerThemeData defaults = DatePickerTheme.defaults(context);
950979
final TextStyle? weekdayStyle = datePickerTheme.weekdayStyle ?? defaults.weekdayStyle;
951980

981+
final Orientation orientation = MediaQuery.orientationOf(context);
982+
final bool isLandscapeOrientation = orientation == Orientation.landscape;
983+
952984
final int year = widget.displayedMonth.year;
953985
final int month = widget.displayedMonth.month;
954986

@@ -990,12 +1022,17 @@ class _DayPickerState extends State<_DayPicker> {
9901022
padding: const EdgeInsets.symmetric(
9911023
horizontal: _monthPickerHorizontalPadding,
9921024
),
993-
child: GridView.custom(
994-
physics: const ClampingScrollPhysics(),
995-
gridDelegate: _dayPickerGridDelegate,
996-
childrenDelegate: SliverChildListDelegate(
997-
dayItems,
998-
addRepaintBoundaries: false,
1025+
child: MediaQuery.withClampedTextScaling(
1026+
maxScaleFactor: isLandscapeOrientation ?
1027+
_kDayPickerGridLandscapeMaxScaleFactor :
1028+
_kDayPickerGridPortraitMaxScaleFactor,
1029+
child: GridView.custom(
1030+
physics: const ClampingScrollPhysics(),
1031+
gridDelegate: _DayPickerGridDelegate(context),
1032+
childrenDelegate: SliverChildListDelegate(
1033+
dayItems,
1034+
addRepaintBoundaries: false,
1035+
),
9991036
),
10001037
),
10011038
);
@@ -1120,14 +1157,19 @@ class _DayState extends State<_Day> {
11201157
}
11211158

11221159
class _DayPickerGridDelegate extends SliverGridDelegate {
1123-
const _DayPickerGridDelegate();
1160+
const _DayPickerGridDelegate(this.context);
1161+
1162+
final BuildContext context;
11241163

11251164
@override
11261165
SliverGridLayout getLayout(SliverConstraints constraints) {
1166+
final double textScaleFactor = MediaQuery.textScalerOf(context).clamp(maxScaleFactor: 3.0).scale(_fontSizeToScale) / _fontSizeToScale;
1167+
final double scaledRowHeight =
1168+
textScaleFactor > 1.3 ? ((textScaleFactor - 1) * 30) + _dayPickerRowHeight : _dayPickerRowHeight;
11271169
const int columnCount = DateTime.daysPerWeek;
11281170
final double tileWidth = constraints.crossAxisExtent / columnCount;
11291171
final double tileHeight = math.min(
1130-
_dayPickerRowHeight,
1172+
scaledRowHeight,
11311173
constraints.viewportMainAxisExtent / (_maxDayPickerRowCount + 1),
11321174
);
11331175
return SliverGridRegularTileLayout(
@@ -1144,8 +1186,6 @@ class _DayPickerGridDelegate extends SliverGridDelegate {
11441186
bool shouldRelayout(_DayPickerGridDelegate oldDelegate) => false;
11451187
}
11461188

1147-
const _DayPickerGridDelegate _dayPickerGridDelegate = _DayPickerGridDelegate();
1148-
11491189
/// A scrollable grid of years to allow picking a year.
11501190
///
11511191
/// The year picker widget is rarely used directly. Instead, consider using
@@ -1259,14 +1299,16 @@ class _YearPickerState extends State<YearPicker> {
12591299
);
12601300
}
12611301

1302+
final double textScaleFactor = MediaQuery.textScalerOf(context).clamp(maxScaleFactor: 3.0).scale(_fontSizeToScale) / _fontSizeToScale;
1303+
12621304
// Backfill the _YearPicker with disabled years if necessary.
12631305
final int offset = _itemCount < minYears ? (minYears - _itemCount) ~/ 2 : 0;
12641306
final int year = widget.firstDate.year + index - offset;
12651307
final bool isSelected = year == widget.selectedDate?.year;
12661308
final bool isCurrentYear = year == widget.currentDate.year;
12671309
final bool isDisabled = year < widget.firstDate.year || year > widget.lastDate.year;
1268-
const double decorationHeight = 36.0;
1269-
const double decorationWidth = 72.0;
1310+
final double decorationHeight = 36.0 * textScaleFactor;
1311+
final double decorationWidth = 72.0 * textScaleFactor;
12701312

12711313
final Set<MaterialState> states = <MaterialState>{
12721314
if (isDisabled) MaterialState.disabled,
@@ -1350,7 +1392,7 @@ class _YearPickerState extends State<YearPicker> {
13501392
child: GridView.builder(
13511393
controller: _scrollController,
13521394
dragStartBehavior: widget.dragStartBehavior,
1353-
gridDelegate: _yearPickerGridDelegate,
1395+
gridDelegate: _YearPickerGridDelegate(context),
13541396
itemBuilder: _buildYearItem,
13551397
itemCount: math.max(_itemCount, minYears),
13561398
padding: const EdgeInsets.symmetric(horizontal: _yearPickerPadding),
@@ -1363,24 +1405,27 @@ class _YearPickerState extends State<YearPicker> {
13631405
}
13641406

13651407
class _YearPickerGridDelegate extends SliverGridDelegate {
1366-
const _YearPickerGridDelegate();
1408+
const _YearPickerGridDelegate(this.context);
1409+
1410+
final BuildContext context;
13671411

13681412
@override
13691413
SliverGridLayout getLayout(SliverConstraints constraints) {
1414+
final double textScaleFactor = MediaQuery.textScalerOf(context).clamp(maxScaleFactor: 3.0).scale(_fontSizeToScale) / _fontSizeToScale;
1415+
final int scaledYearPickerColumnCount = textScaleFactor > 1.65 ? _yearPickerColumnCount - 1 : _yearPickerColumnCount;
13701416
final double tileWidth =
1371-
(constraints.crossAxisExtent - (_yearPickerColumnCount - 1) * _yearPickerRowSpacing) / _yearPickerColumnCount;
1417+
(constraints.crossAxisExtent - (scaledYearPickerColumnCount - 1) * _yearPickerRowSpacing) / scaledYearPickerColumnCount;
1418+
final double scaledYearPickerRowHeight = textScaleFactor > 1 ? _yearPickerRowHeight + (( textScaleFactor - 1 ) * 9) : _yearPickerRowHeight;
13721419
return SliverGridRegularTileLayout(
13731420
childCrossAxisExtent: tileWidth,
1374-
childMainAxisExtent: _yearPickerRowHeight,
1375-
crossAxisCount: _yearPickerColumnCount,
1421+
childMainAxisExtent: scaledYearPickerRowHeight,
1422+
crossAxisCount: scaledYearPickerColumnCount,
13761423
crossAxisStride: tileWidth + _yearPickerRowSpacing,
1377-
mainAxisStride: _yearPickerRowHeight,
1424+
mainAxisStride: scaledYearPickerRowHeight,
13781425
reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
13791426
);
13801427
}
13811428

13821429
@override
13831430
bool shouldRelayout(_YearPickerGridDelegate oldDelegate) => false;
13841431
}
1385-
1386-
const _YearPickerGridDelegate _yearPickerGridDelegate = _YearPickerGridDelegate();

0 commit comments

Comments
 (0)