Commit e967b4b3 authored by Jason Simmons's avatar Jason Simmons Committed by GitHub

Improve the year selector UI in the date picker (#11214)

Fixes https://github.com/flutter/flutter/issues/10917
parent 6ac0f612
......@@ -22,6 +22,7 @@ import 'flat_button.dart';
import 'icon_button.dart';
import 'icons.dart';
import 'ink_well.dart';
import 'material.dart';
import 'theme.dart';
import 'typography.dart';
......@@ -101,16 +102,33 @@ class _DatePickerHeader extends StatelessWidget {
switch (orientation) {
case Orientation.portrait:
height = _kDatePickerHeaderPortraitHeight;
padding = const EdgeInsets.symmetric(horizontal: 24.0);
padding = const EdgeInsets.symmetric(horizontal: 16.0);
mainAxisAlignment = MainAxisAlignment.center;
break;
case Orientation.landscape:
width = _kDatePickerHeaderLandscapeWidth;
padding = const EdgeInsets.all(16.0);
padding = const EdgeInsets.all(8.0);
mainAxisAlignment = MainAxisAlignment.start;
break;
}
Widget yearButton = new _DateHeaderButton(
color: backgroundColor,
onTap: Feedback.wrapForTap(() => _handleChangeMode(_DatePickerMode.year), context),
child: new Text(new DateFormat('yyyy').format(selectedDate), style: yearStyle),
);
Widget dayButton = new _DateHeaderButton(
color: backgroundColor,
onTap: Feedback.wrapForTap(() => _handleChangeMode(_DatePickerMode.day), context),
child: new Text(new DateFormat('E, MMM\u00a0d').format(selectedDate), style: dayStyle),
);
// Disable the button for the current mode.
if (mode == _DatePickerMode.day)
dayButton = new IgnorePointer(child: dayButton);
else
yearButton = new IgnorePointer(child: yearButton);
return new Container(
width: width,
height: height,
......@@ -119,16 +137,40 @@ class _DatePickerHeader extends StatelessWidget {
child: new Column(
mainAxisAlignment: mainAxisAlignment,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new GestureDetector(
onTap: Feedback.wrapForTap(() => _handleChangeMode(_DatePickerMode.year), context),
child: new Text(new DateFormat('yyyy').format(selectedDate), style: yearStyle),
),
new GestureDetector(
onTap: Feedback.wrapForTap(() => _handleChangeMode(_DatePickerMode.day), context),
child: new Text(new DateFormat('E, MMM\u00a0d').format(selectedDate), style: dayStyle),
),
],
children: <Widget>[yearButton, dayButton],
),
);
}
}
class _DateHeaderButton extends StatelessWidget {
_DateHeaderButton({
Key key,
this.onTap,
this.color,
this.child,
}) : super(key: key);
final VoidCallback onTap;
final Color color;
final Widget child;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return new Material(
type: MaterialType.button,
color: color,
child: new InkWell(
borderRadius: kMaterialEdges[MaterialType.button],
highlightColor: theme.highlightColor,
splashColor: theme.splashColor,
onTap: onTap,
child: new Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: child,
),
),
);
}
......@@ -181,6 +223,7 @@ class DayPicker extends StatelessWidget {
@required this.firstDate,
@required this.lastDate,
@required this.displayedMonth,
this.onMonthHeaderTap,
this.selectableDayPredicate,
}) : assert(selectedDate != null),
assert(currentDate != null),
......@@ -201,6 +244,9 @@ class DayPicker extends StatelessWidget {
/// Called when the user picks a day.
final ValueChanged<DateTime> onChanged;
/// Called when the user taps on the header that displays the current month.
final VoidCallback onMonthHeaderTap;
/// The earliest date the user is permitted to pick.
final DateTime firstDate;
......@@ -300,8 +346,11 @@ class DayPicker extends StatelessWidget {
new Container(
height: _kDayPickerRowHeight,
child: new Center(
child: new Text(new DateFormat('yMMMM').format(displayedMonth),
style: themeData.textTheme.subhead,
child: new GestureDetector(
onTap: onMonthHeaderTap != null ? Feedback.wrapForTap(onMonthHeaderTap, context) : null,
child: new Text(new DateFormat('yMMMM').format(displayedMonth),
style: themeData.textTheme.subhead,
),
),
),
),
......@@ -341,6 +390,7 @@ class MonthPicker extends StatefulWidget {
@required this.firstDate,
@required this.lastDate,
this.selectableDayPredicate,
this.onMonthHeaderTap,
}) : assert(selectedDate != null),
assert(onChanged != null),
assert(!firstDate.isAfter(lastDate)),
......@@ -355,6 +405,9 @@ class MonthPicker extends StatefulWidget {
/// Called when the user picks a month.
final ValueChanged<DateTime> onChanged;
/// Called when the user taps on the header that displays the current month.
final VoidCallback onMonthHeaderTap;
/// The earliest date the user is permitted to pick.
final DateTime firstDate;
......@@ -426,6 +479,7 @@ class _MonthPickerState extends State<MonthPicker> {
lastDate: widget.lastDate,
displayedMonth: month,
selectableDayPredicate: widget.selectableDayPredicate,
onMonthHeaderTap: widget.onMonthHeaderTap,
);
}
......@@ -672,6 +726,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
firstDate: widget.firstDate,
lastDate: widget.lastDate,
selectableDayPredicate: widget.selectableDayPredicate,
onMonthHeaderTap: () { _handleModeChanged(_DatePickerMode.year); },
);
case _DatePickerMode.year:
return new YearPicker(
......
......@@ -337,6 +337,7 @@ void main() {
expect(feedback.hapticCount, 2);
});
});
});
test('days in month', () {
......@@ -347,4 +348,17 @@ void main() {
expect(DayPicker.getDaysInMonth(2000, 2), 29);
expect(DayPicker.getDaysInMonth(1900, 2), 28);
});
testWidgets('month header tap', (WidgetTester tester) async {
selectableDayPredicate = null;
await preparePicker(tester, (Future<DateTime> date) async {
// Switch into the year selector.
await tester.tap(find.text('January 2016'));
await tester.pump();
expect(find.text('2020'), isNotNull);
await tester.tap(find.text('CANCEL'));
expect(await date, isNull);
});
});
}
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