Exposed the YearPicker as a public API again to match the previous API. (#71061)
diff --git a/packages/flutter/lib/src/material/calendar_date_picker.dart b/packages/flutter/lib/src/material/calendar_date_picker.dart index 28eb6ea..d58055d 100644 --- a/packages/flutter/lib/src/material/calendar_date_picker.dart +++ b/packages/flutter/lib/src/material/calendar_date_picker.dart
@@ -278,7 +278,7 @@ case DatePickerMode.year: return Padding( padding: const EdgeInsets.only(top: _subHeaderHeight), - child: _YearPicker( + child: YearPicker( key: _yearPickerKey, currentDate: widget.currentDate, firstDate: widget.firstDate, @@ -1095,12 +1095,12 @@ /// * [showDatePicker], which shows a dialog containing a Material Design /// date picker. /// -class _YearPicker extends StatefulWidget { +class YearPicker extends StatefulWidget { /// Creates a year picker. /// /// The [firstDate], [lastDate], [selectedDate], and [onChanged] /// arguments must be non-null. The [lastDate] must be after the [firstDate]. - _YearPicker({ + YearPicker({ Key? key, DateTime? currentDate, required this.firstDate, @@ -1147,7 +1147,7 @@ _YearPickerState createState() => _YearPickerState(); } -class _YearPickerState extends State<_YearPicker> { +class _YearPickerState extends State<YearPicker> { late ScrollController _scrollController; // The approximate number of years necessary to fill the available space. @@ -1160,7 +1160,7 @@ } @override - void didUpdateWidget(_YearPicker oldWidget) { + void didUpdateWidget(YearPicker oldWidget) { super.didUpdateWidget(oldWidget); if (widget.selectedDate != oldWidget.selectedDate) { _scrollController.jumpTo(_scrollOffsetForYear(widget.selectedDate));
diff --git a/packages/flutter/lib/src/material/date_picker_deprecated.dart b/packages/flutter/lib/src/material/date_picker_deprecated.dart index b244596..48d56ef 100644 --- a/packages/flutter/lib/src/material/date_picker_deprecated.dart +++ b/packages/flutter/lib/src/material/date_picker_deprecated.dart
@@ -10,11 +10,8 @@ import 'package:flutter/widgets.dart'; import 'date.dart'; -import 'debug.dart'; import 'icon_button.dart'; import 'icons.dart'; -import 'ink_well.dart'; -import 'material.dart'; import 'material_localizations.dart'; import 'theme.dart'; @@ -609,108 +606,3 @@ static const _MonthPickerSortKey nextMonth = _MonthPickerSortKey(2.0); static const _MonthPickerSortKey calendar = _MonthPickerSortKey(3.0); } - -/// A scrollable list of years to allow picking a year. -/// -/// The year picker widget is rarely used directly. Instead, consider using -/// [showDatePicker], which creates a date picker dialog. -/// -/// Requires one of its ancestors to be a [Material] widget. -/// -/// See also: -/// -/// * [showDatePicker], which shows a dialog that contains a material design -/// date picker. -/// * [showTimePicker], which shows a dialog that contains a material design -/// time picker. -/// -@Deprecated( - 'Use CalendarDatePicker instead. ' - 'This feature was deprecated after v1.15.3.' -) -class YearPicker extends StatefulWidget { - /// Creates a year picker. - /// - /// The [selectedDate] and [onChanged] arguments must not be null. The - /// [lastDate] must be after the [firstDate]. - /// - /// Rarely used directly. Instead, typically used as part of the dialog shown - /// by [showDatePicker]. - YearPicker({ - Key? key, - required this.selectedDate, - required this.onChanged, - required this.firstDate, - required this.lastDate, - this.dragStartBehavior = DragStartBehavior.start, - }) : assert(selectedDate != null), - assert(onChanged != null), - assert(!firstDate.isAfter(lastDate)), - super(key: key); - - /// The currently selected date. - /// - /// This date is highlighted in the picker. - final DateTime selectedDate; - - /// Called when the user picks a year. - final ValueChanged<DateTime> onChanged; - - /// The earliest date the user is permitted to pick. - final DateTime firstDate; - - /// The latest date the user is permitted to pick. - final DateTime lastDate; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - @override - _YearPickerState createState() => _YearPickerState(); -} - -class _YearPickerState extends State<YearPicker> { - static const double _itemExtent = 50.0; - late ScrollController scrollController; - - @override - void initState() { - super.initState(); - scrollController = ScrollController( - // Move the initial scroll position to the currently selected date's year. - initialScrollOffset: (widget.selectedDate.year - widget.firstDate.year) * _itemExtent, - ); - } - - @override - Widget build(BuildContext context) { - assert(debugCheckHasMaterial(context)); - final ThemeData themeData = Theme.of(context); - final TextStyle? style = themeData.textTheme.bodyText2; - return ListView.builder( - dragStartBehavior: widget.dragStartBehavior, - controller: scrollController, - itemExtent: _itemExtent, - itemCount: widget.lastDate.year - widget.firstDate.year + 1, - itemBuilder: (BuildContext context, int index) { - final int year = widget.firstDate.year + index; - final bool isSelected = year == widget.selectedDate.year; - final TextStyle? itemStyle = isSelected - ? themeData.textTheme.headline5!.copyWith(color: themeData.accentColor) - : style; - return InkWell( - key: ValueKey<int>(year), - onTap: () { - widget.onChanged(DateTime(year, widget.selectedDate.month, widget.selectedDate.day)); - }, - child: Center( - child: Semantics( - selected: isSelected, - child: Text(year.toString(), style: itemStyle), - ), - ), - ); - }, - ); - } -}
diff --git a/packages/flutter/test/material/calendar_date_picker_test.dart b/packages/flutter/test/material/calendar_date_picker_test.dart index 78c80b7..4c2fdaf 100644 --- a/packages/flutter/test/material/calendar_date_picker_test.dart +++ b/packages/flutter/test/material/calendar_date_picker_test.dart
@@ -47,6 +47,34 @@ ); } + Widget yearPicker({ + Key? key, + DateTime? selectedDate, + DateTime? initialDate, + DateTime? firstDate, + DateTime? lastDate, + DateTime? currentDate, + ValueChanged<DateTime>? onChanged, + TextDirection textDirection = TextDirection.ltr, + }) { + return MaterialApp( + home: Material( + child: Directionality( + textDirection: textDirection, + child: YearPicker( + key: key, + selectedDate: selectedDate ?? DateTime(2016, DateTime.january, 15), + initialDate: initialDate ?? DateTime(2016, DateTime.january, 15), + firstDate: firstDate ?? DateTime(2001, DateTime.january, 1), + lastDate: lastDate ?? DateTime(2031, DateTime.december, 31), + currentDate: currentDate ?? DateTime(2016, DateTime.january, 3), + onChanged: onChanged ?? (DateTime date) {}, + ), + ), + ), + ); + } + group('CalendarDatePicker', () { testWidgets('Can select a day', (WidgetTester tester) async { DateTime? selectedDate; @@ -163,13 +191,6 @@ expect(selectedDate, equals(DateTime(2017, DateTime.january, 19))); }); - testWidgets('Current year is visible in year picker', (WidgetTester tester) async { - await tester.pumpWidget(calendarDatePicker()); - await tester.tap(find.text('January 2016')); // Switch to year mode. - await tester.pumpAndSettle(); - expect(find.text('2016'), findsOneWidget); - }); - testWidgets('Cannot select a day outside bounds', (WidgetTester tester) async { final DateTime validDate = DateTime(2017, DateTime.january, 15); DateTime? selectedDate; @@ -193,8 +214,7 @@ expect(selectedDate, validDate); }); - testWidgets('Cannot navigate to a month outside bounds', ( - WidgetTester tester) async { + testWidgets('Cannot navigate to a month outside bounds', (WidgetTester tester) async { DateTime? displayedMonth; await tester.pumpWidget(calendarDatePicker( firstDate: DateTime(2016, DateTime.december, 15), @@ -832,4 +852,41 @@ }); }); + + group('YearPicker', () { + testWidgets('Current year is visible in year picker', (WidgetTester tester) async { + await tester.pumpWidget(yearPicker()); + expect(find.text('2016'), findsOneWidget); + }); + + testWidgets('Can select a year', (WidgetTester tester) async { + DateTime? selectedDate; + await tester.pumpWidget(yearPicker( + onChanged: (DateTime date) => selectedDate = date, + )); + await tester.pumpAndSettle(); + await tester.tap(find.text('2018')); + await tester.pumpAndSettle(); + expect(selectedDate, equals(DateTime(2018, DateTime.january, 1))); + }); + + testWidgets('Cannot select disabled year', (WidgetTester tester) async { + DateTime? selectedYear; + await tester.pumpWidget(yearPicker( + firstDate: DateTime(2018, DateTime.june, 9), + initialDate: DateTime(2018, DateTime.july, 4), + lastDate: DateTime(2018, DateTime.december, 15), + onChanged: (DateTime date) => selectedYear = date, + )); + await tester.tap(find.text('2016')); // Disabled, doesn't change the year. + await tester.pumpAndSettle(); + expect(selectedYear, isNull); + await tester.tap(find.text('2020')); // Disabled, doesn't change the year. + await tester.pumpAndSettle(); + expect(selectedYear, isNull); + await tester.tap(find.text('2018')); + await tester.pumpAndSettle(); + expect(selectedYear, equals(DateTime(2018, DateTime.july, 1))); + }); + }); }