LCOV - code coverage report
Current view: top level - src - month_picker.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 204 0.0 %
Date: 2022-02-12 14:49:12 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : 
       3             : import 'package:flutter/material.dart';
       4             : import 'package:flutter/rendering.dart';
       5             : import 'package:flutter_localizations/flutter_localizations.dart';
       6             : import 'package:intl/intl.dart' as intl;
       7             : 
       8             : import 'date_picker_keys.dart';
       9             : import 'day_type.dart';
      10             : import 'i_selectable_picker.dart';
      11             : import 'month_picker_selection.dart';
      12             : import 'semantic_sorting.dart';
      13             : import 'styles/date_picker_styles.dart';
      14             : import 'styles/layout_settings.dart';
      15             : import 'utils.dart';
      16             : 
      17             : const Locale _defaultLocale = Locale('en', 'US');
      18             : 
      19             : /// Month picker widget.
      20             : class MonthPicker<T extends Object> extends StatefulWidget {
      21           0 :   MonthPicker._({
      22             :     Key? key,
      23             :     required this.selectionLogic,
      24             :     required this.selection,
      25             :     required this.onChanged,
      26             :     required this.firstDate,
      27             :     required this.lastDate,
      28             :     this.datePickerLayoutSettings = const DatePickerLayoutSettings(),
      29             :     this.datePickerKeys,
      30             :     required this.datePickerStyles,
      31           0 :   })  : assert(!firstDate.isAfter(lastDate)),
      32             :         assert(
      33           0 :             selection.isEmpty || !selection.isBefore(firstDate),
      34             :             'Selection must not be before first date. '
      35           0 :             'Earliest selection is: ${selection.earliest}. '
      36             :             'First date is: $firstDate'),
      37             :         assert(
      38           0 :             selection.isEmpty || !selection.isAfter(lastDate),
      39             :             'Selection must not be after last date. '
      40           0 :             'Latest selection is: ${selection.latest}. '
      41             :             'First date is: $lastDate'),
      42           0 :         super(key: key);
      43             : 
      44             :   /// Creates a month picker where only one single month can be selected.
      45             :   ///
      46             :   /// See also:
      47             :   /// * [MonthPicker.multi] - month picker where many single months
      48             :   ///   can be selected.
      49           0 :   static MonthPicker<DateTime> single(
      50             :       {Key? key,
      51             :       required DateTime selectedDate,
      52             :       required ValueChanged<DateTime> onChanged,
      53             :       required DateTime firstDate,
      54             :       required DateTime lastDate,
      55             :       DatePickerLayoutSettings datePickerLayoutSettings =
      56             :           const DatePickerLayoutSettings(),
      57             :       DatePickerStyles? datePickerStyles,
      58             :       DatePickerKeys? datePickerKeys,
      59             :       SelectableDayPredicate? selectableDayPredicate,
      60             :       ValueChanged<DateTime>? onMonthChanged}) {
      61           0 :     assert(!firstDate.isAfter(lastDate));
      62           0 :     assert(!lastDate.isBefore(firstDate));
      63           0 :     assert(!selectedDate.isBefore(firstDate));
      64           0 :     assert(!selectedDate.isAfter(lastDate));
      65             : 
      66           0 :     final selection = MonthPickerSingleSelection(selectedDate);
      67           0 :     final selectionLogic = MonthSelectable(selectedDate, firstDate, lastDate,
      68             :         selectableDayPredicate: selectableDayPredicate);
      69             : 
      70           0 :     return MonthPicker<DateTime>._(
      71             :       onChanged: onChanged,
      72             :       firstDate: firstDate,
      73             :       lastDate: lastDate,
      74             :       selectionLogic: selectionLogic,
      75             :       selection: selection,
      76             :       datePickerKeys: datePickerKeys,
      77           0 :       datePickerStyles: datePickerStyles ?? DatePickerRangeStyles(),
      78             :       datePickerLayoutSettings: datePickerLayoutSettings,
      79             :     );
      80             :   }
      81             : 
      82             :   /// Creates a month picker where many single months can be selected.
      83             :   ///
      84             :   /// See also:
      85             :   /// * [MonthPicker.single] - month picker where only one single month
      86             :   /// can be selected.
      87           0 :   static MonthPicker<List<DateTime>> multi(
      88             :       {Key? key,
      89             :       required List<DateTime> selectedDates,
      90             :       required ValueChanged<List<DateTime>> onChanged,
      91             :       required DateTime firstDate,
      92             :       required DateTime lastDate,
      93             :       DatePickerLayoutSettings datePickerLayoutSettings =
      94             :           const DatePickerLayoutSettings(),
      95             :       DatePickerStyles? datePickerStyles,
      96             :       DatePickerKeys? datePickerKeys,
      97             :       SelectableDayPredicate? selectableDayPredicate,
      98             :       ValueChanged<DateTime>? onMonthChanged}) {
      99           0 :     assert(!firstDate.isAfter(lastDate));
     100           0 :     assert(!lastDate.isBefore(firstDate));
     101             : 
     102           0 :     final selection = MonthPickerMultiSelection(selectedDates);
     103           0 :     final selectionLogic = MonthMultiSelectable(
     104             :         selectedDates, firstDate, lastDate,
     105             :         selectableDayPredicate: selectableDayPredicate);
     106             : 
     107           0 :     return MonthPicker<List<DateTime>>._(
     108             :       onChanged: onChanged,
     109             :       firstDate: firstDate,
     110             :       lastDate: lastDate,
     111             :       selectionLogic: selectionLogic,
     112             :       selection: selection,
     113             :       datePickerKeys: datePickerKeys,
     114           0 :       datePickerStyles: datePickerStyles ?? DatePickerStyles(),
     115             :       datePickerLayoutSettings: datePickerLayoutSettings,
     116             :     );
     117             :   }
     118             : 
     119             :   /// The currently selected date or dates.
     120             :   ///
     121             :   /// This date or dates are highlighted in the picker.
     122             :   final MonthPickerSelection selection;
     123             : 
     124             :   /// Called when the user picks a month.
     125             :   final ValueChanged<T> onChanged;
     126             : 
     127             :   /// The earliest date the user is permitted to pick.
     128             :   final DateTime firstDate;
     129             : 
     130             :   /// The latest date the user is permitted to pick.
     131             :   final DateTime lastDate;
     132             : 
     133             :   /// Layout settings what can be customized by user
     134             :   final DatePickerLayoutSettings datePickerLayoutSettings;
     135             : 
     136             :   /// Some keys useful for integration tests
     137             :   final DatePickerKeys? datePickerKeys;
     138             : 
     139             :   /// Styles what can be customized by user
     140             :   final DatePickerStyles datePickerStyles;
     141             : 
     142             :   /// Logic to handle user's selections.
     143             :   final ISelectablePicker<T> selectionLogic;
     144             : 
     145           0 :   @override
     146           0 :   State<StatefulWidget> createState() => _MonthPickerState<T>();
     147             : }
     148             : 
     149             : class _MonthPickerState<T extends Object> extends State<MonthPicker<T>> {
     150             :   PageController _monthPickerController = PageController();
     151             : 
     152             :   Locale locale = _defaultLocale;
     153             :   MaterialLocalizations localizations = _defaultLocalizations;
     154             : 
     155             :   TextDirection textDirection = TextDirection.ltr;
     156             : 
     157             :   DateTime _todayDate = DateTime.now();
     158             :   DateTime _previousYearDate = DateTime(DateTime.now().year - 1);
     159             :   DateTime _nextYearDate = DateTime(DateTime.now().year + 1);
     160             : 
     161             :   DateTime _currentDisplayedYearDate = DateTime.now();
     162             : 
     163             :   Timer? _timer;
     164             :   StreamSubscription<T>? _changesSubscription;
     165             : 
     166             :   /// True if the earliest allowable year is displayed.
     167           0 :   bool get _isDisplayingFirstYear =>
     168           0 :       !_currentDisplayedYearDate.isAfter(DateTime(widget.firstDate.year));
     169             : 
     170             :   /// True if the latest allowable year is displayed.
     171           0 :   bool get _isDisplayingLastYear =>
     172           0 :       !_currentDisplayedYearDate.isBefore(DateTime(widget.lastDate.year));
     173             : 
     174           0 :   @override
     175             :   void initState() {
     176           0 :     super.initState();
     177           0 :     _initWidgetData();
     178           0 :     _updateCurrentDate();
     179             :   }
     180             : 
     181           0 :   @override
     182             :   void didUpdateWidget(MonthPicker<T> oldWidget) {
     183           0 :     super.didUpdateWidget(oldWidget);
     184           0 :     if (widget.selection != oldWidget.selection ||
     185           0 :         widget.selectionLogic != oldWidget.selectionLogic) {
     186           0 :       _initWidgetData();
     187             :     }
     188             :   }
     189             : 
     190           0 :   @override
     191             :   void didChangeDependencies() {
     192           0 :     super.didChangeDependencies();
     193             : 
     194             :     try {
     195           0 :       locale = Localizations.localeOf(context);
     196             : 
     197             :       MaterialLocalizations? curLocalizations =
     198           0 :           Localizations.of<MaterialLocalizations>(
     199           0 :               context, MaterialLocalizations);
     200           0 :       if (curLocalizations != null && localizations != curLocalizations) {
     201           0 :         localizations = curLocalizations;
     202             :       }
     203             : 
     204           0 :       textDirection = Directionality.of(context);
     205             : 
     206             :       // No MaterialLocalizations or Directionality or Locale was found
     207             :       // and ".of" method throws error
     208             :       // trying to cast null to MaterialLocalizations.
     209           0 :     } on TypeError catch (_) {}
     210             :   }
     211             : 
     212           0 :   @override
     213             :   Widget build(BuildContext context) {
     214             :     int yearsCount =
     215           0 :         DatePickerUtils.yearDelta(widget.firstDate, widget.lastDate) + 1;
     216             : 
     217           0 :     return SizedBox(
     218           0 :       width: widget.datePickerLayoutSettings.monthPickerPortraitWidth,
     219           0 :       height: widget.datePickerLayoutSettings.maxDayPickerHeight,
     220           0 :       child: Stack(
     221           0 :         children: <Widget>[
     222           0 :           Semantics(
     223             :             sortKey: YearPickerSortKey.calendar,
     224           0 :             child: PageView.builder(
     225             :               // key: ValueKey<DateTime>(widget.selection),
     226           0 :               controller: _monthPickerController,
     227             :               scrollDirection: Axis.horizontal,
     228             :               itemCount: yearsCount,
     229           0 :               itemBuilder: _buildItems,
     230           0 :               onPageChanged: _handleYearPageChanged,
     231             :             ),
     232             :           ),
     233           0 :           PositionedDirectional(
     234             :             top: 0.0,
     235             :             start: 8.0,
     236           0 :             child: Semantics(
     237             :               sortKey: YearPickerSortKey.previousYear,
     238           0 :               child: IconButton(
     239           0 :                 key: widget.datePickerKeys?.previousPageIconKey,
     240           0 :                 icon: widget.datePickerStyles.prevIcon,
     241           0 :                 tooltip: _isDisplayingFirstYear
     242             :                     ? null
     243           0 :                     : '${localizations.formatYear(_previousYearDate)}',
     244           0 :                 onPressed: _isDisplayingFirstYear ? null : _handlePreviousYear,
     245             :               ),
     246             :             ),
     247             :           ),
     248           0 :           PositionedDirectional(
     249             :             top: 0.0,
     250             :             end: 8.0,
     251           0 :             child: Semantics(
     252             :               sortKey: YearPickerSortKey.nextYear,
     253           0 :               child: IconButton(
     254           0 :                 key: widget.datePickerKeys?.nextPageIconKey,
     255           0 :                 icon: widget.datePickerStyles.nextIcon,
     256           0 :                 tooltip: _isDisplayingLastYear
     257             :                     ? null
     258           0 :                     : '${localizations.formatYear(_nextYearDate)}',
     259           0 :                 onPressed: _isDisplayingLastYear ? null : _handleNextYear,
     260             :               ),
     261             :             ),
     262             :           ),
     263             :         ],
     264             :       ),
     265             :     );
     266             :   }
     267             : 
     268           0 :   @override
     269             :   void dispose() {
     270           0 :     _timer?.cancel();
     271           0 :     _changesSubscription?.cancel();
     272           0 :     super.dispose();
     273             :   }
     274             : 
     275           0 :   void _initWidgetData() {
     276             :     final initiallyShowDate =
     277           0 :         widget.selection.isEmpty ? DateTime.now() : widget.selection.earliest;
     278             : 
     279             :     // Initially display the pre-selected date.
     280             :     final int yearPage =
     281           0 :         DatePickerUtils.yearDelta(widget.firstDate, initiallyShowDate);
     282             : 
     283           0 :     _changesSubscription?.cancel();
     284           0 :     _changesSubscription = widget.selectionLogic.onUpdate
     285           0 :         .listen((newSelectedDate) => widget.onChanged(newSelectedDate))
     286           0 :       ..onError((e) => print(e.toString()));
     287             : 
     288           0 :     _monthPickerController.dispose();
     289           0 :     _monthPickerController = PageController(initialPage: yearPage);
     290           0 :     _handleYearPageChanged(yearPage);
     291             :   }
     292             : 
     293           0 :   void _updateCurrentDate() {
     294           0 :     _todayDate = DateTime.now();
     295             :     final DateTime tomorrow =
     296           0 :         DateTime(_todayDate.year, _todayDate.month, _todayDate.day + 1);
     297           0 :     Duration timeUntilTomorrow = tomorrow.difference(_todayDate);
     298           0 :     timeUntilTomorrow +=
     299             :         const Duration(seconds: 1); // so we don't miss it by rounding
     300           0 :     _timer?.cancel();
     301           0 :     _timer = Timer(timeUntilTomorrow, () {
     302           0 :       setState(_updateCurrentDate);
     303             :     });
     304             :   }
     305             : 
     306             :   /// Add years to a year truncated date.
     307           0 :   DateTime _addYearsToYearDate(DateTime yearDate, int yearsToAdd) =>
     308           0 :       DateTime(yearDate.year + yearsToAdd);
     309             : 
     310           0 :   Widget _buildItems(BuildContext context, int index) {
     311           0 :     final DateTime year = _addYearsToYearDate(widget.firstDate, index);
     312             : 
     313           0 :     final ThemeData theme = Theme.of(context);
     314           0 :     DatePickerStyles styles = widget.datePickerStyles;
     315           0 :     styles = styles.fulfillWithTheme(theme);
     316             : 
     317           0 :     return _MonthPicker<T>(
     318           0 :       key: ValueKey<DateTime>(year),
     319           0 :       currentDate: _todayDate,
     320           0 :       onChanged: widget.onChanged,
     321           0 :       firstDate: widget.firstDate,
     322           0 :       lastDate: widget.lastDate,
     323           0 :       datePickerLayoutSettings: widget.datePickerLayoutSettings,
     324             :       displayedYear: year,
     325           0 :       selectedPeriodKey: widget.datePickerKeys?.selectedPeriodKeys,
     326             :       datePickerStyles: styles,
     327           0 :       locale: locale,
     328           0 :       localizations: localizations,
     329           0 :       selectionLogic: widget.selectionLogic,
     330             :     );
     331             :   }
     332             : 
     333           0 :   void _handleNextYear() {
     334           0 :     if (!_isDisplayingLastYear) {
     335           0 :       String yearStr = localizations.formatYear(_nextYearDate);
     336           0 :       SemanticsService.announce(yearStr, textDirection);
     337           0 :       _monthPickerController.nextPage(
     338           0 :           duration: widget.datePickerLayoutSettings.pagesScrollDuration,
     339             :           curve: Curves.ease);
     340             :     }
     341             :   }
     342             : 
     343           0 :   void _handlePreviousYear() {
     344           0 :     if (!_isDisplayingFirstYear) {
     345           0 :       String yearStr = localizations.formatYear(_previousYearDate);
     346           0 :       SemanticsService.announce(yearStr, textDirection);
     347           0 :       _monthPickerController.previousPage(
     348           0 :           duration: widget.datePickerLayoutSettings.pagesScrollDuration,
     349             :           curve: Curves.ease);
     350             :     }
     351             :   }
     352             : 
     353           0 :   void _handleYearPageChanged(int yearPage) {
     354           0 :     setState(() {
     355           0 :       _previousYearDate = _addYearsToYearDate(widget.firstDate, yearPage - 1);
     356           0 :       _currentDisplayedYearDate =
     357           0 :           _addYearsToYearDate(widget.firstDate, yearPage);
     358           0 :       _nextYearDate = _addYearsToYearDate(widget.firstDate, yearPage + 1);
     359             :     });
     360             :   }
     361             : 
     362           0 :   static MaterialLocalizations get _defaultLocalizations =>
     363           0 :       MaterialLocalizationEn(
     364             :         twoDigitZeroPaddedFormat:
     365           0 :             intl.NumberFormat('00', _defaultLocale.toString()),
     366           0 :         fullYearFormat: intl.DateFormat.y(_defaultLocale.toString()),
     367           0 :         longDateFormat: intl.DateFormat.yMMMMEEEEd(_defaultLocale.toString()),
     368           0 :         shortMonthDayFormat: intl.DateFormat.MMMd(_defaultLocale.toString()),
     369             :         decimalFormat:
     370           0 :             intl.NumberFormat.decimalPattern(_defaultLocale.toString()),
     371           0 :         shortDateFormat: intl.DateFormat.yMMMd(_defaultLocale.toString()),
     372           0 :         mediumDateFormat: intl.DateFormat.MMMEd(_defaultLocale.toString()),
     373           0 :         compactDateFormat: intl.DateFormat.yMd(_defaultLocale.toString()),
     374           0 :         yearMonthFormat: intl.DateFormat.yMMMM(_defaultLocale.toString()),
     375             :       );
     376             : }
     377             : 
     378             : class _MonthPicker<T> extends StatelessWidget {
     379             :   /// The month whose days are displayed by this picker.
     380             :   final DateTime displayedYear;
     381             : 
     382             :   /// The earliest date the user is permitted to pick.
     383             :   final DateTime firstDate;
     384             : 
     385             :   /// The latest date the user is permitted to pick.
     386             :   final DateTime lastDate;
     387             : 
     388             :   /// The current date at the time the picker is displayed.
     389             :   final DateTime currentDate;
     390             : 
     391             :   /// Layout settings what can be customized by user
     392             :   final DatePickerLayoutSettings datePickerLayoutSettings;
     393             : 
     394             :   /// Called when the user picks a day.
     395             :   final ValueChanged<T> onChanged;
     396             : 
     397             :   ///  Key fo selected month (useful for integration tests)
     398             :   final Key? selectedPeriodKey;
     399             : 
     400             :   /// Styles what can be customized by user
     401             :   final DatePickerStyles datePickerStyles;
     402             : 
     403             :   final MaterialLocalizations localizations;
     404             : 
     405             :   final ISelectablePicker<T> selectionLogic;
     406             : 
     407             :   final Locale locale;
     408             : 
     409           0 :   _MonthPicker(
     410             :       {required this.displayedYear,
     411             :       required this.firstDate,
     412             :       required this.lastDate,
     413             :       required this.currentDate,
     414             :       required this.onChanged,
     415             :       required this.datePickerLayoutSettings,
     416             :       required this.datePickerStyles,
     417             :       required this.selectionLogic,
     418             :       required this.localizations,
     419             :       required this.locale,
     420             :       this.selectedPeriodKey,
     421             :       Key? key})
     422           0 :       : assert(!firstDate.isAfter(lastDate)),
     423           0 :         super(key: key);
     424             : 
     425           0 :   @override
     426             :   Widget build(BuildContext context) {
     427             :     final int monthsInYear = 12;
     428           0 :     final int year = displayedYear.year;
     429             :     final int day = 1;
     430             : 
     431           0 :     final List<Widget> labels = <Widget>[];
     432             : 
     433           0 :     for (int month = 1; month <= monthsInYear; month += 1) {
     434           0 :       DateTime monthToBuild = DateTime(year, month, day);
     435           0 :       DayType monthType = selectionLogic.getDayType(monthToBuild);
     436             : 
     437           0 :       Widget monthWidget = _MonthCell(
     438             :         monthToBuild: monthToBuild,
     439           0 :         currentDate: currentDate,
     440           0 :         selectionLogic: selectionLogic,
     441           0 :         datePickerStyles: datePickerStyles,
     442           0 :         localizations: localizations,
     443           0 :         locale: locale,
     444             :       );
     445             : 
     446           0 :       if (monthType != DayType.disabled) {
     447           0 :         monthWidget = GestureDetector(
     448             :           behavior: HitTestBehavior.opaque,
     449           0 :           onTap: () {
     450           0 :             DatePickerUtils.sameMonth(firstDate, monthToBuild)
     451           0 :                 ? selectionLogic.onDayTapped(firstDate)
     452           0 :                 : selectionLogic.onDayTapped(monthToBuild);
     453             :           },
     454             :           child: monthWidget,
     455             :         );
     456             :       }
     457             : 
     458           0 :       labels.add(monthWidget);
     459             :     }
     460             : 
     461           0 :     return Padding(
     462             :       padding: const EdgeInsets.symmetric(horizontal: 8.0),
     463           0 :       child: Column(
     464           0 :         children: <Widget>[
     465           0 :           Container(
     466           0 :             height: datePickerLayoutSettings.dayPickerRowHeight,
     467           0 :             child: Center(
     468           0 :               child: ExcludeSemantics(
     469           0 :                 child: Text(
     470           0 :                   localizations.formatYear(displayedYear),
     471           0 :                   key: selectedPeriodKey,
     472           0 :                   style: datePickerStyles.displayedPeriodTitle,
     473             :                 ),
     474             :               ),
     475             :             ),
     476             :           ),
     477           0 :           Flexible(
     478           0 :             child: GridView.count(
     479           0 :               physics: datePickerLayoutSettings.scrollPhysics,
     480             :               crossAxisCount: 4,
     481             :               children: labels,
     482             :             ),
     483             :           ),
     484             :         ],
     485             :       ),
     486             :     );
     487             :   }
     488             : }
     489             : 
     490             : class _MonthCell<T> extends StatelessWidget {
     491             :   /// Styles what can be customized by user
     492             :   final DatePickerStyles datePickerStyles;
     493             :   final Locale locale;
     494             :   final MaterialLocalizations localizations;
     495             :   final ISelectablePicker<T> selectionLogic;
     496             :   final DateTime monthToBuild;
     497             : 
     498             :   /// The current date at the time the picker is displayed.
     499             :   final DateTime currentDate;
     500             : 
     501           0 :   const _MonthCell({
     502             :     required this.monthToBuild,
     503             :     required this.currentDate,
     504             :     required this.selectionLogic,
     505             :     required this.datePickerStyles,
     506             :     required this.locale,
     507             :     required this.localizations,
     508             :     Key? key,
     509           0 :   }) : super(key: key);
     510             : 
     511           0 :   @override
     512             :   Widget build(BuildContext context) {
     513           0 :     DayType monthType = selectionLogic.getDayType(monthToBuild);
     514             : 
     515             :     BoxDecoration? decoration;
     516             :     TextStyle? itemStyle;
     517             : 
     518           0 :     if (monthType != DayType.disabled && monthType != DayType.notSelected) {
     519           0 :       itemStyle = datePickerStyles.selectedDateStyle;
     520           0 :       decoration = datePickerStyles.selectedSingleDateDecoration;
     521           0 :     } else if (monthType == DayType.disabled) {
     522           0 :       itemStyle = datePickerStyles.disabledDateStyle;
     523           0 :     } else if (DatePickerUtils.sameMonth(currentDate, monthToBuild)) {
     524           0 :       itemStyle = datePickerStyles.currentDateStyle;
     525             :     } else {
     526           0 :       itemStyle = datePickerStyles.defaultDateTextStyle;
     527             :     }
     528             : 
     529           0 :     String semanticLabel =
     530           0 :         '${localizations.formatDecimal(monthToBuild.month)}, '
     531           0 :         '${localizations.formatFullDate(monthToBuild)}';
     532             : 
     533             :     bool isSelectedMonth =
     534           0 :         monthType != DayType.disabled && monthType != DayType.notSelected;
     535             : 
     536           0 :     String monthStr = _getMonthStr(monthToBuild);
     537             : 
     538           0 :     Widget monthWidget = Container(
     539             :       decoration: decoration,
     540           0 :       child: Center(
     541           0 :         child: Semantics(
     542             :           // We want the day of month to be spoken first irrespective of the
     543             :           // locale-specific preferences or TextDirection. This is because
     544             :           // an accessibility user is more likely to be interested in the
     545             :           // day of month before the rest of the date, as they are looking
     546             :           // for the day of month. To do that we prepend day of month to the
     547             :           // formatted full date.
     548             :           label: semanticLabel,
     549             :           selected: isSelectedMonth,
     550           0 :           child: ExcludeSemantics(
     551           0 :             child: Text(monthStr, style: itemStyle),
     552             :           ),
     553             :         ),
     554             :       ),
     555             :     );
     556             : 
     557             :     return monthWidget;
     558             :   }
     559             : 
     560             :   // Returns only month made with intl.DateFormat.MMM() for current [locale].
     561             :   // We can'r use [localizations] here because MaterialLocalizations doesn't
     562             :   // provide short month string.
     563           0 :   String _getMonthStr(DateTime date) {
     564           0 :     String month = intl.DateFormat.MMM(locale.toString()).format(date);
     565             :     return month;
     566             :   }
     567             : }

Generated by: LCOV version 1.15