Commit 4cb3a98f authored by xster's avatar xster Committed by GitHub

[Discuss] Let the initial date picker mode be selectable (#11614)

* Let the initial date picker mode be selectable

* Doc for enum

* Add a test

* Add a comment for test

* actually decouple test from setup state
parent 3b815956
...@@ -26,7 +26,18 @@ import 'material.dart'; ...@@ -26,7 +26,18 @@ import 'material.dart';
import 'theme.dart'; import 'theme.dart';
import 'typography.dart'; import 'typography.dart';
enum _DatePickerMode { day, year } /// Date picker UI mode for either showing a list of available years or a
/// monthly calendar.
///
/// Also see:
///
/// * <https://material.io/guidelines/components/pickers.html#pickers-date-pickers>
enum DatePickerMode {
/// Show a date picker UI for choosing a month and day.
day,
/// Show a date picker UI for choosing a year.
year,
}
const double _kDatePickerHeaderPortraitHeight = 100.0; const double _kDatePickerHeaderPortraitHeight = 100.0;
const double _kDatePickerHeaderLandscapeWidth = 168.0; const double _kDatePickerHeaderLandscapeWidth = 168.0;
...@@ -57,11 +68,11 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -57,11 +68,11 @@ class _DatePickerHeader extends StatelessWidget {
super(key: key); super(key: key);
final DateTime selectedDate; final DateTime selectedDate;
final _DatePickerMode mode; final DatePickerMode mode;
final ValueChanged<_DatePickerMode> onModeChanged; final ValueChanged<DatePickerMode> onModeChanged;
final Orientation orientation; final Orientation orientation;
void _handleChangeMode(_DatePickerMode value) { void _handleChangeMode(DatePickerMode value) {
if (value != mode) if (value != mode)
onModeChanged(value); onModeChanged(value);
} }
...@@ -74,12 +85,12 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -74,12 +85,12 @@ class _DatePickerHeader extends StatelessWidget {
Color yearColor; Color yearColor;
switch(themeData.primaryColorBrightness) { switch(themeData.primaryColorBrightness) {
case Brightness.light: case Brightness.light:
dayColor = mode == _DatePickerMode.day ? Colors.black87 : Colors.black54; dayColor = mode == DatePickerMode.day ? Colors.black87 : Colors.black54;
yearColor = mode == _DatePickerMode.year ? Colors.black87 : Colors.black54; yearColor = mode == DatePickerMode.year ? Colors.black87 : Colors.black54;
break; break;
case Brightness.dark: case Brightness.dark:
dayColor = mode == _DatePickerMode.day ? Colors.white : Colors.white70; dayColor = mode == DatePickerMode.day ? Colors.white : Colors.white70;
yearColor = mode == _DatePickerMode.year ? Colors.white : Colors.white70; yearColor = mode == DatePickerMode.year ? Colors.white : Colors.white70;
break; break;
} }
final TextStyle dayStyle = headerTextTheme.display1.copyWith(color: dayColor, height: 1.4); final TextStyle dayStyle = headerTextTheme.display1.copyWith(color: dayColor, height: 1.4);
...@@ -114,17 +125,17 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -114,17 +125,17 @@ class _DatePickerHeader extends StatelessWidget {
Widget yearButton = new _DateHeaderButton( Widget yearButton = new _DateHeaderButton(
color: backgroundColor, color: backgroundColor,
onTap: Feedback.wrapForTap(() => _handleChangeMode(_DatePickerMode.year), context), onTap: Feedback.wrapForTap(() => _handleChangeMode(DatePickerMode.year), context),
child: new Text(new DateFormat('yyyy').format(selectedDate), style: yearStyle), child: new Text(new DateFormat('yyyy').format(selectedDate), style: yearStyle),
); );
Widget dayButton = new _DateHeaderButton( Widget dayButton = new _DateHeaderButton(
color: backgroundColor, color: backgroundColor,
onTap: Feedback.wrapForTap(() => _handleChangeMode(_DatePickerMode.day), context), onTap: Feedback.wrapForTap(() => _handleChangeMode(DatePickerMode.day), context),
child: new Text(new DateFormat('E, MMM\u00a0d').format(selectedDate), style: dayStyle), child: new Text(new DateFormat('E, MMM\u00a0d').format(selectedDate), style: dayStyle),
); );
// Disable the button for the current mode. // Disable the button for the current mode.
if (mode == _DatePickerMode.day) if (mode == DatePickerMode.day)
dayButton = new IgnorePointer(child: dayButton); dayButton = new IgnorePointer(child: dayButton);
else else
yearButton = new IgnorePointer(child: yearButton); yearButton = new IgnorePointer(child: yearButton);
...@@ -658,12 +669,14 @@ class _DatePickerDialog extends StatefulWidget { ...@@ -658,12 +669,14 @@ class _DatePickerDialog extends StatefulWidget {
this.firstDate, this.firstDate,
this.lastDate, this.lastDate,
this.selectableDayPredicate, this.selectableDayPredicate,
this.initialDatePickerMode,
}) : super(key: key); }) : super(key: key);
final DateTime initialDate; final DateTime initialDate;
final DateTime firstDate; final DateTime firstDate;
final DateTime lastDate; final DateTime lastDate;
final SelectableDayPredicate selectableDayPredicate; final SelectableDayPredicate selectableDayPredicate;
final DatePickerMode initialDatePickerMode;
@override @override
_DatePickerDialogState createState() => new _DatePickerDialogState(); _DatePickerDialogState createState() => new _DatePickerDialogState();
...@@ -674,10 +687,11 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -674,10 +687,11 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
void initState() { void initState() {
super.initState(); super.initState();
_selectedDate = widget.initialDate; _selectedDate = widget.initialDate;
_mode = widget.initialDatePickerMode;
} }
DateTime _selectedDate; DateTime _selectedDate;
_DatePickerMode _mode = _DatePickerMode.day; DatePickerMode _mode;
final GlobalKey _pickerKey = new GlobalKey(); final GlobalKey _pickerKey = new GlobalKey();
void _vibrate() { void _vibrate() {
...@@ -691,7 +705,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -691,7 +705,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
} }
} }
void _handleModeChanged(_DatePickerMode mode) { void _handleModeChanged(DatePickerMode mode) {
_vibrate(); _vibrate();
setState(() { setState(() {
_mode = mode; _mode = mode;
...@@ -701,7 +715,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -701,7 +715,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
void _handleYearChanged(DateTime value) { void _handleYearChanged(DateTime value) {
_vibrate(); _vibrate();
setState(() { setState(() {
_mode = _DatePickerMode.day; _mode = DatePickerMode.day;
_selectedDate = value; _selectedDate = value;
}); });
} }
...@@ -724,7 +738,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -724,7 +738,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
Widget _buildPicker() { Widget _buildPicker() {
assert(_mode != null); assert(_mode != null);
switch (_mode) { switch (_mode) {
case _DatePickerMode.day: case DatePickerMode.day:
return new MonthPicker( return new MonthPicker(
key: _pickerKey, key: _pickerKey,
selectedDate: _selectedDate, selectedDate: _selectedDate,
...@@ -732,9 +746,9 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -732,9 +746,9 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
firstDate: widget.firstDate, firstDate: widget.firstDate,
lastDate: widget.lastDate, lastDate: widget.lastDate,
selectableDayPredicate: widget.selectableDayPredicate, selectableDayPredicate: widget.selectableDayPredicate,
onMonthHeaderTap: () { _handleModeChanged(_DatePickerMode.year); }, onMonthHeaderTap: () { _handleModeChanged(DatePickerMode.year); },
); );
case _DatePickerMode.year: case DatePickerMode.year:
return new YearPicker( return new YearPicker(
key: _pickerKey, key: _pickerKey,
selectedDate: _selectedDate, selectedDate: _selectedDate,
...@@ -831,6 +845,10 @@ typedef bool SelectableDayPredicate(DateTime day); ...@@ -831,6 +845,10 @@ typedef bool SelectableDayPredicate(DateTime day);
/// the days to enable for selection. If provided, only the days that /// the days to enable for selection. If provided, only the days that
/// [selectableDayPredicate] returned true for will be selectable. /// [selectableDayPredicate] returned true for will be selectable.
/// ///
/// An optional [initialDatePickerMode] argument can be used to display the
/// date picker initially in the year or month+day picker mode. It defaults
/// to month+day, but cannot be null.
///
/// See also: /// See also:
/// ///
/// * [showTimePicker] /// * [showTimePicker]
...@@ -841,6 +859,7 @@ Future<DateTime> showDatePicker({ ...@@ -841,6 +859,7 @@ Future<DateTime> showDatePicker({
@required DateTime firstDate, @required DateTime firstDate,
@required DateTime lastDate, @required DateTime lastDate,
SelectableDayPredicate selectableDayPredicate, SelectableDayPredicate selectableDayPredicate,
DatePickerMode initialDatePickerMode: DatePickerMode.day,
}) async { }) async {
assert(!initialDate.isBefore(firstDate), 'initialDate must be on or after firstDate'); assert(!initialDate.isBefore(firstDate), 'initialDate must be on or after firstDate');
assert(!initialDate.isAfter(lastDate), 'initialDate must be on or before lastDate'); assert(!initialDate.isAfter(lastDate), 'initialDate must be on or before lastDate');
...@@ -849,6 +868,7 @@ Future<DateTime> showDatePicker({ ...@@ -849,6 +868,7 @@ Future<DateTime> showDatePicker({
selectableDayPredicate == null || selectableDayPredicate(initialDate), selectableDayPredicate == null || selectableDayPredicate(initialDate),
'Provided initialDate must satisfy provided selectableDayPredicate' 'Provided initialDate must satisfy provided selectableDayPredicate'
); );
assert(initialDatePickerMode != null, 'initialDatePickerMode cannot be null');
return await showDialog( return await showDialog(
context: context, context: context,
child: new _DatePickerDialog( child: new _DatePickerDialog(
...@@ -856,6 +876,7 @@ Future<DateTime> showDatePicker({ ...@@ -856,6 +876,7 @@ Future<DateTime> showDatePicker({
firstDate: firstDate, firstDate: firstDate,
lastDate: lastDate, lastDate: lastDate,
selectableDayPredicate: selectableDayPredicate, selectableDayPredicate: selectableDayPredicate,
initialDatePickerMode: initialDatePickerMode,
) )
); );
} }
...@@ -13,11 +13,14 @@ void main() { ...@@ -13,11 +13,14 @@ void main() {
DateTime lastDate; DateTime lastDate;
DateTime initialDate; DateTime initialDate;
SelectableDayPredicate selectableDayPredicate; SelectableDayPredicate selectableDayPredicate;
DatePickerMode initialDatePickerMode;
setUp(() { setUp(() {
firstDate = new DateTime(2001, DateTime.JANUARY, 1); firstDate = new DateTime(2001, DateTime.JANUARY, 1);
lastDate = new DateTime(2031, DateTime.DECEMBER, 31); lastDate = new DateTime(2031, DateTime.DECEMBER, 31);
initialDate = new DateTime(2016, DateTime.JANUARY, 15); initialDate = new DateTime(2016, DateTime.JANUARY, 15);
selectableDayPredicate = null;
initialDatePickerMode = null;
}); });
testWidgets('tap-select a day', (WidgetTester tester) async { testWidgets('tap-select a day', (WidgetTester tester) async {
...@@ -138,13 +141,25 @@ void main() { ...@@ -138,13 +141,25 @@ void main() {
await tester.tap(find.text('Go')); await tester.tap(find.text('Go'));
expect(buttonContext, isNotNull); expect(buttonContext, isNotNull);
final Future<DateTime> date = showDatePicker( final Future<DateTime> date = initialDatePickerMode == null
context: buttonContext, // Exercise the argument default for initialDatePickerMode.
initialDate: initialDate, ?
firstDate: firstDate, showDatePicker(
lastDate: lastDate, context: buttonContext,
selectableDayPredicate: selectableDayPredicate initialDate: initialDate,
); firstDate: firstDate,
lastDate: lastDate,
selectableDayPredicate: selectableDayPredicate,
)
:
showDatePicker(
context: buttonContext,
initialDate: initialDate,
firstDate: firstDate,
lastDate: lastDate,
selectableDayPredicate: selectableDayPredicate,
initialDatePickerMode: initialDatePickerMode,
);
await tester.pumpAndSettle(const Duration(seconds: 1)); await tester.pumpAndSettle(const Duration(seconds: 1));
await callback(date); await callback(date);
...@@ -283,6 +298,19 @@ void main() { ...@@ -283,6 +298,19 @@ void main() {
}); });
}); });
testWidgets('Can select initial date picker mode', (WidgetTester tester) async {
initialDate = new DateTime(2014, DateTime.JANUARY, 15);
initialDatePickerMode = DatePickerMode.year;
await preparePicker(tester, (Future<DateTime> date) async {
await tester.pump();
// 2018 wouldn't be available if the year picker wasn't showing.
// The initial current year is 2014.
await tester.tap(find.text('2018'));
await tester.tap(find.text('OK'));
expect(await date, equals(new DateTime(2018, DateTime.JANUARY, 15)));
});
});
group('haptic feedback', () { group('haptic feedback', () {
const Duration kHapticFeedbackInterval = const Duration(milliseconds: 10); const Duration kHapticFeedbackInterval = const Duration(milliseconds: 10);
FeedbackTester feedback; FeedbackTester feedback;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment