Commit 69311c28 authored by Adam Barth's avatar Adam Barth

Improve date picker fidelity (#4023)

This patch updates the date picker to match the new spec.

We're still missing a fade effect when scrolling from one month to
another and we're missing the landscape layout.

Fixes #3558
parent ef563c48
...@@ -11,6 +11,8 @@ import 'package:intl/intl.dart'; ...@@ -11,6 +11,8 @@ import 'package:intl/intl.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'icons.dart';
import 'icon_button.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'theme.dart'; import 'theme.dart';
import 'typography.dart'; import 'typography.dart';
...@@ -88,8 +90,6 @@ class _DatePickerState extends State<DatePicker> { ...@@ -88,8 +90,6 @@ class _DatePickerState extends State<DatePicker> {
config.onChanged(dateTime); config.onChanged(dateTime);
} }
static const double _calendarHeight = _kMaxDayPickerHeight;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget header = new _DatePickerHeader( Widget header = new _DatePickerHeader(
...@@ -104,8 +104,7 @@ class _DatePickerState extends State<DatePicker> { ...@@ -104,8 +104,7 @@ class _DatePickerState extends State<DatePicker> {
selectedDate: config.selectedDate, selectedDate: config.selectedDate,
onChanged: _handleDayChanged, onChanged: _handleDayChanged,
firstDate: config.firstDate, firstDate: config.firstDate,
lastDate: config.lastDate, lastDate: config.lastDate
itemExtent: _calendarHeight
); );
break; break;
case _DatePickerMode.year: case _DatePickerMode.year:
...@@ -122,7 +121,7 @@ class _DatePickerState extends State<DatePicker> { ...@@ -122,7 +121,7 @@ class _DatePickerState extends State<DatePicker> {
children: <Widget>[ children: <Widget>[
header, header,
new Container( new Container(
height: _calendarHeight, height: _kMaxDayPickerHeight,
child: picker child: picker
) )
] ]
...@@ -154,11 +153,11 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -154,11 +153,11 @@ class _DatePickerHeader extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ThemeData theme = Theme.of(context); ThemeData themeData = Theme.of(context);
TextTheme headerTheme = theme.primaryTextTheme; TextTheme headerTextTheme = themeData.primaryTextTheme;
Color dayColor; Color dayColor;
Color yearColor; Color yearColor;
switch(theme.primaryColorBrightness) { switch(themeData.primaryColorBrightness) {
case ThemeBrightness.light: case ThemeBrightness.light:
dayColor = mode == _DatePickerMode.day ? Colors.black87 : Colors.black54; dayColor = mode == _DatePickerMode.day ? Colors.black87 : Colors.black54;
yearColor = mode == _DatePickerMode.year ? Colors.black87 : Colors.black54; yearColor = mode == _DatePickerMode.year ? Colors.black87 : Colors.black54;
...@@ -168,34 +167,43 @@ class _DatePickerHeader extends StatelessWidget { ...@@ -168,34 +167,43 @@ class _DatePickerHeader extends StatelessWidget {
yearColor = mode == _DatePickerMode.year ? Colors.white : Colors.white70; yearColor = mode == _DatePickerMode.year ? Colors.white : Colors.white70;
break; break;
} }
TextStyle dayStyle = headerTheme.display3.copyWith(color: dayColor, height: 1.0, fontSize: 100.0); TextStyle dayStyle = headerTextTheme.display1.copyWith(color: dayColor, height: 1.4);
TextStyle monthStyle = headerTheme.headline.copyWith(color: dayColor, height: 1.0); TextStyle yearStyle = headerTextTheme.subhead.copyWith(color: yearColor, height: 1.4);
TextStyle yearStyle = headerTheme.headline.copyWith(color: yearColor, height: 1.0);
Color backgroundColor;
switch (themeData.brightness) {
case ThemeBrightness.light:
backgroundColor = themeData.primaryColor;
break;
case ThemeBrightness.dark:
backgroundColor = themeData.backgroundColor;
break;
}
return new Container( return new Container(
padding: new EdgeInsets.all(10.0), height: 100.0,
decoration: new BoxDecoration(backgroundColor: theme.primaryColor), padding: const EdgeInsets.symmetric(horizontal: 24.0),
decoration: new BoxDecoration(backgroundColor: backgroundColor),
child: new Column( child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
new GestureDetector( new GestureDetector(
onTap: () => _handleChangeMode(_DatePickerMode.day), onTap: () => _handleChangeMode(_DatePickerMode.year),
child: new Text(new DateFormat('MMM').format(selectedDate).toUpperCase(), style: monthStyle) 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('d').format(selectedDate), style: dayStyle) child: new Text(new DateFormat('MMMEd').format(selectedDate), style: dayStyle)
), ),
new GestureDetector(
onTap: () => _handleChangeMode(_DatePickerMode.year),
child: new Text(new DateFormat('yyyy').format(selectedDate), style: yearStyle)
)
] ]
) )
); );
} }
} }
const double _kDayPickerRowHeight = 30.0; const Duration _kMonthScrollDuration = const Duration(milliseconds: 200);
const double _kDayPickerRowHeight = 42.0;
const int _kMaxDayPickerRowCount = 6; // A 31 day month that starts on Saturday. 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. // Two extra rows: one for the day-of-week header and one for the month header.
const double _kMaxDayPickerHeight = _kDayPickerRowHeight * (_kMaxDayPickerRowCount + 2); const double _kMaxDayPickerHeight = _kDayPickerRowHeight * (_kMaxDayPickerRowCount + 2);
...@@ -269,10 +277,6 @@ class DayPicker extends StatelessWidget { ...@@ -269,10 +277,6 @@ class DayPicker extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
final TextStyle headerStyle = themeData.textTheme.caption.copyWith(fontWeight: FontWeight.w700);
final TextStyle monthStyle = headerStyle.copyWith(fontSize: 14.0, height: 24.0 / 14.0);
final TextStyle dayStyle = headerStyle.copyWith(fontWeight: FontWeight.w500);
final int year = displayedMonth.year; final int year = displayedMonth.year;
final int month = displayedMonth.month; final int month = displayedMonth.month;
// Dart's Date time constructor is very forgiving and will understand // Dart's Date time constructor is very forgiving and will understand
...@@ -281,7 +285,7 @@ class DayPicker extends StatelessWidget { ...@@ -281,7 +285,7 @@ class DayPicker extends StatelessWidget {
// This assumes a start day of SUNDAY, but could be changed. // This assumes a start day of SUNDAY, but could be changed.
final int firstWeekday = new DateTime(year, month).weekday % 7; final int firstWeekday = new DateTime(year, month).weekday % 7;
final List<Widget> labels = <Widget>[]; final List<Widget> labels = <Widget>[];
labels.addAll(_getDayHeaders(headerStyle)); labels.addAll(_getDayHeaders(themeData.textTheme.caption));
for (int i = 0; true; ++i) { for (int i = 0; true; ++i) {
final int day = i - firstWeekday + 1; final int day = i - firstWeekday + 1;
if (day > daysInMonth) if (day > daysInMonth)
...@@ -290,13 +294,12 @@ class DayPicker extends StatelessWidget { ...@@ -290,13 +294,12 @@ class DayPicker extends StatelessWidget {
labels.add(new Container()); labels.add(new Container());
} else { } else {
BoxDecoration decoration; BoxDecoration decoration;
TextStyle itemStyle = dayStyle; TextStyle itemStyle = themeData.textTheme.body1;
if (selectedDate.year == year && selectedDate.month == month && selectedDate.day == day) { if (selectedDate.year == year && selectedDate.month == month && selectedDate.day == day) {
// The selected day gets a circle background highlight, and a contrasting text color. // The selected day gets a circle background highlight, and a contrasting text color.
final ThemeData theme = Theme.of(context); itemStyle = themeData.textTheme.body2.copyWith(
itemStyle = itemStyle.copyWith( color: (themeData.brightness == ThemeBrightness.light) ? Colors.white : Colors.black87
color: (theme.brightness == ThemeBrightness.light) ? Colors.white : Colors.black87
); );
decoration = new BoxDecoration( decoration = new BoxDecoration(
backgroundColor: themeData.accentColor, backgroundColor: themeData.accentColor,
...@@ -304,7 +307,7 @@ class DayPicker extends StatelessWidget { ...@@ -304,7 +307,7 @@ class DayPicker extends StatelessWidget {
); );
} else if (currentDate.year == year && currentDate.month == month && currentDate.day == day) { } else if (currentDate.year == year && currentDate.month == month && currentDate.day == day) {
// The current day gets a different text color. // The current day gets a different text color.
itemStyle = itemStyle.copyWith(color: themeData.accentColor); itemStyle = themeData.textTheme.body2.copyWith(color: themeData.accentColor);
} }
labels.add(new GestureDetector( labels.add(new GestureDetector(
...@@ -323,15 +326,24 @@ class DayPicker extends StatelessWidget { ...@@ -323,15 +326,24 @@ class DayPicker extends StatelessWidget {
} }
} }
return new Column( return new Padding(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, padding: const EdgeInsets.symmetric(horizontal: 8.0),
children: <Widget>[ child: new Column(
new Text(new DateFormat('MMMM y').format(displayedMonth), style: monthStyle), children: <Widget>[
new CustomGrid( new Container(
delegate: _kDayPickerGridDelegate, height: _kDayPickerRowHeight,
children: labels child: new Center(
) child: new Text(new DateFormat('yMMMM').format(displayedMonth),
] style: themeData.textTheme.subhead
)
)
),
new CustomGrid(
delegate: _kDayPickerGridDelegate,
children: labels
)
]
)
); );
} }
} }
...@@ -356,8 +368,7 @@ class MonthPicker extends StatefulWidget { ...@@ -356,8 +368,7 @@ class MonthPicker extends StatefulWidget {
this.selectedDate, this.selectedDate,
this.onChanged, this.onChanged,
this.firstDate, this.firstDate,
this.lastDate, this.lastDate
this.itemExtent
}) : super(key: key) { }) : super(key: key) {
assert(selectedDate != null); assert(selectedDate != null);
assert(onChanged != null); assert(onChanged != null);
...@@ -379,9 +390,6 @@ class MonthPicker extends StatefulWidget { ...@@ -379,9 +390,6 @@ class MonthPicker extends StatefulWidget {
/// The latest date the user is permitted to pick. /// The latest date the user is permitted to pick.
final DateTime lastDate; final DateTime lastDate;
/// The amount of vertical space to use for each month in the picker.
final double itemExtent;
@override @override
_MonthPickerState createState() => new _MonthPickerState(); _MonthPickerState createState() => new _MonthPickerState();
} }
...@@ -393,8 +401,15 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -393,8 +401,15 @@ class _MonthPickerState extends State<MonthPicker> {
_updateCurrentDate(); _updateCurrentDate();
} }
@override
void didUpdateConfig(MonthPicker oldConfig) {
if (config.selectedDate != oldConfig.selectedDate)
_dayPickerListKey = new GlobalKey<ScrollableState>();
}
DateTime _currentDate; DateTime _currentDate;
Timer _timer; Timer _timer;
GlobalKey<ScrollableState> _dayPickerListKey = new GlobalKey<ScrollableState>();
void _updateCurrentDate() { void _updateCurrentDate() {
_currentDate = new DateTime.now(); _currentDate = new DateTime.now();
...@@ -420,7 +435,7 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -420,7 +435,7 @@ class _MonthPickerState extends State<MonthPicker> {
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
DateTime displayedMonth = new DateTime(startDate.year + i ~/ 12, startDate.month + i % 12); DateTime displayedMonth = new DateTime(startDate.year + i ~/ 12, startDate.month + i % 12);
result.add(new DayPicker( result.add(new DayPicker(
key: new ObjectKey(displayedMonth), key: new ValueKey<DateTime>(displayedMonth),
selectedDate: config.selectedDate, selectedDate: config.selectedDate,
currentDate: _currentDate, currentDate: _currentDate,
onChanged: config.onChanged, onChanged: config.onChanged,
...@@ -430,14 +445,46 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -430,14 +445,46 @@ class _MonthPickerState extends State<MonthPicker> {
return result; return result;
} }
void _handleNextMonth() {
ScrollableState state = _dayPickerListKey.currentState;
state?.scrollTo(state.scrollOffset.round() + 1.0, duration: _kMonthScrollDuration);
}
void _handlePreviousMonth() {
ScrollableState state = _dayPickerListKey.currentState;
state?.scrollTo(state.scrollOffset.round() - 1.0, duration: _kMonthScrollDuration);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new ScrollableLazyList( return new Stack(
key: new ValueKey<DateTime>(config.selectedDate), children: <Widget>[
initialScrollOffset: config.itemExtent * _monthDelta(config.firstDate, config.selectedDate), new PageableLazyList(
itemExtent: config.itemExtent, key: _dayPickerListKey,
itemCount: _monthDelta(config.firstDate, config.lastDate) + 1, initialScrollOffset: _monthDelta(config.firstDate, config.selectedDate).toDouble(),
itemBuilder: _buildItems scrollDirection: Axis.horizontal,
itemCount: _monthDelta(config.firstDate, config.lastDate) + 1,
itemBuilder: _buildItems
),
new Positioned(
top: 0.0,
left: 8.0,
child: new IconButton(
icon: Icons.chevron_left,
tooltip: 'Previous month',
onPressed: _handlePreviousMonth
)
),
new Positioned(
top: 0.0,
right: 8.0,
child: new IconButton(
icon: Icons.chevron_right,
tooltip: 'Next month',
onPressed: _handleNextMonth
)
)
]
); );
} }
...@@ -500,29 +547,22 @@ class _YearPickerState extends State<YearPicker> { ...@@ -500,29 +547,22 @@ class _YearPickerState extends State<YearPicker> {
static const double _itemExtent = 50.0; static const double _itemExtent = 50.0;
List<Widget> _buildItems(BuildContext context, int start, int count) { List<Widget> _buildItems(BuildContext context, int start, int count) {
TextStyle style = Theme.of(context).textTheme.body1.copyWith(color: Colors.black54); final ThemeData themeData = Theme.of(context);
List<Widget> items = new List<Widget>(); final TextStyle style = themeData.textTheme.body1;
final List<Widget> items = new List<Widget>();
for (int i = start; i < start + count; i++) { for (int i = start; i < start + count; i++) {
int year = config.firstDate.year + i; final int year = config.firstDate.year + i;
String label = year.toString(); final TextStyle itemStyle = year == config.selectedDate.year ?
Widget item = new InkWell( themeData.textTheme.headline.copyWith(color: themeData.accentColor) : style;
key: new Key(label), items.add(new InkWell(
key: new ValueKey<int>(year),
onTap: () { onTap: () {
DateTime result = new DateTime(year, config.selectedDate.month, config.selectedDate.day); config.onChanged(new DateTime(year, config.selectedDate.month, config.selectedDate.day));
config.onChanged(result);
}, },
child: new Container( child: new Center(
height: _itemExtent, child: new Text(year.toString(), style: itemStyle)
decoration: year == config.selectedDate.year ? new BoxDecoration(
backgroundColor: Theme.of(context).backgroundColor,
shape: BoxShape.circle
) : null,
child: new Center(
child: new Text(label, style: style)
)
) )
); ));
items.add(item);
} }
return items; return items;
} }
......
...@@ -18,12 +18,8 @@ enum PageableListFlingBehavior { ...@@ -18,12 +18,8 @@ enum PageableListFlingBehavior {
stopAtNextPage stopAtNextPage
} }
/// Scrollable widget that scrolls one "page" at a time. abstract class PageableListBase extends Scrollable {
/// PageableListBase({
/// In a pageable list, one child is visible at a time. Scrolling the list
/// reveals either the next or previous child.
class PageableList extends Scrollable {
PageableList({
Key key, Key key,
double initialScrollOffset, double initialScrollOffset,
Axis scrollDirection: Axis.vertical, Axis scrollDirection: Axis.vertical,
...@@ -36,8 +32,7 @@ class PageableList extends Scrollable { ...@@ -36,8 +32,7 @@ class PageableList extends Scrollable {
this.itemsSnapAlignment: PageableListFlingBehavior.stopAtNextPage, this.itemsSnapAlignment: PageableListFlingBehavior.stopAtNextPage,
this.onPageChanged, this.onPageChanged,
this.duration: const Duration(milliseconds: 200), this.duration: const Duration(milliseconds: 200),
this.curve: Curves.ease, this.curve: Curves.ease
this.children
}) : super( }) : super(
key: key, key: key,
initialScrollOffset: initialScrollOffset, initialScrollOffset: initialScrollOffset,
...@@ -66,19 +61,102 @@ class PageableList extends Scrollable { ...@@ -66,19 +61,102 @@ class PageableList extends Scrollable {
/// The animation curve to use when animating to a given page. /// The animation curve to use when animating to a given page.
final Curve curve; final Curve curve;
int get _itemCount;
}
/// Scrollable widget that scrolls one "page" at a time.
///
/// In a pageable list, one child is visible at a time. Scrolling the list
/// reveals either the next or previous child.
class PageableList extends PageableListBase {
PageableList({
Key key,
double initialScrollOffset,
Axis scrollDirection: Axis.vertical,
ViewportAnchor scrollAnchor: ViewportAnchor.start,
ScrollListener onScrollStart,
ScrollListener onScroll,
ScrollListener onScrollEnd,
SnapOffsetCallback snapOffsetCallback,
bool itemsWrap: false,
PageableListFlingBehavior itemsSnapAlignment: PageableListFlingBehavior.stopAtNextPage,
ValueChanged<int> onPageChanged,
Duration duration: const Duration(milliseconds: 200),
Curve curve: Curves.ease,
this.children
}) : super(
key: key,
initialScrollOffset: initialScrollOffset,
scrollDirection: scrollDirection,
scrollAnchor: scrollAnchor,
onScrollStart: onScrollStart,
onScroll: onScroll,
onScrollEnd: onScrollEnd,
snapOffsetCallback: snapOffsetCallback,
itemsWrap: itemsWrap,
itemsSnapAlignment: itemsSnapAlignment,
onPageChanged: onPageChanged,
duration: duration,
curve: curve
);
/// The list of pages themselves. /// The list of pages themselves.
final Iterable<Widget> children; final Iterable<Widget> children;
@override
int get _itemCount => children?.length ?? 0;
@override @override
PageableListState<PageableList> createState() => new PageableListState<PageableList>(); PageableListState<PageableList> createState() => new PageableListState<PageableList>();
} }
/// State for a [PageableList] widget. class PageableLazyList extends PageableListBase {
/// PageableLazyList({
/// Widgets that subclass [PageableList] can subclass this class to have Key key,
/// sensible default behaviors for pageable lists. double initialScrollOffset,
class PageableListState<T extends PageableList> extends ScrollableState<T> { Axis scrollDirection: Axis.vertical,
int get _itemCount => config.children?.length ?? 0; ViewportAnchor scrollAnchor: ViewportAnchor.start,
ScrollListener onScrollStart,
ScrollListener onScroll,
ScrollListener onScrollEnd,
SnapOffsetCallback snapOffsetCallback,
PageableListFlingBehavior itemsSnapAlignment: PageableListFlingBehavior.stopAtNextPage,
ValueChanged<int> onPageChanged,
Duration duration: const Duration(milliseconds: 200),
Curve curve: Curves.ease,
this.itemCount,
this.itemBuilder
}) : super(
key: key,
initialScrollOffset: initialScrollOffset,
scrollDirection: scrollDirection,
scrollAnchor: scrollAnchor,
onScrollStart: onScrollStart,
onScroll: onScroll,
onScrollEnd: onScrollEnd,
snapOffsetCallback: snapOffsetCallback,
itemsWrap: false,
itemsSnapAlignment: itemsSnapAlignment,
onPageChanged: onPageChanged,
duration: duration,
curve: curve
);
/// The total number of list items.
final int itemCount;
/// A function that returns the pages themselves.
final ItemListBuilder itemBuilder;
@override
int get _itemCount => itemCount ?? 0;
@override
_PageableLazyListState createState() => new _PageableLazyListState();
}
abstract class _PageableListStateBase<T extends PageableListBase> extends ScrollableState<T> {
int get _itemCount => config._itemCount;
int _previousItemCount; int _previousItemCount;
double get _pixelsPerScrollUnit { double get _pixelsPerScrollUnit {
...@@ -124,7 +202,7 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> { ...@@ -124,7 +202,7 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> {
} }
@override @override
void didUpdateConfig(PageableList oldConfig) { void didUpdateConfig(PageableListBase oldConfig) {
super.didUpdateConfig(oldConfig); super.didUpdateConfig(oldConfig);
bool scrollBehaviorUpdateNeeded = config.scrollDirection != oldConfig.scrollDirection; bool scrollBehaviorUpdateNeeded = config.scrollDirection != oldConfig.scrollDirection;
...@@ -149,17 +227,6 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> { ...@@ -149,17 +227,6 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> {
)); ));
} }
@override
Widget buildContent(BuildContext context) {
return new PageViewport(
itemsWrap: config.itemsWrap,
mainAxis: config.scrollDirection,
anchor: config.scrollAnchor,
startOffset: scrollOffset,
children: config.children
);
}
UnboundedBehavior _unboundedBehavior; UnboundedBehavior _unboundedBehavior;
OverscrollBehavior _overscrollBehavior; OverscrollBehavior _overscrollBehavior;
...@@ -216,15 +283,44 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> { ...@@ -216,15 +283,44 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> {
} }
} }
class PageViewport extends VirtualViewportFromIterable { /// State for a [PageableList] widget.
PageViewport({ ///
this.startOffset: 0.0, /// Widgets that subclass [PageableList] can subclass this class to have
this.mainAxis: Axis.vertical, /// sensible default behaviors for pageable lists.
this.anchor: ViewportAnchor.start, class PageableListState<T extends PageableList> extends _PageableListStateBase<T> {
this.itemsWrap: false, @override
this.overlayPainter, Widget buildContent(BuildContext context) {
this.children return new PageViewport(
}) { itemsWrap: config.itemsWrap,
mainAxis: config.scrollDirection,
anchor: config.scrollAnchor,
startOffset: scrollOffset,
children: config.children
);
}
}
class _PageableLazyListState extends _PageableListStateBase<PageableLazyList> {
@override
Widget buildContent(BuildContext context) {
return new LazyPageViewport(
mainAxis: config.scrollDirection,
anchor: config.scrollAnchor,
startOffset: scrollOffset,
itemCount: config.itemCount,
itemBuilder: config.itemBuilder
);
}
}
class _VirtualPageViewport extends VirtualViewport {
_VirtualPageViewport(
this.startOffset,
this.mainAxis,
this.anchor,
this.itemsWrap,
this.overlayPainter
) {
assert(mainAxis != null); assert(mainAxis != null);
} }
...@@ -236,21 +332,18 @@ class PageViewport extends VirtualViewportFromIterable { ...@@ -236,21 +332,18 @@ class PageViewport extends VirtualViewportFromIterable {
final bool itemsWrap; final bool itemsWrap;
final RenderObjectPainter overlayPainter; final RenderObjectPainter overlayPainter;
@override
final Iterable<Widget> children;
@override @override
RenderList createRenderObject(BuildContext context) => new RenderList(); RenderList createRenderObject(BuildContext context) => new RenderList();
@override @override
_PageViewportElement createElement() => new _PageViewportElement(this); _VirtualPageViewportElement createElement() => new _VirtualPageViewportElement(this);
} }
class _PageViewportElement extends VirtualViewportElement { class _VirtualPageViewportElement extends VirtualViewportElement {
_PageViewportElement(PageViewport widget) : super(widget); _VirtualPageViewportElement(_VirtualPageViewport widget) : super(widget);
@override @override
PageViewport get widget => super.widget; _VirtualPageViewport get widget => super.widget;
@override @override
RenderList get renderObject => super.renderObject; RenderList get renderObject => super.renderObject;
...@@ -279,7 +372,7 @@ class _PageViewportElement extends VirtualViewportElement { ...@@ -279,7 +372,7 @@ class _PageViewportElement extends VirtualViewportElement {
} }
@override @override
void updateRenderObject(PageViewport oldWidget) { void updateRenderObject(_VirtualPageViewport oldWidget) {
renderObject renderObject
..mainAxis = widget.mainAxis ..mainAxis = widget.mainAxis
..overlayPainter = widget.overlayPainter; ..overlayPainter = widget.overlayPainter;
...@@ -342,3 +435,46 @@ class _PageViewportElement extends VirtualViewportElement { ...@@ -342,3 +435,46 @@ class _PageViewportElement extends VirtualViewportElement {
super.layout(constraints); super.layout(constraints);
} }
} }
class PageViewport extends _VirtualPageViewport with VirtualViewportFromIterable {
PageViewport({
double startOffset: 0.0,
Axis mainAxis: Axis.vertical,
ViewportAnchor anchor: ViewportAnchor.start,
bool itemsWrap: false,
RenderObjectPainter overlayPainter,
this.children
}) : super(
startOffset,
mainAxis,
anchor,
itemsWrap,
overlayPainter
);
@override
final Iterable<Widget> children;
}
class LazyPageViewport extends _VirtualPageViewport with VirtualViewportFromBuilder {
LazyPageViewport({
double startOffset: 0.0,
Axis mainAxis: Axis.vertical,
ViewportAnchor anchor: ViewportAnchor.start,
RenderObjectPainter overlayPainter,
this.itemCount,
this.itemBuilder
}) : super(
startOffset,
mainAxis,
anchor,
false, // Don't support wrapping yet.
overlayPainter
);
@override
final int itemCount;
@override
final ItemListBuilder itemBuilder;
}
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