Switch DatePicker to SliverGrid (#7890)
After this patch, the old grid code is not used in the framework.
diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart
index 84aa8f1..5232fab 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: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 @@
children: <Widget>[
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 @@
@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 @@
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 @@
onTap: () {
onChanged(dayToBuild);
},
- child: dayWidget
+ child: dayWidget,
);
}
@@ -284,18 +293,18 @@
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 @@
@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 @@
firstDate: config.firstDate,
lastDate: config.lastDate,
displayedMonth: monthToBuild,
- selectableDayPredicate: config.selectableDayPredicate
+ selectableDayPredicate: config.selectableDayPredicate,
));
}
return result;
@@ -460,7 +469,7 @@
itemCount: _monthDelta(config.firstDate, config.lastDate) + 1,
itemBuilder: _buildItems,
duration: _kMonthScrollDuration,
- onPageChanged: _handleMonthPageChanged
+ onPageChanged: _handleMonthPageChanged,
),
new Positioned(
top: 0.0,
@@ -468,8 +477,8 @@
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 @@
child: new IconButton(
icon: new Icon(Icons.chevron_right),
tooltip: 'Next month',
- onPressed: _isDisplayingLastMonth ? null : _handleNextMonth
- )
- )
- ]
- )
+ onPressed: _isDisplayingLastMonth ? null : _handleNextMonth,
+ ),
+ ),
+ ],
+ ),
);
}
@@ -516,7 +525,7 @@
@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 @@
this.initialDate,
this.firstDate,
this.lastDate,
- this.selectableDayPredicate
+ this.selectableDayPredicate,
}) : super(key: key);
final DateTime initialDate;
@@ -639,7 +648,7 @@
onChanged: _handleDayChanged,
firstDate: config.firstDate,
lastDate: config.lastDate,
- selectableDayPredicate: config.selectableDayPredicate
+ selectableDayPredicate: config.selectableDayPredicate,
);
case _DatePickerMode.year:
return new YearPicker(
@@ -647,7 +656,7 @@
selectedDate: _selectedDate,
onChanged: _handleYearChanged,
firstDate: config.firstDate,
- lastDate: config.lastDate
+ lastDate: config.lastDate,
);
}
return null;
@@ -659,21 +668,21 @@
child: new SizedBox(
height: _kMaxDayPickerHeight,
child: _buildPicker(),
- )
+ ),
);
Widget actions = new ButtonTheme.bar(
child: new ButtonBar(
children: <Widget>[
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 @@
selectedDate: _selectedDate,
mode: _mode,
onModeChanged: _handleModeChanged,
- orientation: orientation
+ orientation: orientation,
);
assert(orientation != null);
switch (orientation) {
@@ -693,8 +702,8 @@
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
- children: <Widget>[header, picker, actions]
- )
+ children: <Widget>[header, picker, actions],
+ ),
);
case Orientation.landscape:
return new SizedBox(
@@ -710,12 +719,12 @@
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
- children: <Widget>[picker, actions]
- )
- )
- )
- ]
- )
+ children: <Widget>[picker, actions],
+ ),
+ ),
+ ),
+ ],
+ ),
);
}
return null;
@@ -748,7 +757,7 @@
@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 @@
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 ea75e10..20d43f7 100644
--- a/packages/flutter/lib/src/widgets/sliver.dart
+++ b/packages/flutter/lib/src/widgets/sliver.dart
@@ -79,8 +79,20 @@
// /// 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<Widget> children;
@override
@@ -90,7 +102,7 @@
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 133c06c..6ff5069 100644
--- a/packages/flutter/test/material/date_picker_test.dart
+++ b/packages/flutter/test/material/date_picker_test.dart
@@ -41,16 +41,16 @@
setState(() {
_selectedDate = value;
});
- }
- )
- )
- )
+ },
+ ),
+ ),
+ ),
);
- }
- )
- )
- ]
- )
+ },
+ ),
+ ),
+ ],
+ ),
);
await tester.tapAt(const Point(50.0, 100.0));
@@ -79,7 +79,7 @@
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 @@
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));
});