Commit 079db95b authored by Adam Barth's avatar Adam Barth Committed by GitHub

Switch DatePicker to SliverGrid (#7890)

After this patch, the old grid code is not used in the framework.
parent 31e2a500
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:intl/date_symbols.dart'; import 'package:intl/date_symbols.dart';
...@@ -122,32 +123,40 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -122,32 +123,40 @@ class _DatePickerHeader extends StatelessWidget {
children: <Widget>[ children: <Widget>[
new GestureDetector( new GestureDetector(
onTap: () => _handleChangeMode(_DatePickerMode.year), onTap: () => _handleChangeMode(_DatePickerMode.year),
child: new Text(new DateFormat('yyyy').format(selectedDate), style: yearStyle) child: new Text(new DateFormat('yyyy').format(selectedDate), style: yearStyle),
), ),
new GestureDetector( new GestureDetector(
onTap: () => _handleChangeMode(_DatePickerMode.day), onTap: () => _handleChangeMode(_DatePickerMode.day),
child: new Text(new DateFormat('E, MMM\u00a0d').format(selectedDate), style: dayStyle) child: new Text(new DateFormat('E, MMM\u00a0d').format(selectedDate), style: dayStyle),
),
],
), ),
]
)
); );
} }
} }
class _DayPickerGridDelegate extends GridDelegateWithInOrderChildPlacement { class _DayPickerGridDelegate extends SliverGridDelegate {
const _DayPickerGridDelegate();
@override @override
GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) { SliverGridLayout getLayout(SliverConstraints constraints) {
final int columnCount = DateTime.DAYS_PER_WEEK; final int columnCount = DateTime.DAYS_PER_WEEK;
return new GridSpecification.fromRegularTiles( final double tileWidth = constraints.crossAxisExtent / columnCount;
tileWidth: constraints.maxWidth / columnCount, final double tileHeight = math.min(_kDayPickerRowHeight, constraints.viewportMainAxisExtent / (_kMaxDayPickerRowCount + 1));
tileHeight: math.min(_kDayPickerRowHeight, constraints.maxHeight / (_kMaxDayPickerRowCount + 1)), return new SliverGridRegularTileLayout(
columnCount: columnCount, crossAxisCount: columnCount,
rowCount: (childCount / columnCount).ceil() mainAxisStride: tileHeight,
crossAxisStride: tileWidth,
childMainAxisExtent: tileHeight,
childCrossAxisExtent: tileWidth,
); );
} }
@override
bool shouldRelayout(_DayPickerGridDelegate oldDelegate) => false;
} }
final _DayPickerGridDelegate _kDayPickerGridDelegate = new _DayPickerGridDelegate(); const _DayPickerGridDelegate _kDayPickerGridDelegate = const _DayPickerGridDelegate();
/// Displays the days of a given month and allows choosing a day. /// Displays the days of a given month and allows choosing a day.
/// ///
...@@ -173,7 +182,7 @@ class DayPicker extends StatelessWidget { ...@@ -173,7 +182,7 @@ class DayPicker extends StatelessWidget {
@required this.firstDate, @required this.firstDate,
@required this.lastDate, @required this.lastDate,
@required this.displayedMonth, @required this.displayedMonth,
this.selectableDayPredicate this.selectableDayPredicate,
}) : super(key: key) { }) : super(key: key) {
assert(selectedDate != null); assert(selectedDate != null);
assert(currentDate != null); assert(currentDate != null);
...@@ -258,8 +267,8 @@ class DayPicker extends StatelessWidget { ...@@ -258,8 +267,8 @@ class DayPicker extends StatelessWidget {
Widget dayWidget = new Container( Widget dayWidget = new Container(
decoration: decoration, decoration: decoration,
child: new Center( child: new Center(
child: new Text(day.toString(), style: itemStyle) child: new Text(day.toString(), style: itemStyle),
) ),
); );
if (!disabled) { if (!disabled) {
...@@ -268,7 +277,7 @@ class DayPicker extends StatelessWidget { ...@@ -268,7 +277,7 @@ class DayPicker extends StatelessWidget {
onTap: () { onTap: () {
onChanged(dayToBuild); onChanged(dayToBuild);
}, },
child: dayWidget child: dayWidget,
); );
} }
...@@ -284,18 +293,18 @@ class DayPicker extends StatelessWidget { ...@@ -284,18 +293,18 @@ class DayPicker extends StatelessWidget {
height: _kDayPickerRowHeight, height: _kDayPickerRowHeight,
child: new Center( child: new Center(
child: new Text(new DateFormat('yMMMM').format(displayedMonth), child: new Text(new DateFormat('yMMMM').format(displayedMonth),
style: themeData.textTheme.subhead style: themeData.textTheme.subhead,
) ),
) ),
), ),
new Flexible( new Flexible(
child: new CustomGrid( child: new GridView.custom(
delegate: _kDayPickerGridDelegate, gridDelegate: _kDayPickerGridDelegate,
children: labels childrenDelegate: new SliverChildListDelegate(labels, addRepaintBoundaries: false),
) ),
) ),
] ],
) ),
); );
} }
} }
...@@ -322,7 +331,7 @@ class MonthPicker extends StatefulWidget { ...@@ -322,7 +331,7 @@ class MonthPicker extends StatefulWidget {
@required this.onChanged, @required this.onChanged,
@required this.firstDate, @required this.firstDate,
@required this.lastDate, @required this.lastDate,
this.selectableDayPredicate this.selectableDayPredicate,
}) : super(key: key) { }) : super(key: key) {
assert(selectedDate != null); assert(selectedDate != null);
assert(onChanged != null); assert(onChanged != null);
...@@ -410,7 +419,7 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -410,7 +419,7 @@ class _MonthPickerState extends State<MonthPicker> {
firstDate: config.firstDate, firstDate: config.firstDate,
lastDate: config.lastDate, lastDate: config.lastDate,
displayedMonth: monthToBuild, displayedMonth: monthToBuild,
selectableDayPredicate: config.selectableDayPredicate selectableDayPredicate: config.selectableDayPredicate,
)); ));
} }
return result; return result;
...@@ -460,7 +469,7 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -460,7 +469,7 @@ class _MonthPickerState extends State<MonthPicker> {
itemCount: _monthDelta(config.firstDate, config.lastDate) + 1, itemCount: _monthDelta(config.firstDate, config.lastDate) + 1,
itemBuilder: _buildItems, itemBuilder: _buildItems,
duration: _kMonthScrollDuration, duration: _kMonthScrollDuration,
onPageChanged: _handleMonthPageChanged onPageChanged: _handleMonthPageChanged,
), ),
new Positioned( new Positioned(
top: 0.0, top: 0.0,
...@@ -468,8 +477,8 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -468,8 +477,8 @@ class _MonthPickerState extends State<MonthPicker> {
child: new IconButton( child: new IconButton(
icon: new Icon(Icons.chevron_left), icon: new Icon(Icons.chevron_left),
tooltip: 'Previous month', tooltip: 'Previous month',
onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth,
) ),
), ),
new Positioned( new Positioned(
top: 0.0, top: 0.0,
...@@ -477,11 +486,11 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -477,11 +486,11 @@ class _MonthPickerState extends State<MonthPicker> {
child: new IconButton( child: new IconButton(
icon: new Icon(Icons.chevron_right), icon: new Icon(Icons.chevron_right),
tooltip: 'Next month', tooltip: 'Next month',
onPressed: _isDisplayingLastMonth ? null : _handleNextMonth onPressed: _isDisplayingLastMonth ? null : _handleNextMonth,
) ),
) ),
] ],
) ),
); );
} }
...@@ -516,7 +525,7 @@ class YearPicker extends StatefulWidget { ...@@ -516,7 +525,7 @@ class YearPicker extends StatefulWidget {
@required this.selectedDate, @required this.selectedDate,
@required this.onChanged, @required this.onChanged,
@required this.firstDate, @required this.firstDate,
@required this.lastDate @required this.lastDate,
}) : super(key: key) { }) : super(key: key) {
assert(selectedDate != null); assert(selectedDate != null);
assert(onChanged != null); assert(onChanged != null);
...@@ -576,7 +585,7 @@ class _DatePickerDialog extends StatefulWidget { ...@@ -576,7 +585,7 @@ class _DatePickerDialog extends StatefulWidget {
this.initialDate, this.initialDate,
this.firstDate, this.firstDate,
this.lastDate, this.lastDate,
this.selectableDayPredicate this.selectableDayPredicate,
}) : super(key: key); }) : super(key: key);
final DateTime initialDate; final DateTime initialDate;
...@@ -639,7 +648,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -639,7 +648,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
onChanged: _handleDayChanged, onChanged: _handleDayChanged,
firstDate: config.firstDate, firstDate: config.firstDate,
lastDate: config.lastDate, lastDate: config.lastDate,
selectableDayPredicate: config.selectableDayPredicate selectableDayPredicate: config.selectableDayPredicate,
); );
case _DatePickerMode.year: case _DatePickerMode.year:
return new YearPicker( return new YearPicker(
...@@ -647,7 +656,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -647,7 +656,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
selectedDate: _selectedDate, selectedDate: _selectedDate,
onChanged: _handleYearChanged, onChanged: _handleYearChanged,
firstDate: config.firstDate, firstDate: config.firstDate,
lastDate: config.lastDate lastDate: config.lastDate,
); );
} }
return null; return null;
...@@ -659,21 +668,21 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -659,21 +668,21 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
child: new SizedBox( child: new SizedBox(
height: _kMaxDayPickerHeight, height: _kMaxDayPickerHeight,
child: _buildPicker(), child: _buildPicker(),
) ),
); );
Widget actions = new ButtonTheme.bar( Widget actions = new ButtonTheme.bar(
child: new ButtonBar( child: new ButtonBar(
children: <Widget>[ children: <Widget>[
new FlatButton( new FlatButton(
child: new Text('CANCEL'), child: new Text('CANCEL'),
onPressed: _handleCancel onPressed: _handleCancel,
), ),
new FlatButton( new FlatButton(
child: new Text('OK'), child: new Text('OK'),
onPressed: _handleOk onPressed: _handleOk,
),
],
), ),
]
)
); );
return new Dialog( return new Dialog(
...@@ -683,7 +692,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -683,7 +692,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
selectedDate: _selectedDate, selectedDate: _selectedDate,
mode: _mode, mode: _mode,
onModeChanged: _handleModeChanged, onModeChanged: _handleModeChanged,
orientation: orientation orientation: orientation,
); );
assert(orientation != null); assert(orientation != null);
switch (orientation) { switch (orientation) {
...@@ -693,8 +702,8 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -693,8 +702,8 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
child: new Column( child: new Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[header, picker, actions] children: <Widget>[header, picker, actions],
) ),
); );
case Orientation.landscape: case Orientation.landscape:
return new SizedBox( return new SizedBox(
...@@ -710,12 +719,12 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -710,12 +719,12 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
child: new Column( child: new Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[picker, actions] children: <Widget>[picker, actions],
) ),
) ),
) ),
] ],
) ),
); );
} }
return null; return null;
...@@ -748,7 +757,7 @@ Future<DateTime> showDatePicker({ ...@@ -748,7 +757,7 @@ Future<DateTime> showDatePicker({
@required DateTime initialDate, @required DateTime initialDate,
@required DateTime firstDate, @required DateTime firstDate,
@required DateTime lastDate, @required DateTime lastDate,
SelectableDayPredicate selectableDayPredicate SelectableDayPredicate selectableDayPredicate,
}) 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');
...@@ -763,7 +772,7 @@ Future<DateTime> showDatePicker({ ...@@ -763,7 +772,7 @@ Future<DateTime> showDatePicker({
initialDate: initialDate, initialDate: initialDate,
firstDate: firstDate, firstDate: firstDate,
lastDate: lastDate, lastDate: lastDate,
selectableDayPredicate: selectableDayPredicate selectableDayPredicate: selectableDayPredicate,
) )
); );
} }
...@@ -79,8 +79,20 @@ class SliverChildBuilderDelegate extends SliverChildDelegate { ...@@ -79,8 +79,20 @@ class SliverChildBuilderDelegate extends SliverChildDelegate {
// /// demand). For example, the body of a dialog box might fit both of these // /// demand). For example, the body of a dialog box might fit both of these
// /// conditions. // /// conditions.
class SliverChildListDelegate extends SliverChildDelegate { class SliverChildListDelegate extends SliverChildDelegate {
const SliverChildListDelegate(this.children); const SliverChildListDelegate(this.children, { this.addRepaintBoundaries: true });
/// Whether to wrap each child in a [RepaintBoundary].
///
/// Typically, children in a scrolling container are wrapped in repaint
/// boundaries so that they do not need to be repainted as the list scrolls.
/// If the children are easy to repaint (e.g., solid color blocks or a short
/// snippet of text), it might be more efficient to not add a repaint boundary
/// and simply repaint the children during scrolling.
///
/// Defaults to true.
final bool addRepaintBoundaries;
/// The widgets to display.
final List<Widget> children; final List<Widget> children;
@override @override
...@@ -90,7 +102,7 @@ class SliverChildListDelegate extends SliverChildDelegate { ...@@ -90,7 +102,7 @@ class SliverChildListDelegate extends SliverChildDelegate {
return null; return null;
final Widget child = children[index]; final Widget child = children[index];
assert(child != null); assert(child != null);
return new RepaintBoundary.wrap(child, index); return addRepaintBoundaries ? new RepaintBoundary.wrap(child, index) : child;
} }
@override @override
......
...@@ -41,16 +41,16 @@ void main() { ...@@ -41,16 +41,16 @@ void main() {
setState(() { setState(() {
_selectedDate = value; _selectedDate = value;
}); });
} },
) ),
) ),
) ),
); );
} },
) ),
) ),
] ],
) ),
); );
await tester.tapAt(const Point(50.0, 100.0)); await tester.tapAt(const Point(50.0, 100.0));
...@@ -79,7 +79,7 @@ void main() { ...@@ -79,7 +79,7 @@ void main() {
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));
await tester.scroll(find.byKey(_datePickerKey), const Offset(300.0, 10.0)); await tester.scroll(find.byKey(_datePickerKey), const Offset(300.0, 0.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.SEPTEMBER, 25))); expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25)));
...@@ -105,17 +105,17 @@ void main() { ...@@ -105,17 +105,17 @@ void main() {
firstDate: new DateTime(0), firstDate: new DateTime(0),
lastDate: new DateTime(9999), lastDate: new DateTime(9999),
onChanged: (DateTime value) { }, onChanged: (DateTime value) { },
selectedDate: new DateTime(2000, DateTime.JANUARY, 1) selectedDate: new DateTime(2000, DateTime.JANUARY, 1),
) ),
) ),
) ),
) ),
); );
} },
) ),
) ),
] ],
) ),
); );
await tester.pump(const Duration(seconds: 5)); await tester.pump(const Duration(seconds: 5));
}); });
......
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