Complete features of ScrollableList2
This patch implements the remaining missing features of ScrollableList2. It should now be nearly a drop-in replacement for ScrollableList. The next patch will switch callers over to the new machinery.
This commit is contained in:
parent
f72c8f6db7
commit
37106ea603
@ -30,22 +30,25 @@ class ScrollbarPainter extends ScrollableListPainter {
|
||||
Point thumbOrigin;
|
||||
Size thumbSize;
|
||||
|
||||
if (isVertical) {
|
||||
double thumbHeight = viewportBounds.height * viewportBounds.height / contentExtent;
|
||||
thumbHeight = thumbHeight.clamp(_kMinScrollbarThumbLength, viewportBounds.height);
|
||||
final double maxThumbTop = viewportBounds.height - thumbHeight;
|
||||
double thumbTop = (scrollOffset / (contentExtent - viewportBounds.height)) * maxThumbTop;
|
||||
thumbTop = viewportBounds.top + thumbTop.clamp(0.0, maxThumbTop);
|
||||
thumbOrigin = new Point(viewportBounds.right - _kScrollbarThumbGirth, thumbTop);
|
||||
thumbSize = new Size(_kScrollbarThumbGirth, thumbHeight);
|
||||
} else {
|
||||
double thumbWidth = viewportBounds.width * viewportBounds.width / contentExtent;
|
||||
thumbWidth = thumbWidth.clamp(_kMinScrollbarThumbLength, viewportBounds.width);
|
||||
final double maxThumbLeft = viewportBounds.width - thumbWidth;
|
||||
double thumbLeft = (scrollOffset / (contentExtent - viewportBounds.width)) * maxThumbLeft;
|
||||
thumbLeft = viewportBounds.left + thumbLeft.clamp(0.0, maxThumbLeft);
|
||||
thumbOrigin = new Point(thumbLeft, viewportBounds.height - _kScrollbarThumbGirth);
|
||||
thumbSize = new Size(thumbWidth, _kScrollbarThumbGirth);
|
||||
switch (scrollDirection) {
|
||||
case ScrollDirection.vertical:
|
||||
double thumbHeight = viewportBounds.height * viewportBounds.height / contentExtent;
|
||||
thumbHeight = thumbHeight.clamp(_kMinScrollbarThumbLength, viewportBounds.height);
|
||||
final double maxThumbTop = viewportBounds.height - thumbHeight;
|
||||
double thumbTop = (scrollOffset / (contentExtent - viewportBounds.height)) * maxThumbTop;
|
||||
thumbTop = viewportBounds.top + thumbTop.clamp(0.0, maxThumbTop);
|
||||
thumbOrigin = new Point(viewportBounds.right - _kScrollbarThumbGirth, thumbTop);
|
||||
thumbSize = new Size(_kScrollbarThumbGirth, thumbHeight);
|
||||
break;
|
||||
case ScrollDirection.horizontal:
|
||||
double thumbWidth = viewportBounds.width * viewportBounds.width / contentExtent;
|
||||
thumbWidth = thumbWidth.clamp(_kMinScrollbarThumbLength, viewportBounds.width);
|
||||
final double maxThumbLeft = viewportBounds.width - thumbWidth;
|
||||
double thumbLeft = (scrollOffset / (contentExtent - viewportBounds.width)) * maxThumbLeft;
|
||||
thumbLeft = viewportBounds.left + thumbLeft.clamp(0.0, maxThumbLeft);
|
||||
thumbOrigin = new Point(thumbLeft, viewportBounds.height - _kScrollbarThumbGirth);
|
||||
thumbSize = new Size(thumbWidth, _kScrollbarThumbGirth);
|
||||
break;
|
||||
}
|
||||
|
||||
paintThumb(context, thumbOrigin & thumbSize);
|
||||
@ -65,7 +68,7 @@ class ScrollbarPainter extends ScrollableListPainter {
|
||||
..variable = new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.ease)
|
||||
..addListener(() {
|
||||
_opacity = _fade.value;
|
||||
renderer?.markNeedsPaint();
|
||||
renderObject?.markNeedsPaint();
|
||||
});
|
||||
return _fade.forward();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
import 'box.dart';
|
||||
import 'object.dart';
|
||||
import 'viewport.dart';
|
||||
|
||||
/// Parent data for use with [RenderBlockBase].
|
||||
class BlockParentData extends ContainerBoxParentDataMixin<RenderBox> { }
|
||||
@ -32,8 +33,10 @@ typedef double _Constrainer(double value);
|
||||
/// children. Because blocks expand in the main axis, blocks must be given
|
||||
/// unlimited space in the main axis, typically by being contained in a
|
||||
/// viewport with a scrolling direction that matches the block's main axis.
|
||||
abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin<RenderBox, BlockParentData>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData> {
|
||||
abstract class RenderBlockBase extends RenderBox
|
||||
with ContainerRenderObjectMixin<RenderBox, BlockParentData>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox, BlockParentData>
|
||||
implements RenderScrollable {
|
||||
|
||||
RenderBlockBase({
|
||||
List<RenderBox> children,
|
||||
@ -82,6 +85,9 @@ abstract class RenderBlockBase extends RenderBox with ContainerRenderObjectMixin
|
||||
/// Whether the main axis is vertical.
|
||||
bool get isVertical => _direction == BlockDirection.vertical;
|
||||
|
||||
// TODO(abarth): Remove BlockDirection in favor of ScrollDirection.
|
||||
ScrollDirection get scrollDirection => isVertical ? ScrollDirection.vertical : ScrollDirection.horizontal;
|
||||
|
||||
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
|
||||
if (isVertical)
|
||||
return new BoxConstraints.tightFor(width: constraints.constrainWidth(constraints.maxWidth),
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'box.dart';
|
||||
import 'object.dart';
|
||||
import 'viewport.dart';
|
||||
@ -9,18 +11,23 @@ import 'viewport.dart';
|
||||
/// Parent data for use with [RenderList].
|
||||
class ListParentData extends ContainerBoxParentDataMixin<RenderBox> { }
|
||||
|
||||
class RenderList extends RenderVirtualViewport<ListParentData> {
|
||||
class RenderList extends RenderVirtualViewport<ListParentData> implements RenderScrollable {
|
||||
RenderList({
|
||||
List<RenderBox> children,
|
||||
double itemExtent,
|
||||
EdgeDims padding,
|
||||
int virtualChildCount,
|
||||
Offset paintOffset: Offset.zero,
|
||||
ScrollDirection scrollDirection: ScrollDirection.vertical,
|
||||
LayoutCallback callback
|
||||
}) : _itemExtent = itemExtent, super(
|
||||
virtualChildCount: virtualChildCount,
|
||||
paintOffset: paintOffset,
|
||||
callback: callback
|
||||
) {
|
||||
}) : _itemExtent = itemExtent,
|
||||
_padding = padding,
|
||||
_scrollDirection = scrollDirection,
|
||||
super(
|
||||
virtualChildCount: virtualChildCount,
|
||||
paintOffset: paintOffset,
|
||||
callback: callback
|
||||
) {
|
||||
assert(itemExtent != null);
|
||||
addAll(children);
|
||||
}
|
||||
@ -35,50 +42,124 @@ class RenderList extends RenderVirtualViewport<ListParentData> {
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
EdgeDims get padding => _padding;
|
||||
EdgeDims _padding;
|
||||
void set padding (EdgeDims newValue) {
|
||||
if (_padding == newValue)
|
||||
return;
|
||||
_padding = newValue;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
ScrollDirection get scrollDirection => _scrollDirection;
|
||||
ScrollDirection _scrollDirection;
|
||||
void set scrollDirection (ScrollDirection newValue) {
|
||||
if (_scrollDirection == newValue)
|
||||
return;
|
||||
_scrollDirection = newValue;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
void setupParentData(RenderBox child) {
|
||||
if (child.parentData is! ListParentData)
|
||||
child.parentData = new ListParentData();
|
||||
}
|
||||
|
||||
double get _preferredMainAxisExtent => itemExtent * virtualChildCount;
|
||||
double get _scrollAxisPadding {
|
||||
switch (scrollDirection) {
|
||||
case ScrollDirection.vertical:
|
||||
return padding.vertical;
|
||||
case ScrollDirection.horizontal:
|
||||
return padding.horizontal;
|
||||
}
|
||||
}
|
||||
|
||||
double get _preferredExtent {
|
||||
double extent = itemExtent * virtualChildCount;
|
||||
if (padding != null)
|
||||
extent += _scrollAxisPadding;
|
||||
return extent;
|
||||
}
|
||||
|
||||
double _getIntrinsicWidth(BoxConstraints constraints) {
|
||||
assert(constraints.isNormalized);
|
||||
switch (scrollDirection) {
|
||||
case ScrollDirection.vertical:
|
||||
return constraints.constrainWidth(0.0);
|
||||
case ScrollDirection.horizontal:
|
||||
return constraints.constrainWidth(_preferredExtent);
|
||||
}
|
||||
}
|
||||
|
||||
double getMinIntrinsicWidth(BoxConstraints constraints) {
|
||||
assert(constraints.isNormalized);
|
||||
return constraints.constrainWidth(0.0);
|
||||
return _getIntrinsicWidth(constraints);
|
||||
}
|
||||
|
||||
double getMaxIntrinsicWidth(BoxConstraints constraints) {
|
||||
return _getIntrinsicWidth(constraints);
|
||||
}
|
||||
|
||||
double _getIntrinsicHeight(BoxConstraints constraints) {
|
||||
assert(constraints.isNormalized);
|
||||
return constraints.constrainWidth(0.0);
|
||||
switch (scrollDirection) {
|
||||
case ScrollDirection.vertical:
|
||||
return constraints.constrainHeight(_preferredExtent);
|
||||
case ScrollDirection.horizontal:
|
||||
return constraints.constrainHeight(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
double getMinIntrinsicHeight(BoxConstraints constraints) {
|
||||
assert(constraints.isNormalized);
|
||||
return constraints.constrainHeight(_preferredMainAxisExtent);
|
||||
return _getIntrinsicHeight(constraints);
|
||||
}
|
||||
|
||||
double getMaxIntrinsicHeight(BoxConstraints constraints) {
|
||||
assert(constraints.isNormalized);
|
||||
return constraints.constrainHeight(_preferredMainAxisExtent);
|
||||
return _getIntrinsicHeight(constraints);
|
||||
}
|
||||
|
||||
void performLayout() {
|
||||
double height = _preferredMainAxisExtent;
|
||||
size = new Size(constraints.maxWidth, constraints.constrainHeight(height));
|
||||
size = new Size(constraints.maxWidth,
|
||||
constraints.constrainHeight(_preferredExtent));
|
||||
|
||||
if (callback != null)
|
||||
invokeLayoutCallback(callback);
|
||||
|
||||
BoxConstraints innerConstraints =
|
||||
new BoxConstraints.tightFor(width: size.width, height: itemExtent);
|
||||
double itemWidth;
|
||||
double itemHeight;
|
||||
|
||||
double x = 0.0;
|
||||
double dx = 0.0;
|
||||
|
||||
double y = 0.0;
|
||||
double dy = 0.0;
|
||||
|
||||
switch (scrollDirection) {
|
||||
case ScrollDirection.vertical:
|
||||
itemWidth = math.max(0, size.width - (padding == null ? 0.0 : padding.horizontal));
|
||||
itemHeight = itemExtent;
|
||||
y = padding != null ? padding.top : 0.0;
|
||||
dy = itemExtent;
|
||||
break;
|
||||
case ScrollDirection.horizontal:
|
||||
itemWidth = itemExtent;
|
||||
itemHeight = math.max(0, size.height - (padding == null ? 0.0 : padding.vertical));
|
||||
x = padding != null ? padding.left : 0.0;
|
||||
dx = itemExtent;
|
||||
break;
|
||||
}
|
||||
|
||||
BoxConstraints innerConstraints =
|
||||
new BoxConstraints.tightFor(width: itemWidth, height: itemHeight);
|
||||
|
||||
int childIndex = 0;
|
||||
RenderBox child = firstChild;
|
||||
while (child != null) {
|
||||
child.layout(innerConstraints);
|
||||
|
||||
final ListParentData childParentData = child.parentData;
|
||||
childParentData.offset = new Offset(0.0, childIndex * itemExtent);
|
||||
childIndex += 1;
|
||||
childParentData.offset = new Offset(x, y);
|
||||
x += dx;
|
||||
y += dy;
|
||||
|
||||
assert(child.parentData == childParentData);
|
||||
child = childParentData.nextSibling;
|
||||
}
|
||||
|
@ -18,6 +18,10 @@ enum ScrollDirection {
|
||||
vertical,
|
||||
}
|
||||
|
||||
abstract class RenderScrollable {
|
||||
ScrollDirection get scrollDirection;
|
||||
}
|
||||
|
||||
/// A render object that's bigger on the inside.
|
||||
///
|
||||
/// The child of a viewport can layout to a larger size than the viewport
|
||||
@ -27,7 +31,8 @@ enum ScrollDirection {
|
||||
///
|
||||
/// Viewport is the core scrolling primitive in the system, but it can be used
|
||||
/// in other situations.
|
||||
class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
|
||||
class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox>
|
||||
implements RenderScrollable {
|
||||
|
||||
RenderViewport({
|
||||
RenderBox child,
|
||||
@ -177,10 +182,12 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
|
||||
RenderVirtualViewport({
|
||||
int virtualChildCount,
|
||||
Offset paintOffset,
|
||||
LayoutCallback callback
|
||||
LayoutCallback callback,
|
||||
Painter overlayPainter
|
||||
}) : _virtualChildCount = virtualChildCount,
|
||||
_paintOffset = paintOffset,
|
||||
_callback = callback;
|
||||
_callback = callback,
|
||||
_overlayPainter = overlayPainter;
|
||||
|
||||
int get virtualChildCount => _virtualChildCount ?? childCount;
|
||||
int _virtualChildCount;
|
||||
@ -217,8 +224,31 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
Painter get overlayPainter => _overlayPainter;
|
||||
Painter _overlayPainter;
|
||||
void set overlayPainter(Painter value) {
|
||||
if (_overlayPainter == value)
|
||||
return;
|
||||
if (attached)
|
||||
_overlayPainter?.detach();
|
||||
_overlayPainter = value;
|
||||
if (attached)
|
||||
_overlayPainter?.attach(this);
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
void attach() {
|
||||
super.attach();
|
||||
_overlayPainter?.attach(this);
|
||||
}
|
||||
|
||||
void detach() {
|
||||
super.detach();
|
||||
_overlayPainter?.detach();
|
||||
}
|
||||
|
||||
void applyPaintTransform(RenderBox child, Matrix4 transform) {
|
||||
super.applyPaintTransform(child, transform.translate(paintOffset));
|
||||
super.applyPaintTransform(child, transform.translate(paintOffset.dx, paintOffset.dy));
|
||||
}
|
||||
|
||||
bool hitTestChildren(HitTestResult result, { Point position }) {
|
||||
@ -227,6 +257,7 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
|
||||
|
||||
void _paintContents(PaintingContext context, Offset offset) {
|
||||
defaultPaint(context, offset + paintOffset);
|
||||
_overlayPainter?.paint(context, offset);
|
||||
}
|
||||
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
|
@ -445,15 +445,18 @@ class Block extends StatelessComponent {
|
||||
|
||||
abstract class ScrollableListPainter extends Painter {
|
||||
void attach(RenderObject renderObject) {
|
||||
assert(renderObject is RenderBlockViewport);
|
||||
assert(renderObject is RenderBox);
|
||||
assert(renderObject is RenderScrollable);
|
||||
super.attach(renderObject);
|
||||
}
|
||||
|
||||
RenderBlockViewport get renderer => renderObject;
|
||||
RenderBox get renderObject => super.renderObject;
|
||||
|
||||
bool get isVertical => renderer.isVertical;
|
||||
ScrollDirection get scrollDirection {
|
||||
return (renderObject as RenderScrollable)?.scrollDirection;
|
||||
}
|
||||
|
||||
Size get viewportSize => renderer.size;
|
||||
Size get viewportSize => renderObject.size;
|
||||
|
||||
double get contentExtent => _contentExtent;
|
||||
double _contentExtent = 0.0;
|
||||
@ -463,7 +466,7 @@ abstract class ScrollableListPainter extends Painter {
|
||||
if (_contentExtent == value)
|
||||
return;
|
||||
_contentExtent = value;
|
||||
renderer?.markNeedsPaint();
|
||||
renderObject?.markNeedsPaint();
|
||||
}
|
||||
|
||||
double get scrollOffset => _scrollOffset;
|
||||
@ -473,7 +476,7 @@ abstract class ScrollableListPainter extends Painter {
|
||||
if (_scrollOffset == value)
|
||||
return;
|
||||
_scrollOffset = value;
|
||||
renderer?.markNeedsPaint();
|
||||
renderObject?.markNeedsPaint();
|
||||
}
|
||||
|
||||
/// Called when a scroll starts. Subclasses may override this method to
|
||||
@ -675,7 +678,7 @@ class ScrollableList<T> extends ScrollableWidgetList {
|
||||
double snapAlignmentOffset: 0.0,
|
||||
this.items,
|
||||
this.itemBuilder,
|
||||
itemsWrap: false,
|
||||
bool itemsWrap: false,
|
||||
double itemExtent,
|
||||
EdgeDims padding,
|
||||
ScrollableListPainter scrollableListPainter
|
||||
|
@ -79,6 +79,9 @@ class GridViewport extends VirtualViewport {
|
||||
final ExtentsChangedCallback onExtentsChanged;
|
||||
final List<Widget> children;
|
||||
|
||||
// TODO(abarth): Support horizontal scrolling;
|
||||
ScrollDirection get scrollDirection => ScrollDirection.vertical;
|
||||
|
||||
RenderGrid createRenderObject() => new RenderGrid(delegate: delegate);
|
||||
|
||||
_GridViewportElement createElement() => new _GridViewportElement(this);
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'framework.dart';
|
||||
import 'scrollable.dart';
|
||||
import 'virtual_viewport.dart';
|
||||
@ -13,22 +15,30 @@ class ScrollableList2 extends Scrollable {
|
||||
ScrollableList2({
|
||||
Key key,
|
||||
double initialScrollOffset,
|
||||
ScrollDirection scrollDirection: ScrollDirection.vertical,
|
||||
ScrollListener onScroll,
|
||||
SnapOffsetCallback snapOffsetCallback,
|
||||
double snapAlignmentOffset: 0.0,
|
||||
this.itemExtent,
|
||||
this.itemsWrap: false,
|
||||
this.padding,
|
||||
this.scrollableListPainter,
|
||||
this.children
|
||||
}) : super(
|
||||
key: key,
|
||||
initialScrollOffset: initialScrollOffset,
|
||||
// TODO(abarth): Support horizontal offsets.
|
||||
scrollDirection: ScrollDirection.vertical,
|
||||
scrollDirection: scrollDirection,
|
||||
onScroll: onScroll,
|
||||
snapOffsetCallback: snapOffsetCallback,
|
||||
snapAlignmentOffset: snapAlignmentOffset
|
||||
);
|
||||
) {
|
||||
assert(itemExtent != null);
|
||||
}
|
||||
|
||||
final double itemExtent;
|
||||
final bool itemsWrap;
|
||||
final EdgeDims padding;
|
||||
final ScrollableListPainter scrollableListPainter;
|
||||
final List<Widget> children;
|
||||
|
||||
ScrollableState createState() => new _ScrollableList2State();
|
||||
@ -39,20 +49,40 @@ class _ScrollableList2State extends ScrollableState<ScrollableList2> {
|
||||
ExtentScrollBehavior get scrollBehavior => super.scrollBehavior;
|
||||
|
||||
void _handleExtentsChanged(double contentExtent, double containerExtent) {
|
||||
config.scrollableListPainter?.contentExtent = contentExtent;
|
||||
setState(() {
|
||||
scrollTo(scrollBehavior.updateExtents(
|
||||
contentExtent: contentExtent,
|
||||
contentExtent: config.itemsWrap ? double.INFINITY : contentExtent,
|
||||
containerExtent: containerExtent,
|
||||
scrollOffset: scrollOffset
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
void dispatchOnScrollStart() {
|
||||
super.dispatchOnScrollStart();
|
||||
config.scrollableListPainter?.scrollStarted();
|
||||
}
|
||||
|
||||
void dispatchOnScroll() {
|
||||
super.dispatchOnScroll();
|
||||
config.scrollableListPainter?.scrollOffset = scrollOffset;
|
||||
}
|
||||
|
||||
void dispatchOnScrollEnd() {
|
||||
super.dispatchOnScrollEnd();
|
||||
config.scrollableListPainter?.scrollEnded();
|
||||
}
|
||||
|
||||
Widget buildContent(BuildContext context) {
|
||||
return new ListViewport(
|
||||
startOffset: scrollOffset,
|
||||
itemExtent: config.itemExtent,
|
||||
onExtentsChanged: _handleExtentsChanged,
|
||||
startOffset: scrollOffset,
|
||||
scrollDirection: config.scrollDirection,
|
||||
itemExtent: config.itemExtent,
|
||||
itemsWrap: config.itemsWrap,
|
||||
padding: config.padding,
|
||||
overlayPainter: config.scrollableListPainter,
|
||||
children: config.children
|
||||
);
|
||||
}
|
||||
@ -61,15 +91,26 @@ class _ScrollableList2State extends ScrollableState<ScrollableList2> {
|
||||
class ListViewport extends VirtualViewport {
|
||||
ListViewport({
|
||||
Key key,
|
||||
this.startOffset,
|
||||
this.itemExtent,
|
||||
this.onExtentsChanged,
|
||||
this.startOffset: 0.0,
|
||||
this.scrollDirection: ScrollDirection.vertical,
|
||||
this.itemExtent,
|
||||
this.itemsWrap: false,
|
||||
this.padding,
|
||||
this.overlayPainter,
|
||||
this.children
|
||||
});
|
||||
}) {
|
||||
assert(scrollDirection != null);
|
||||
assert(itemExtent != null);
|
||||
}
|
||||
|
||||
final double startOffset;
|
||||
final double itemExtent;
|
||||
final ExtentsChangedCallback onExtentsChanged;
|
||||
final double startOffset;
|
||||
final ScrollDirection scrollDirection;
|
||||
final double itemExtent;
|
||||
final bool itemsWrap;
|
||||
final EdgeDims padding;
|
||||
final Painter overlayPainter;
|
||||
final List<Widget> children;
|
||||
|
||||
RenderList createRenderObject() => new RenderList(itemExtent: itemExtent);
|
||||
@ -95,21 +136,39 @@ class _ListViewportElement extends VirtualViewportElement<ListViewport> {
|
||||
double _repaintOffsetLimit;
|
||||
|
||||
void updateRenderObject() {
|
||||
renderObject.scrollDirection = widget.scrollDirection;
|
||||
renderObject.itemExtent = widget.itemExtent;
|
||||
renderObject.padding = widget.padding;
|
||||
renderObject.overlayPainter = widget.overlayPainter;
|
||||
super.updateRenderObject();
|
||||
}
|
||||
|
||||
double _contentExtent;
|
||||
double _containerExtent;
|
||||
|
||||
double _getContainerExtentFromRenderObject() {
|
||||
switch (widget.scrollDirection) {
|
||||
case ScrollDirection.vertical:
|
||||
return renderObject.size.height;
|
||||
case ScrollDirection.horizontal:
|
||||
return renderObject.size.width;
|
||||
}
|
||||
}
|
||||
|
||||
void layout(BoxConstraints constraints) {
|
||||
double contentExtent = widget.itemExtent * widget.children.length;
|
||||
double containerExtent = renderObject.size.height;
|
||||
double containerExtent = _getContainerExtentFromRenderObject();
|
||||
|
||||
_materializedChildBase = math.max(0, widget.startOffset ~/ widget.itemExtent);
|
||||
int materializedChildLimit = math.max(0, ((widget.startOffset + containerExtent) / widget.itemExtent).ceil());
|
||||
|
||||
if (!widget.itemsWrap) {
|
||||
int length = widget.children.length;
|
||||
_materializedChildBase = math.min(length, _materializedChildBase);
|
||||
materializedChildLimit = math.min(length, materializedChildLimit);
|
||||
}
|
||||
|
||||
_materializedChildBase = (widget.startOffset ~/ widget.itemExtent).clamp(0, widget.children.length);
|
||||
int materializedChildLimit = ((widget.startOffset + containerExtent) / widget.itemExtent).ceil().clamp(0, widget.children.length);
|
||||
_materializedChildCount = materializedChildLimit - _materializedChildBase;
|
||||
|
||||
_repaintOffsetBase = _materializedChildBase * widget.itemExtent;
|
||||
_repaintOffsetLimit = materializedChildLimit * widget.itemExtent;
|
||||
|
||||
|
@ -11,6 +11,7 @@ typedef void ExtentsChangedCallback(double contentExtent, double containerExtent
|
||||
|
||||
abstract class VirtualViewport extends RenderObjectWidget {
|
||||
double get startOffset;
|
||||
ScrollDirection get scrollDirection;
|
||||
List<Widget> get children;
|
||||
}
|
||||
|
||||
@ -52,8 +53,23 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO
|
||||
}
|
||||
|
||||
void _updatePaintOffset() {
|
||||
renderObject.paintOffset =
|
||||
renderObject.paintOffset = new Offset(0.0, -(widget.startOffset - repaintOffsetBase));
|
||||
switch (widget.scrollDirection) {
|
||||
case ScrollDirection.vertical:
|
||||
renderObject.paintOffset = new Offset(0.0, -(widget.startOffset - repaintOffsetBase));
|
||||
break;
|
||||
case ScrollDirection.horizontal:
|
||||
renderObject.paintOffset = new Offset(-(widget.startOffset - repaintOffsetBase), 0.0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double get _containerExtent {
|
||||
switch (widget.scrollDirection) {
|
||||
case ScrollDirection.vertical:
|
||||
return renderObject.size.height;
|
||||
case ScrollDirection.horizontal:
|
||||
return renderObject.size.width;
|
||||
}
|
||||
}
|
||||
|
||||
void updateRenderObject() {
|
||||
@ -67,7 +83,7 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO
|
||||
if (!renderObject.needsLayout) {
|
||||
if (repaintOffsetBase != null && widget.startOffset < repaintOffsetBase)
|
||||
renderObject.markNeedsLayout();
|
||||
else if (repaintOffsetLimit != null && widget.startOffset + renderObject.size.height > repaintOffsetLimit)
|
||||
else if (repaintOffsetLimit != null && widget.startOffset + _containerExtent > repaintOffsetLimit)
|
||||
renderObject.markNeedsLayout();
|
||||
}
|
||||
}
|
||||
@ -88,7 +104,7 @@ abstract class VirtualViewportElement<T extends VirtualViewport> extends RenderO
|
||||
List<Widget> newWidgets = new List<Widget>(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int childIndex = base + i;
|
||||
Widget child = widget.children[childIndex];
|
||||
Widget child = widget.children[childIndex % widget.children.length];
|
||||
Key key = new ValueKey(child.key ?? childIndex);
|
||||
newWidgets[i] = new RepaintBoundary(key: key, child: child);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user