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'; ...@@ -28,7 +28,6 @@ export 'src/material/constants.dart';
export 'src/material/data_table.dart'; export 'src/material/data_table.dart';
export 'src/material/data_table_source.dart'; export 'src/material/data_table_source.dart';
export 'src/material/date_picker.dart'; export 'src/material/date_picker.dart';
export 'src/material/date_picker_dialog.dart';
export 'src/material/dialog.dart'; export 'src/material/dialog.dart';
export 'src/material/divider.dart'; export 'src/material/divider.dart';
export 'src/material/drawer.dart'; export 'src/material/drawer.dart';
......
...@@ -10,10 +10,14 @@ import 'package:intl/date_symbols.dart'; ...@@ -10,10 +10,14 @@ import 'package:intl/date_symbols.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'button_bar.dart';
import 'button.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'icon.dart'; import 'dialog.dart';
import 'flat_button.dart';
import 'icon_button.dart'; import 'icon_button.dart';
import 'icon.dart';
import 'icons.dart'; import 'icons.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -21,115 +25,14 @@ import 'typography.dart'; ...@@ -21,115 +25,14 @@ import 'typography.dart';
enum _DatePickerMode { day, year } enum _DatePickerMode { day, year }
/// A material design date picker. const double _kDatePickerHeaderHeight = 100.0;
///
/// 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 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 // Shows the selected date in large font and toggles between year and day mode
class _DatePickerHeader extends StatelessWidget { class _DatePickerHeader extends StatelessWidget {
...@@ -182,7 +85,7 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -182,7 +85,7 @@ class _DatePickerHeader extends StatelessWidget {
} }
return new Container( return new Container(
height: 100.0, height: _kDatePickerHeaderHeight,
padding: const EdgeInsets.symmetric(horizontal: 24.0), padding: const EdgeInsets.symmetric(horizontal: 24.0),
decoration: new BoxDecoration(backgroundColor: backgroundColor), decoration: new BoxDecoration(backgroundColor: backgroundColor),
child: new Column( child: new Column(
...@@ -203,12 +106,6 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -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 { class _DayPickerGridDelegate extends GridDelegateWithInOrderChildPlacement {
@override @override
GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) { GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) {
...@@ -229,11 +126,12 @@ final _DayPickerGridDelegate _kDayPickerGridDelegate = new _DayPickerGridDelegat ...@@ -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 days are arranged in a rectangular grid with one column for each day of
/// the week. /// 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: /// See also:
/// ///
/// * [DatePicker]. /// * [showDatePicker].
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers> /// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
class DayPicker extends StatelessWidget { class DayPicker extends StatelessWidget {
/// Creates a day picker. /// Creates a day picker.
...@@ -353,11 +251,12 @@ class DayPicker extends StatelessWidget { ...@@ -353,11 +251,12 @@ class DayPicker extends StatelessWidget {
/// Shows the days of each month in a rectangular grid with one column for each /// Shows the days of each month in a rectangular grid with one column for each
/// day of the week. /// 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: /// See also:
/// ///
/// * [DatePicker] /// * [showDatePicker]
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers> /// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
class MonthPicker extends StatefulWidget { class MonthPicker extends StatefulWidget {
/// Creates a month picker. /// Creates a month picker.
...@@ -458,7 +357,8 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -458,7 +357,8 @@ class _MonthPickerState extends State<MonthPicker> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new SizedBox( return new SizedBox(
width: 330.0, width: _kMonthPickerWidth,
height: _kMaxDayPickerHeight,
child: new Stack( child: new Stack(
children: <Widget>[ children: <Widget>[
new PageableLazyList( new PageableLazyList(
...@@ -501,13 +401,14 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -501,13 +401,14 @@ class _MonthPickerState extends State<MonthPicker> {
/// A scrollable list of years to allow picking a year. /// 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. /// Requires one of its ancestors to be a [Material] widget.
/// ///
/// See also: /// See also:
/// ///
/// * [DatePicker] /// * [showDatePicker]
/// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers> /// * <https://www.google.com/design/spec/components/pickers.html#pickers-date-pickers>
class YearPicker extends StatefulWidget { class YearPicker extends StatefulWidget {
/// Creates a year picker. /// Creates a year picker.
...@@ -580,3 +481,147 @@ class _YearPickerState extends State<YearPicker> { ...@@ -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() { ...@@ -21,7 +21,7 @@ void main() {
child: new Block( child: new Block(
children: <Widget>[ children: <Widget>[
new Material( new Material(
child: new DatePicker( child: new MonthPicker(
firstDate: new DateTime(0), firstDate: new DateTime(0),
lastDate: new DateTime(9999), lastDate: new DateTime(9999),
key: _datePickerKey, key: _datePickerKey,
...@@ -43,20 +43,20 @@ void main() { ...@@ -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))); expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 26)));
await tester.pump(const Duration(seconds: 2)); 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))); expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 1)));
await tester.pump(const Duration(seconds: 2)); 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();
await tester.pump(const Duration(seconds: 2)); await tester.pump(const Duration(seconds: 2));
expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 1))); 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))); expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 5)));
await tester.pump(const Duration(seconds: 2)); await tester.pump(const Duration(seconds: 2));
...@@ -65,7 +65,7 @@ void main() { ...@@ -65,7 +65,7 @@ void main() {
await tester.pump(const Duration(seconds: 2)); await tester.pump(const Duration(seconds: 2));
expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 5))); 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))); expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25)));
await tester.pump(const Duration(seconds: 2)); await tester.pump(const Duration(seconds: 2));
...@@ -74,7 +74,7 @@ void main() { ...@@ -74,7 +74,7 @@ void main() {
await tester.pump(const Duration(seconds: 2)); await tester.pump(const Duration(seconds: 2));
expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25))); 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))); expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 17)));
await tester.pump(const Duration(seconds: 2)); await tester.pump(const Duration(seconds: 2));
...@@ -92,10 +92,10 @@ void main() { ...@@ -92,10 +92,10 @@ void main() {
child: new Material( child: new Material(
child: new Block( child: new Block(
children: <Widget>[ children: <Widget>[
new DatePicker( new MonthPicker(
firstDate: new DateTime(0), firstDate: new DateTime(0),
lastDate: new DateTime(9999), lastDate: new DateTime(9999),
onChanged: null, onChanged: (DateTime value) { },
selectedDate: new DateTime(2000, DateTime.JANUARY, 1) selectedDate: new DateTime(2000, DateTime.JANUARY, 1)
) )
] ]
......
...@@ -12,7 +12,7 @@ void main() { ...@@ -12,7 +12,7 @@ void main() {
Widget widget = new Material( Widget widget = new Material(
child: new Block( child: new Block(
children: <Widget>[ children: <Widget>[
new DatePicker( new MonthPicker(
selectedDate: new DateTime.utc(2015, 6, 9, 7, 12), selectedDate: new DateTime.utc(2015, 6, 9, 7, 12),
firstDate: new DateTime.utc(2013), firstDate: new DateTime.utc(2013),
lastDate: new DateTime.utc(2018), 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