diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index 84aa8f1c286..5232fab29f2 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math' as math; import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:intl/date_symbols.dart'; @@ -122,32 +123,40 @@ class _DatePickerHeader extends StatelessWidget { children: [ new GestureDetector( 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( 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 - GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) { + SliverGridLayout getLayout(SliverConstraints constraints) { final int columnCount = DateTime.DAYS_PER_WEEK; - return new GridSpecification.fromRegularTiles( - tileWidth: constraints.maxWidth / columnCount, - tileHeight: math.min(_kDayPickerRowHeight, constraints.maxHeight / (_kMaxDayPickerRowCount + 1)), - columnCount: columnCount, - rowCount: (childCount / columnCount).ceil() + final double tileWidth = constraints.crossAxisExtent / columnCount; + final double tileHeight = math.min(_kDayPickerRowHeight, constraints.viewportMainAxisExtent / (_kMaxDayPickerRowCount + 1)); + return new SliverGridRegularTileLayout( + crossAxisCount: columnCount, + 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. /// @@ -173,7 +182,7 @@ class DayPicker extends StatelessWidget { @required this.firstDate, @required this.lastDate, @required this.displayedMonth, - this.selectableDayPredicate + this.selectableDayPredicate, }) : super(key: key) { assert(selectedDate != null); assert(currentDate != null); @@ -258,8 +267,8 @@ class DayPicker extends StatelessWidget { Widget dayWidget = new Container( decoration: decoration, child: new Center( - child: new Text(day.toString(), style: itemStyle) - ) + child: new Text(day.toString(), style: itemStyle), + ), ); if (!disabled) { @@ -268,7 +277,7 @@ class DayPicker extends StatelessWidget { onTap: () { onChanged(dayToBuild); }, - child: dayWidget + child: dayWidget, ); } @@ -284,18 +293,18 @@ class DayPicker extends StatelessWidget { height: _kDayPickerRowHeight, child: new Center( child: new Text(new DateFormat('yMMMM').format(displayedMonth), - style: themeData.textTheme.subhead - ) - ) + style: themeData.textTheme.subhead, + ), + ), ), new Flexible( - child: new CustomGrid( - delegate: _kDayPickerGridDelegate, - children: labels - ) - ) - ] - ) + child: new GridView.custom( + gridDelegate: _kDayPickerGridDelegate, + childrenDelegate: new SliverChildListDelegate(labels, addRepaintBoundaries: false), + ), + ), + ], + ), ); } } @@ -322,7 +331,7 @@ class MonthPicker extends StatefulWidget { @required this.onChanged, @required this.firstDate, @required this.lastDate, - this.selectableDayPredicate + this.selectableDayPredicate, }) : super(key: key) { assert(selectedDate != null); assert(onChanged != null); @@ -410,7 +419,7 @@ class _MonthPickerState extends State { firstDate: config.firstDate, lastDate: config.lastDate, displayedMonth: monthToBuild, - selectableDayPredicate: config.selectableDayPredicate + selectableDayPredicate: config.selectableDayPredicate, )); } return result; @@ -460,7 +469,7 @@ class _MonthPickerState extends State { itemCount: _monthDelta(config.firstDate, config.lastDate) + 1, itemBuilder: _buildItems, duration: _kMonthScrollDuration, - onPageChanged: _handleMonthPageChanged + onPageChanged: _handleMonthPageChanged, ), new Positioned( top: 0.0, @@ -468,8 +477,8 @@ class _MonthPickerState extends State { child: new IconButton( icon: new Icon(Icons.chevron_left), tooltip: 'Previous month', - onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth - ) + onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth, + ), ), new Positioned( top: 0.0, @@ -477,11 +486,11 @@ class _MonthPickerState extends State { child: new IconButton( icon: new Icon(Icons.chevron_right), tooltip: 'Next month', - onPressed: _isDisplayingLastMonth ? null : _handleNextMonth - ) - ) - ] - ) + onPressed: _isDisplayingLastMonth ? null : _handleNextMonth, + ), + ), + ], + ), ); } @@ -516,7 +525,7 @@ class YearPicker extends StatefulWidget { @required this.selectedDate, @required this.onChanged, @required this.firstDate, - @required this.lastDate + @required this.lastDate, }) : super(key: key) { assert(selectedDate != null); assert(onChanged != null); @@ -576,7 +585,7 @@ class _DatePickerDialog extends StatefulWidget { this.initialDate, this.firstDate, this.lastDate, - this.selectableDayPredicate + this.selectableDayPredicate, }) : super(key: key); final DateTime initialDate; @@ -639,7 +648,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { onChanged: _handleDayChanged, firstDate: config.firstDate, lastDate: config.lastDate, - selectableDayPredicate: config.selectableDayPredicate + selectableDayPredicate: config.selectableDayPredicate, ); case _DatePickerMode.year: return new YearPicker( @@ -647,7 +656,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { selectedDate: _selectedDate, onChanged: _handleYearChanged, firstDate: config.firstDate, - lastDate: config.lastDate + lastDate: config.lastDate, ); } return null; @@ -659,21 +668,21 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { child: new SizedBox( height: _kMaxDayPickerHeight, child: _buildPicker(), - ) + ), ); Widget actions = new ButtonTheme.bar( child: new ButtonBar( children: [ new FlatButton( child: new Text('CANCEL'), - onPressed: _handleCancel + onPressed: _handleCancel, ), new FlatButton( child: new Text('OK'), - onPressed: _handleOk + onPressed: _handleOk, ), - ] - ) + ], + ), ); return new Dialog( @@ -683,7 +692,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { selectedDate: _selectedDate, mode: _mode, onModeChanged: _handleModeChanged, - orientation: orientation + orientation: orientation, ); assert(orientation != null); switch (orientation) { @@ -693,8 +702,8 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { child: new Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, - children: [header, picker, actions] - ) + children: [header, picker, actions], + ), ); case Orientation.landscape: return new SizedBox( @@ -710,12 +719,12 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { child: new Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, - children: [picker, actions] - ) - ) - ) - ] - ) + children: [picker, actions], + ), + ), + ), + ], + ), ); } return null; @@ -748,7 +757,7 @@ Future showDatePicker({ @required DateTime initialDate, @required DateTime firstDate, @required DateTime lastDate, - SelectableDayPredicate selectableDayPredicate + SelectableDayPredicate selectableDayPredicate, }) async { assert(!initialDate.isBefore(firstDate), 'initialDate must be on or after firstDate'); assert(!initialDate.isAfter(lastDate), 'initialDate must be on or before lastDate'); @@ -763,7 +772,7 @@ Future showDatePicker({ initialDate: initialDate, firstDate: firstDate, lastDate: lastDate, - selectableDayPredicate: selectableDayPredicate + selectableDayPredicate: selectableDayPredicate, ) ); } diff --git a/packages/flutter/lib/src/widgets/sliver.dart b/packages/flutter/lib/src/widgets/sliver.dart index ea75e10aa38..20d43f7e239 100644 --- a/packages/flutter/lib/src/widgets/sliver.dart +++ b/packages/flutter/lib/src/widgets/sliver.dart @@ -79,8 +79,20 @@ class SliverChildBuilderDelegate extends SliverChildDelegate { // /// demand). For example, the body of a dialog box might fit both of these // /// conditions. 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 children; @override @@ -90,7 +102,7 @@ class SliverChildListDelegate extends SliverChildDelegate { return null; final Widget child = children[index]; assert(child != null); - return new RepaintBoundary.wrap(child, index); + return addRepaintBoundaries ? new RepaintBoundary.wrap(child, index) : child; } @override diff --git a/packages/flutter/test/material/date_picker_test.dart b/packages/flutter/test/material/date_picker_test.dart index 133c06c39af..6ff50694568 100644 --- a/packages/flutter/test/material/date_picker_test.dart +++ b/packages/flutter/test/material/date_picker_test.dart @@ -41,16 +41,16 @@ void main() { setState(() { _selectedDate = value; }); - } - ) - ) - ) + }, + ), + ), + ), ); - } - ) - ) - ] - ) + }, + ), + ), + ], + ), ); await tester.tapAt(const Point(50.0, 100.0)); @@ -79,7 +79,7 @@ void main() { expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25))); 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(const Duration(seconds: 2)); expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25))); @@ -105,17 +105,17 @@ void main() { firstDate: new DateTime(0), lastDate: new DateTime(9999), onChanged: (DateTime value) { }, - selectedDate: new DateTime(2000, DateTime.JANUARY, 1) - ) - ) - ) - ) + selectedDate: new DateTime(2000, DateTime.JANUARY, 1), + ), + ), + ), + ), ); - } - ) - ) - ] - ) + }, + ), + ), + ], + ), ); await tester.pump(const Duration(seconds: 5)); });