mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Switch DatePicker to SliverGrid (#7890)
After this patch, the old grid code is not used in the framework.
This commit is contained in:
parent
31e2a500f7
commit
079db95b80
@ -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: <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 @@ 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<MonthPicker> {
|
||||
firstDate: config.firstDate,
|
||||
lastDate: config.lastDate,
|
||||
displayedMonth: monthToBuild,
|
||||
selectableDayPredicate: config.selectableDayPredicate
|
||||
selectableDayPredicate: config.selectableDayPredicate,
|
||||
));
|
||||
}
|
||||
return result;
|
||||
@ -460,7 +469,7 @@ class _MonthPickerState extends State<MonthPicker> {
|
||||
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<MonthPicker> {
|
||||
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<MonthPicker> {
|
||||
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: <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 @@ 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: <Widget>[header, picker, actions]
|
||||
)
|
||||
children: <Widget>[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: <Widget>[picker, actions]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
children: <Widget>[picker, actions],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@ -748,7 +757,7 @@ Future<DateTime> 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<DateTime> showDatePicker({
|
||||
initialDate: initialDate,
|
||||
firstDate: firstDate,
|
||||
lastDate: lastDate,
|
||||
selectableDayPredicate: selectableDayPredicate
|
||||
selectableDayPredicate: selectableDayPredicate,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -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<Widget> 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
|
||||
|
||||
@ -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));
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user