Commit a3b06f31 authored by Adam Barth's avatar Adam Barth Committed by Adam Barth

Switch date picker to using a custom Dialog

Rather than removing all the padding from an AlertDialog, we now just use
Dialog directly for the date picker.
parent 7f9c8c4b
......@@ -28,7 +28,6 @@ export 'src/material/constants.dart';
export 'src/material/data_table.dart';
export 'src/material/data_table_source.dart';
export 'src/material/date_picker.dart';
export 'src/material/date_picker_dialog.dart';
export 'src/material/dialog.dart';
export 'src/material/divider.dart';
export 'src/material/drawer.dart';
......
......@@ -10,10 +10,14 @@ import 'package:intl/date_symbols.dart';
import 'package:intl/intl.dart';
import 'package:meta/meta.dart';
import 'button_bar.dart';
import 'button.dart';
import 'colors.dart';
import 'debug.dart';
import 'icon.dart';
import 'dialog.dart';
import 'flat_button.dart';
import 'icon_button.dart';
import 'icon.dart';
import 'icons.dart';
import 'ink_well.dart';
import 'theme.dart';
......@@ -21,115 +25,14 @@ import 'typography.dart';
enum _DatePickerMode { day, year }
/// A material design date picker.
///
/// The date 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]
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
class DatePicker extends StatefulWidget {
/// Creates a date picker.
///
/// Rather than creating a date picker directly, consider using
/// [showDatePicker] to show a date picker in a dialog.
DatePicker({
Key key,
@required this.selectedDate,
@required this.onChanged,
@required this.firstDate,
@required this.lastDate
}) : super(key: key) {
assert(selectedDate != null);
assert(firstDate != null);
assert(lastDate != null);
}
/// The currently selected date.
///
/// This date is highlighted in the picker.
final DateTime selectedDate;
/// Called when the user picks a date.
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;
@override
_DatePickerState createState() => new _DatePickerState();
}
class _DatePickerState extends State<DatePicker> {
_DatePickerMode _mode = _DatePickerMode.day;
void _handleModeChanged(_DatePickerMode mode) {
HapticFeedback.vibrate();
setState(() {
_mode = mode;
});
}
void _handleYearChanged(DateTime dateTime) {
HapticFeedback.vibrate();
setState(() {
_mode = _DatePickerMode.day;
});
if (config.onChanged != null)
config.onChanged(dateTime);
}
void _handleDayChanged(DateTime dateTime) {
HapticFeedback.vibrate();
if (config.onChanged != null)
config.onChanged(dateTime);
}
@override
Widget build(BuildContext context) {
Widget header = new _DatePickerHeader(
selectedDate: config.selectedDate,
mode: _mode,
onModeChanged: _handleModeChanged
);
Widget picker;
switch (_mode) {
case _DatePickerMode.day:
picker = new MonthPicker(
selectedDate: config.selectedDate,
onChanged: _handleDayChanged,
firstDate: config.firstDate,
lastDate: config.lastDate
);
break;
case _DatePickerMode.year:
picker = new YearPicker(
selectedDate: config.selectedDate,
onChanged: _handleYearChanged,
firstDate: config.firstDate,
lastDate: config.lastDate
);
break;
}
return new BlockBody(
children: <Widget>[
header,
new Container(
height: _kMaxDayPickerHeight,
child: picker
)
]
);
}
const double _kDatePickerHeaderHeight = 100.0;
}
const Duration _kMonthScrollDuration = const Duration(milliseconds: 200);
const double _kDayPickerRowHeight = 42.0;
const int _kMaxDayPickerRowCount = 6; // A 31 day month that starts on Saturday.
// Two extra rows: one for the day-of-week header and one for the month header.
const double _kMaxDayPickerHeight = _kDayPickerRowHeight * (_kMaxDayPickerRowCount + 2);
const double _kMonthPickerWidth = 330.0;
// Shows the selected date in large font and toggles between year and day mode
class _DatePickerHeader extends StatelessWidget {
......@@ -182,7 +85,7 @@ class _DatePickerHeader extends StatelessWidget {
}
return new Container(
height: 100.0,
height: _kDatePickerHeaderHeight,
padding: const EdgeInsets.symmetric(horizontal: 24.0),
decoration: new BoxDecoration(backgroundColor: backgroundColor),
child: new Column(
......@@ -203,12 +106,6 @@ class _DatePickerHeader extends StatelessWidget {
}
}
const Duration _kMonthScrollDuration = const Duration(milliseconds: 200);
const double _kDayPickerRowHeight = 42.0;
const int _kMaxDayPickerRowCount = 6; // A 31 day month that starts on Saturday.
// Two extra rows: one for the day-of-week header and one for the month header.
const double _kMaxDayPickerHeight = _kDayPickerRowHeight * (_kMaxDayPickerRowCount + 2);
class _DayPickerGridDelegate extends GridDelegateWithInOrderChildPlacement {
@override
GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) {
......@@ -229,11 +126,12 @@ final _DayPickerGridDelegate _kDayPickerGridDelegate = new _DayPickerGridDelegat
/// The days are arranged in a rectangular grid with one column for each day of
/// the week.
///
/// Part of the material design [DatePicker].
/// The day picker widget is rarely used directly. Instead, consider using
/// [showDatePicker], which creates a date picker dialog.
///
/// See also:
///
/// * [DatePicker].
/// * [showDatePicker].
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
class DayPicker extends StatelessWidget {
/// Creates a day picker.
......@@ -353,11 +251,12 @@ class DayPicker extends StatelessWidget {
/// Shows the days of each month in a rectangular grid with one column for each
/// day of the week.
///
/// Part of the material design [DatePicker].
/// The month picker widget is rarely used directly. Instead, consider using
/// [showDatePicker], which creates a date picker dialog.
///
/// See also:
///
/// * [DatePicker]
/// * [showDatePicker]
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
class MonthPicker extends StatefulWidget {
/// Creates a month picker.
......@@ -458,7 +357,8 @@ class _MonthPickerState extends State<MonthPicker> {
@override
Widget build(BuildContext context) {
return new SizedBox(
width: 330.0,
width: _kMonthPickerWidth,
height: _kMaxDayPickerHeight,
child: new Stack(
children: <Widget>[
new PageableLazyList(
......@@ -501,13 +401,14 @@ class _MonthPickerState extends State<MonthPicker> {
/// A scrollable list of years to allow picking a year.
///
/// Part of the material design [DatePicker].
/// 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:
///
/// * [DatePicker]
/// * [showDatePicker]
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
class YearPicker extends StatefulWidget {
/// Creates a year picker.
......@@ -580,3 +481,147 @@ class _YearPickerState extends State<YearPicker> {
);
}
}
class _DatePickerDialog extends StatefulWidget {
_DatePickerDialog({
Key key,
this.initialDate,
this.firstDate,
this.lastDate
}) : super(key: key);
final DateTime initialDate;
final DateTime firstDate;
final DateTime lastDate;
@override
_DatePickerDialogState createState() => new _DatePickerDialogState();
}
class _DatePickerDialogState extends State<_DatePickerDialog> {
@override
void initState() {
super.initState();
_selectedDate = config.initialDate;
}
DateTime _selectedDate;
_DatePickerMode _mode = _DatePickerMode.day;
void _handleModeChanged(_DatePickerMode mode) {
HapticFeedback.vibrate();
setState(() {
_mode = mode;
});
}
void _handleYearChanged(DateTime value) {
HapticFeedback.vibrate();
setState(() {
_mode = _DatePickerMode.day;
_selectedDate = value;
});
}
void _handleDayChanged(DateTime value) {
HapticFeedback.vibrate();
setState(() {
_selectedDate = value;
});
}
void _handleCancel() {
Navigator.pop(context);
}
void _handleOk() {
Navigator.pop(context, _selectedDate);
}
Widget _buildPicker() {
assert(_mode != null);
switch (_mode) {
case _DatePickerMode.day:
return new MonthPicker(
selectedDate: _selectedDate,
onChanged: _handleDayChanged,
firstDate: config.firstDate,
lastDate: config.lastDate
);
case _DatePickerMode.year:
return new YearPicker(
selectedDate: _selectedDate,
onChanged: _handleYearChanged,
firstDate: config.firstDate,
lastDate: config.lastDate
);
}
return null;
}
@override
Widget build(BuildContext context) {
return new Dialog(
child: new SizedBox(
width: _kMonthPickerWidth,
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new _DatePickerHeader(
selectedDate: _selectedDate,
mode: _mode,
onModeChanged: _handleModeChanged
),
new Container(
height: _kMaxDayPickerHeight,
child: _buildPicker()
),
new ButtonTheme.bar(
child: new ButtonBar(
alignment: MainAxisAlignment.end,
children: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: _handleCancel
),
new FlatButton(
child: new Text('OK'),
onPressed: _handleOk
),
]
)
)
]
)
)
);
}
}
/// Shows a dialog containing a material design date picker.
///
/// The returned [Future] resolves to the date selected by the user when the
/// user closes the dialog. If the user cancels the dialog, the [Future]
/// resolves to the initialDate.
///
/// See also:
///
/// * [showTimePicker]
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
Future<DateTime> showDatePicker({
BuildContext context,
DateTime initialDate,
DateTime firstDate,
DateTime lastDate
}) async {
DateTime picked = await showDialog(
context: context,
child: new _DatePickerDialog(
initialDate: initialDate,
firstDate: firstDate,
lastDate: lastDate
)
);
return picked ?? initialDate;
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'dialog.dart';
import 'date_picker.dart';
import 'flat_button.dart';
class _DatePickerDialog extends StatefulWidget {
_DatePickerDialog({
Key key,
this.initialDate,
this.firstDate,
this.lastDate
}) : super(key: key);
final DateTime initialDate;
final DateTime firstDate;
final DateTime lastDate;
@override
_DatePickerDialogState createState() => new _DatePickerDialogState();
}
class _DatePickerDialogState extends State<_DatePickerDialog> {
@override
void initState() {
super.initState();
_selectedDate = config.initialDate;
}
DateTime _selectedDate;
void _handleDateChanged(DateTime value) {
setState(() {
_selectedDate = value;
});
}
void _handleCancel() {
Navigator.pop(context);
}
void _handleOk() {
Navigator.pop(context, _selectedDate);
}
@override
Widget build(BuildContext context) {
// TODO(abarth): Use Dialog directly.
return new AlertDialog(
content: new DatePicker(
selectedDate: _selectedDate,
firstDate: config.firstDate,
lastDate: config.lastDate,
onChanged: _handleDateChanged
),
contentPadding: EdgeInsets.zero,
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: _handleCancel
),
new FlatButton(
child: new Text('OK'),
onPressed: _handleOk
),
]
);
}
}
/// Shows a dialog containing a material design date picker.
///
/// The returned [Future] resolves to the date selected by the user when the
/// user closes the dialog. If the user cancels the dialog, the [Future]
/// resolves to the initialDate.
///
/// See also:
///
/// * [DatePicker]
/// * [showTimePicker]
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
Future<DateTime> showDatePicker({
BuildContext context,
DateTime initialDate,
DateTime firstDate,
DateTime lastDate
}) async {
DateTime picked = await showDialog(
context: context,
child: new _DatePickerDialog(
initialDate: initialDate,
firstDate: firstDate,
lastDate: lastDate
)
);
return picked ?? initialDate;
}
......@@ -21,7 +21,7 @@ void main() {
child: new Block(
children: <Widget>[
new Material(
child: new DatePicker(
child: new MonthPicker(
firstDate: new DateTime(0),
lastDate: new DateTime(9999),
key: _datePickerKey,
......@@ -43,20 +43,20 @@ void main() {
)
);
await tester.tapAt(const Point(50.0, 200.0));
await tester.tapAt(const Point(50.0, 100.0));
expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 26)));
await tester.pump(const Duration(seconds: 2));
await tester.tapAt(const Point(300.0, 200.0));
await tester.tapAt(const Point(300.0, 100.0));
expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 1)));
await tester.pump(const Duration(seconds: 2));
await tester.tapAt(const Point(380.0, 120.0));
await tester.tapAt(const Point(380.0, 20.0));
await tester.pump();
await tester.pump(const Duration(seconds: 2));
expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 1)));
await tester.tapAt(const Point(300.0, 200.0));
await tester.tapAt(const Point(300.0, 100.0));
expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 5)));
await tester.pump(const Duration(seconds: 2));
......@@ -65,7 +65,7 @@ void main() {
await tester.pump(const Duration(seconds: 2));
expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 5)));
await tester.tapAt(const Point(45.0, 370.0));
await tester.tapAt(const Point(45.0, 270.0));
expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25)));
await tester.pump(const Duration(seconds: 2));
......@@ -74,7 +74,7 @@ void main() {
await tester.pump(const Duration(seconds: 2));
expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25)));
await tester.tapAt(const Point(210.0, 280.0));
await tester.tapAt(const Point(210.0, 180.0));
expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 17)));
await tester.pump(const Duration(seconds: 2));
......@@ -92,10 +92,10 @@ void main() {
child: new Material(
child: new Block(
children: <Widget>[
new DatePicker(
new MonthPicker(
firstDate: new DateTime(0),
lastDate: new DateTime(9999),
onChanged: null,
onChanged: (DateTime value) { },
selectedDate: new DateTime(2000, DateTime.JANUARY, 1)
)
]
......
......@@ -12,7 +12,7 @@ void main() {
Widget widget = new Material(
child: new Block(
children: <Widget>[
new DatePicker(
new MonthPicker(
selectedDate: new DateTime.utc(2015, 6, 9, 7, 12),
firstDate: new DateTime.utc(2013),
lastDate: new DateTime.utc(2018),
......
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