@@ -42,6 +42,25 @@ const double _yearPickerRowSpacing = 8.0;
4242const double _subHeaderHeight = 52.0 ;
4343const 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
11221159class _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
13651407class _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