Improve the year selector UI in the date picker (#11214)
Fixes https://github.com/flutter/flutter/issues/10917
This commit is contained in:
parent
6ac0f61234
commit
e967b4b3c5
@ -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),
|
||||
children: <Widget>[yearButton, dayButton],
|
||||
),
|
||||
new GestureDetector(
|
||||
onTap: Feedback.wrapForTap(() => _handleChangeMode(_DatePickerMode.day), context),
|
||||
child: new Text(new DateFormat('E, MMM\u00a0d').format(selectedDate), style: dayStyle),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,11 +346,14 @@ class DayPicker extends StatelessWidget {
|
||||
new Container(
|
||||
height: _kDayPickerRowHeight,
|
||||
child: new Center(
|
||||
child: new GestureDetector(
|
||||
onTap: onMonthHeaderTap != null ? Feedback.wrapForTap(onMonthHeaderTap, context) : null,
|
||||
child: new Text(new DateFormat('yMMMM').format(displayedMonth),
|
||||
style: themeData.textTheme.subhead,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
new Flexible(
|
||||
child: new GridView.custom(
|
||||
gridDelegate: _kDayPickerGridDelegate,
|
||||
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user