[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
This commit is contained in:
parent
3b8159564f
commit
4cb3a98f2c
@ -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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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> {
|
|||||||
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> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
|||||||
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> {
|
|||||||
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> {
|
|||||||
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);
|
|||||||
/// 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({
|
|||||||
@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({
|
|||||||
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({
|
|||||||
firstDate: firstDate,
|
firstDate: firstDate,
|
||||||
lastDate: lastDate,
|
lastDate: lastDate,
|
||||||
selectableDayPredicate: selectableDayPredicate,
|
selectableDayPredicate: selectableDayPredicate,
|
||||||
|
initialDatePickerMode: initialDatePickerMode,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,12 +141,24 @@ 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
|
||||||
|
// Exercise the argument default for initialDatePickerMode.
|
||||||
|
?
|
||||||
|
showDatePicker(
|
||||||
context: buttonContext,
|
context: buttonContext,
|
||||||
initialDate: initialDate,
|
initialDate: initialDate,
|
||||||
firstDate: firstDate,
|
firstDate: firstDate,
|
||||||
lastDate: lastDate,
|
lastDate: lastDate,
|
||||||
selectableDayPredicate: selectableDayPredicate
|
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));
|
||||||
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user