@@ -42,6 +42,25 @@ const double _yearPickerRowSpacing = 8.0;
42
42
const double _subHeaderHeight = 52.0 ;
43
43
const double _monthNavButtonsWidth = 108.0 ;
44
44
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
+
45
64
/// Displays a grid of days for a given month and allows the user to select a
46
65
/// date.
47
66
///
@@ -319,20 +338,30 @@ class _CalendarDatePickerState extends State<CalendarDatePicker> {
319
338
assert (debugCheckHasMaterial (context));
320
339
assert (debugCheckHasMaterialLocalizations (context));
321
340
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;
322
348
return Stack (
323
349
children: < Widget > [
324
350
SizedBox (
325
- height: _subHeaderHeight + _maxDayPickerHeight ,
351
+ height: _subHeaderHeight + scaledMaxDayPickerHeight ,
326
352
child: _buildPicker (),
327
353
),
328
354
// 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
+ ),
336
365
),
337
366
],
338
367
);
@@ -949,6 +978,9 @@ class _DayPickerState extends State<_DayPicker> {
949
978
final DatePickerThemeData defaults = DatePickerTheme .defaults (context);
950
979
final TextStyle ? weekdayStyle = datePickerTheme.weekdayStyle ?? defaults.weekdayStyle;
951
980
981
+ final Orientation orientation = MediaQuery .orientationOf (context);
982
+ final bool isLandscapeOrientation = orientation == Orientation .landscape;
983
+
952
984
final int year = widget.displayedMonth.year;
953
985
final int month = widget.displayedMonth.month;
954
986
@@ -990,12 +1022,17 @@ class _DayPickerState extends State<_DayPicker> {
990
1022
padding: const EdgeInsets .symmetric (
991
1023
horizontal: _monthPickerHorizontalPadding,
992
1024
),
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
+ ),
999
1036
),
1000
1037
),
1001
1038
);
@@ -1120,14 +1157,19 @@ class _DayState extends State<_Day> {
1120
1157
}
1121
1158
1122
1159
class _DayPickerGridDelegate extends SliverGridDelegate {
1123
- const _DayPickerGridDelegate ();
1160
+ const _DayPickerGridDelegate (this .context);
1161
+
1162
+ final BuildContext context;
1124
1163
1125
1164
@override
1126
1165
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;
1127
1169
const int columnCount = DateTime .daysPerWeek;
1128
1170
final double tileWidth = constraints.crossAxisExtent / columnCount;
1129
1171
final double tileHeight = math.min (
1130
- _dayPickerRowHeight ,
1172
+ scaledRowHeight ,
1131
1173
constraints.viewportMainAxisExtent / (_maxDayPickerRowCount + 1 ),
1132
1174
);
1133
1175
return SliverGridRegularTileLayout (
@@ -1144,8 +1186,6 @@ class _DayPickerGridDelegate extends SliverGridDelegate {
1144
1186
bool shouldRelayout (_DayPickerGridDelegate oldDelegate) => false ;
1145
1187
}
1146
1188
1147
- const _DayPickerGridDelegate _dayPickerGridDelegate = _DayPickerGridDelegate ();
1148
-
1149
1189
/// A scrollable grid of years to allow picking a year.
1150
1190
///
1151
1191
/// The year picker widget is rarely used directly. Instead, consider using
@@ -1259,14 +1299,16 @@ class _YearPickerState extends State<YearPicker> {
1259
1299
);
1260
1300
}
1261
1301
1302
+ final double textScaleFactor = MediaQuery .textScalerOf (context).clamp (maxScaleFactor: 3.0 ).scale (_fontSizeToScale) / _fontSizeToScale;
1303
+
1262
1304
// Backfill the _YearPicker with disabled years if necessary.
1263
1305
final int offset = _itemCount < minYears ? (minYears - _itemCount) ~ / 2 : 0 ;
1264
1306
final int year = widget.firstDate.year + index - offset;
1265
1307
final bool isSelected = year == widget.selectedDate? .year;
1266
1308
final bool isCurrentYear = year == widget.currentDate.year;
1267
1309
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 ;
1270
1312
1271
1313
final Set <MaterialState > states = < MaterialState > {
1272
1314
if (isDisabled) MaterialState .disabled,
@@ -1350,7 +1392,7 @@ class _YearPickerState extends State<YearPicker> {
1350
1392
child: GridView .builder (
1351
1393
controller: _scrollController,
1352
1394
dragStartBehavior: widget.dragStartBehavior,
1353
- gridDelegate: _yearPickerGridDelegate ,
1395
+ gridDelegate: _YearPickerGridDelegate (context) ,
1354
1396
itemBuilder: _buildYearItem,
1355
1397
itemCount: math.max (_itemCount, minYears),
1356
1398
padding: const EdgeInsets .symmetric (horizontal: _yearPickerPadding),
@@ -1363,24 +1405,27 @@ class _YearPickerState extends State<YearPicker> {
1363
1405
}
1364
1406
1365
1407
class _YearPickerGridDelegate extends SliverGridDelegate {
1366
- const _YearPickerGridDelegate ();
1408
+ const _YearPickerGridDelegate (this .context);
1409
+
1410
+ final BuildContext context;
1367
1411
1368
1412
@override
1369
1413
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;
1370
1416
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;
1372
1419
return SliverGridRegularTileLayout (
1373
1420
childCrossAxisExtent: tileWidth,
1374
- childMainAxisExtent: _yearPickerRowHeight ,
1375
- crossAxisCount: _yearPickerColumnCount ,
1421
+ childMainAxisExtent: scaledYearPickerRowHeight ,
1422
+ crossAxisCount: scaledYearPickerColumnCount ,
1376
1423
crossAxisStride: tileWidth + _yearPickerRowSpacing,
1377
- mainAxisStride: _yearPickerRowHeight ,
1424
+ mainAxisStride: scaledYearPickerRowHeight ,
1378
1425
reverseCrossAxis: axisDirectionIsReversed (constraints.crossAxisDirection),
1379
1426
);
1380
1427
}
1381
1428
1382
1429
@override
1383
1430
bool shouldRelayout (_YearPickerGridDelegate oldDelegate) => false ;
1384
1431
}
1385
-
1386
- const _YearPickerGridDelegate _yearPickerGridDelegate = _YearPickerGridDelegate ();
0 commit comments