mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Rationalize RenderViewport and RenderVirtualViewport
These classes now share more code and have feature parity.
This commit is contained in:
parent
70d96ecb0b
commit
3c8cbef973
@ -771,7 +771,7 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
|
||||
onSizeChanged: _handleViewportSizeChanged,
|
||||
child: new Viewport(
|
||||
scrollDirection: Axis.horizontal,
|
||||
scrollOffset: new Offset(scrollOffset, 0.0),
|
||||
paintOffset: scrollOffsetToPixelDelta(scrollOffset),
|
||||
child: contents
|
||||
)
|
||||
);
|
||||
|
||||
@ -314,10 +314,12 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> {
|
||||
int virtualChildBase: 0,
|
||||
int virtualChildCount,
|
||||
Offset paintOffset: Offset.zero,
|
||||
Painter overlayPainter,
|
||||
LayoutCallback callback
|
||||
}) : _delegate = delegate, _virtualChildBase = virtualChildBase, super(
|
||||
virtualChildCount: virtualChildCount,
|
||||
paintOffset: paintOffset,
|
||||
overlayPainter: overlayPainter,
|
||||
callback: callback
|
||||
) {
|
||||
assert(delegate != null);
|
||||
@ -338,6 +340,15 @@ class RenderGrid extends RenderVirtualViewport<GridParentData> {
|
||||
_delegate = newDelegate;
|
||||
}
|
||||
|
||||
void set scrollDirection(Axis value) {
|
||||
assert(() {
|
||||
if (value != Axis.vertical)
|
||||
throw new RenderingError('RenderGrid doesn\'t yet support horizontal scrolling.');
|
||||
return true;
|
||||
});
|
||||
super.scrollDirection = value;
|
||||
}
|
||||
|
||||
int get virtualChildCount => super.virtualChildCount ?? childCount;
|
||||
|
||||
/// The virtual index of the first child.
|
||||
|
||||
@ -11,7 +11,7 @@ import 'viewport.dart';
|
||||
/// Parent data for use with [RenderList].
|
||||
class ListParentData extends ContainerBoxParentDataMixin<RenderBox> { }
|
||||
|
||||
class RenderList extends RenderVirtualViewport<ListParentData> implements HasScrollDirection {
|
||||
class RenderList extends RenderVirtualViewport<ListParentData> {
|
||||
RenderList({
|
||||
List<RenderBox> children,
|
||||
double itemExtent,
|
||||
@ -19,13 +19,15 @@ class RenderList extends RenderVirtualViewport<ListParentData> implements HasScr
|
||||
int virtualChildCount,
|
||||
Offset paintOffset: Offset.zero,
|
||||
Axis scrollDirection: Axis.vertical,
|
||||
Painter overlayPainter,
|
||||
LayoutCallback callback
|
||||
}) : _itemExtent = itemExtent,
|
||||
_padding = padding,
|
||||
_scrollDirection = scrollDirection,
|
||||
super(
|
||||
virtualChildCount: virtualChildCount,
|
||||
paintOffset: paintOffset,
|
||||
scrollDirection: scrollDirection,
|
||||
overlayPainter: overlayPainter,
|
||||
callback: callback
|
||||
) {
|
||||
addAll(children);
|
||||
@ -50,15 +52,6 @@ class RenderList extends RenderVirtualViewport<ListParentData> implements HasScr
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
Axis get scrollDirection => _scrollDirection;
|
||||
Axis _scrollDirection;
|
||||
void set scrollDirection (Axis newValue) {
|
||||
if (_scrollDirection == newValue)
|
||||
return;
|
||||
_scrollDirection = newValue;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
void setupParentData(RenderBox child) {
|
||||
if (child.parentData is! ListParentData)
|
||||
child.parentData = new ListParentData();
|
||||
|
||||
@ -18,26 +18,22 @@ abstract class HasScrollDirection {
|
||||
Axis get scrollDirection;
|
||||
}
|
||||
|
||||
/// A render object that's bigger on the inside.
|
||||
/// A base class for render objects that are bigger on the inside.
|
||||
///
|
||||
/// The child of a viewport can layout to a larger size than the viewport
|
||||
/// itself. If that happens, only a portion of the child will be visible through
|
||||
/// the viewport. The portion of the child that is visible is controlled by the
|
||||
/// scroll offset.
|
||||
///
|
||||
/// Viewport is the core scrolling primitive in the system, but it can be used
|
||||
/// in other situations.
|
||||
class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox>
|
||||
implements HasScrollDirection {
|
||||
|
||||
RenderViewport({
|
||||
RenderBox child,
|
||||
Offset scrollOffset: Offset.zero,
|
||||
Axis scrollDirection: Axis.vertical
|
||||
}) : _scrollOffset = scrollOffset,
|
||||
_scrollDirection = scrollDirection {
|
||||
assert(_offsetIsSane(scrollOffset, scrollDirection));
|
||||
this.child = child;
|
||||
/// This class holds the common fields for viewport render objects but does not
|
||||
/// have a child model. See [RenderViewport] for a viewport with a single child
|
||||
/// and [RenderVirtualViewport] for a viewport with multiple children.
|
||||
class RenderViewportBase extends RenderBox implements HasScrollDirection {
|
||||
RenderViewportBase(
|
||||
Offset paintOffset,
|
||||
Axis scrollDirection,
|
||||
Painter overlayPainter
|
||||
) : _paintOffset = paintOffset,
|
||||
_scrollDirection = scrollDirection,
|
||||
_overlayPainter = overlayPainter {
|
||||
assert(paintOffset != null);
|
||||
assert(scrollDirection != null);
|
||||
assert(_offsetIsSane(_paintOffset, scrollDirection));
|
||||
}
|
||||
|
||||
bool _offsetIsSane(Offset offset, Axis direction) {
|
||||
@ -52,13 +48,14 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
|
||||
/// The offset at which to paint the child.
|
||||
///
|
||||
/// The offset can be non-zero only in the [scrollDirection].
|
||||
Offset get scrollOffset => _scrollOffset;
|
||||
Offset _scrollOffset;
|
||||
void set scrollOffset(Offset value) {
|
||||
if (value == _scrollOffset)
|
||||
Offset get paintOffset => _paintOffset;
|
||||
Offset _paintOffset;
|
||||
void set paintOffset(Offset value) {
|
||||
assert(value != null);
|
||||
if (value == _paintOffset)
|
||||
return;
|
||||
assert(_offsetIsSane(value, scrollDirection));
|
||||
_scrollOffset = value;
|
||||
_paintOffset = value;
|
||||
markNeedsPaint();
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
@ -71,13 +68,69 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
|
||||
Axis get scrollDirection => _scrollDirection;
|
||||
Axis _scrollDirection;
|
||||
void set scrollDirection(Axis value) {
|
||||
assert(value != null);
|
||||
if (value == _scrollDirection)
|
||||
return;
|
||||
assert(_offsetIsSane(scrollOffset, value));
|
||||
assert(_offsetIsSane(_paintOffset, value));
|
||||
_scrollDirection = value;
|
||||
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();
|
||||
}
|
||||
|
||||
Offset get _paintOffsetRoundedToIntegerDevicePixels {
|
||||
final double devicePixelRatio = ui.window.devicePixelRatio;
|
||||
int dxInDevicePixels = (_paintOffset.dx * devicePixelRatio).round();
|
||||
int dyInDevicePixels = (_paintOffset.dy * devicePixelRatio).round();
|
||||
return new Offset(dxInDevicePixels / devicePixelRatio,
|
||||
dyInDevicePixels / devicePixelRatio);
|
||||
}
|
||||
|
||||
void applyPaintTransform(RenderBox child, Matrix4 transform) {
|
||||
final Offset effectivePaintOffset = _paintOffsetRoundedToIntegerDevicePixels;
|
||||
super.applyPaintTransform(child, transform.translate(effectivePaintOffset.dx, effectivePaintOffset.dy));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// A render object that's bigger on the inside.
|
||||
///
|
||||
/// The child of a viewport can layout to a larger size than the viewport
|
||||
/// itself. If that happens, only a portion of the child will be visible through
|
||||
/// the viewport. The portion of the child that is visible is controlled by the
|
||||
/// paint offset.
|
||||
class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<RenderBox> {
|
||||
|
||||
RenderViewport({
|
||||
RenderBox child,
|
||||
Offset paintOffset: Offset.zero,
|
||||
Axis scrollDirection: Axis.vertical,
|
||||
Painter overlayPainter
|
||||
}) : super(paintOffset, scrollDirection, overlayPainter) {
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
|
||||
BoxConstraints innerConstraints;
|
||||
switch (scrollDirection) {
|
||||
@ -135,41 +188,30 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
|
||||
}
|
||||
}
|
||||
|
||||
Offset get _scrollOffsetRoundedToIntegerDevicePixels {
|
||||
double devicePixelRatio = ui.window.devicePixelRatio;
|
||||
int dxInDevicePixels = (scrollOffset.dx * devicePixelRatio).round();
|
||||
int dyInDevicePixels = (scrollOffset.dy * devicePixelRatio).round();
|
||||
return new Offset(dxInDevicePixels / devicePixelRatio,
|
||||
dyInDevicePixels / devicePixelRatio);
|
||||
}
|
||||
|
||||
bool _wouldNeedClipAtOffset(Offset offset) {
|
||||
bool _shouldClipAtPaintOffset(Offset paintOffset) {
|
||||
assert(child != null);
|
||||
return offset < Offset.zero || !(Offset.zero & size).contains(((Offset.zero - offset) & child.size).bottomRight);
|
||||
return paintOffset < Offset.zero || !(Offset.zero & size).contains((paintOffset & child.size).bottomRight);
|
||||
}
|
||||
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (child != null) {
|
||||
Offset roundedScrollOffset = _scrollOffsetRoundedToIntegerDevicePixels;
|
||||
bool _needsClip = _wouldNeedClipAtOffset(roundedScrollOffset);
|
||||
if (_needsClip) {
|
||||
context.pushClipRect(needsCompositing, offset, Point.origin & size, (PaintingContext context, Offset offset) {
|
||||
context.paintChild(child, offset - roundedScrollOffset);
|
||||
});
|
||||
final Offset effectivePaintOffset = _paintOffsetRoundedToIntegerDevicePixels;
|
||||
|
||||
void paintContents(PaintingContext context, Offset offset) {
|
||||
context.paintChild(child, offset + effectivePaintOffset);
|
||||
_overlayPainter?.paint(context, offset);
|
||||
}
|
||||
|
||||
if (_shouldClipAtPaintOffset(effectivePaintOffset)) {
|
||||
context.pushClipRect(needsCompositing, offset, Point.origin & size, paintContents);
|
||||
} else {
|
||||
context.paintChild(child, offset - roundedScrollOffset);
|
||||
paintContents(context, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void applyPaintTransform(RenderBox child, Matrix4 transform) {
|
||||
transform.translate(-scrollOffset.dx, -scrollOffset.dy);
|
||||
super.applyPaintTransform(child, transform);
|
||||
}
|
||||
|
||||
Rect describeApproximatePaintClip(RenderObject child) {
|
||||
if (child != null &&
|
||||
_wouldNeedClipAtOffset(_scrollOffsetRoundedToIntegerDevicePixels))
|
||||
if (child != null && _shouldClipAtPaintOffset(_paintOffsetRoundedToIntegerDevicePixels))
|
||||
return Point.origin & size;
|
||||
return null;
|
||||
}
|
||||
@ -177,7 +219,7 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
|
||||
bool hitTestChildren(HitTestResult result, { Point position }) {
|
||||
if (child != null) {
|
||||
assert(child.parentData is BoxParentData);
|
||||
Point transformed = position + _scrollOffsetRoundedToIntegerDevicePixels;
|
||||
Point transformed = position + -_paintOffsetRoundedToIntegerDevicePixels;
|
||||
return child.hitTest(result, position: transformed);
|
||||
}
|
||||
return false;
|
||||
@ -185,17 +227,17 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
|
||||
}
|
||||
|
||||
abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<RenderBox>>
|
||||
extends RenderBox with ContainerRenderObjectMixin<RenderBox, T>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox, T> {
|
||||
extends RenderViewportBase with ContainerRenderObjectMixin<RenderBox, T>,
|
||||
RenderBoxContainerDefaultsMixin<RenderBox, T> {
|
||||
RenderVirtualViewport({
|
||||
int virtualChildCount,
|
||||
Offset paintOffset,
|
||||
LayoutCallback callback,
|
||||
Offset paintOffset: Offset.zero,
|
||||
Axis scrollDirection: Axis.vertical,
|
||||
Painter overlayPainter
|
||||
}) : _virtualChildCount = virtualChildCount,
|
||||
_paintOffset = paintOffset,
|
||||
_callback = callback,
|
||||
_overlayPainter = overlayPainter;
|
||||
super(paintOffset, scrollDirection, overlayPainter);
|
||||
|
||||
int get virtualChildCount => _virtualChildCount;
|
||||
int _virtualChildCount;
|
||||
@ -206,21 +248,7 @@ abstract class RenderVirtualViewport<T extends ContainerBoxParentDataMixin<Rende
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
/// The offset at which to paint the first item.
|
||||
///
|
||||
/// Note: you can modify this property from within [callback], if necessary.
|
||||
Offset get paintOffset => _paintOffset;
|
||||
Offset _paintOffset;
|
||||
void set paintOffset(Offset value) {
|
||||
assert(value != null);
|
||||
if (value == _paintOffset)
|
||||
return;
|
||||
_paintOffset = value;
|
||||
markNeedsPaint();
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
|
||||
/// Called during [layout] to determine the grid's children.
|
||||
/// Called during [layout] to determine the render object's children.
|
||||
///
|
||||
/// Typically the callback will mutate the child list appropriately, for
|
||||
/// example so the child list contains only visible children.
|
||||
@ -233,39 +261,12 @@ 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.dx, paintOffset.dy));
|
||||
}
|
||||
|
||||
bool hitTestChildren(HitTestResult result, { Point position }) {
|
||||
return defaultHitTestChildren(result, position: position + -paintOffset);
|
||||
return defaultHitTestChildren(result, position: position + -_paintOffsetRoundedToIntegerDevicePixels);
|
||||
}
|
||||
|
||||
void _paintContents(PaintingContext context, Offset offset) {
|
||||
defaultPaint(context, offset + paintOffset);
|
||||
defaultPaint(context, offset + _paintOffsetRoundedToIntegerDevicePixels);
|
||||
_overlayPainter?.paint(context, offset);
|
||||
}
|
||||
|
||||
|
||||
@ -797,11 +797,12 @@ class Viewport extends OneChildRenderObjectWidget {
|
||||
Viewport({
|
||||
Key key,
|
||||
this.scrollDirection: Axis.vertical,
|
||||
this.scrollOffset: Offset.zero,
|
||||
this.paintOffset: Offset.zero,
|
||||
this.overlayPainter,
|
||||
Widget child
|
||||
}) : super(key: key, child: child) {
|
||||
assert(scrollDirection != null);
|
||||
assert(scrollOffset != null);
|
||||
assert(paintOffset != null);
|
||||
}
|
||||
|
||||
/// The direction in which the child is permitted to be larger than the viewport
|
||||
@ -814,14 +815,25 @@ class Viewport extends OneChildRenderObjectWidget {
|
||||
/// The offset at which to paint the child.
|
||||
///
|
||||
/// The offset can be non-zero only in the [scrollDirection].
|
||||
final Offset scrollOffset;
|
||||
final Offset paintOffset;
|
||||
|
||||
RenderViewport createRenderObject() => new RenderViewport(scrollDirection: scrollDirection, scrollOffset: scrollOffset);
|
||||
/// Paints an overlay over the viewport.
|
||||
///
|
||||
/// Often used to paint scroll bars.
|
||||
final Painter overlayPainter;
|
||||
|
||||
RenderViewport createRenderObject() => new RenderViewport(
|
||||
scrollDirection: scrollDirection,
|
||||
paintOffset: paintOffset,
|
||||
overlayPainter: overlayPainter
|
||||
);
|
||||
|
||||
void updateRenderObject(RenderViewport renderObject, Viewport oldWidget) {
|
||||
// Order dependency: RenderViewport validates scrollOffset based on scrollDirection.
|
||||
renderObject.scrollDirection = scrollDirection;
|
||||
renderObject.scrollOffset = scrollOffset;
|
||||
renderObject
|
||||
..scrollDirection = scrollDirection
|
||||
..paintOffset = paintOffset
|
||||
..overlayPainter = overlayPainter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -467,15 +467,17 @@ class ScrollNotification extends Notification {
|
||||
class ScrollableViewport extends Scrollable {
|
||||
ScrollableViewport({
|
||||
Key key,
|
||||
this.child,
|
||||
double initialScrollOffset,
|
||||
Axis scrollDirection: Axis.vertical,
|
||||
ViewportAnchor scrollAnchor: ViewportAnchor.start,
|
||||
ScrollListener onScrollStart,
|
||||
ScrollListener onScroll,
|
||||
ScrollListener onScrollEnd
|
||||
ScrollListener onScrollEnd,
|
||||
this.child
|
||||
}) : super(
|
||||
key: key,
|
||||
scrollDirection: scrollDirection,
|
||||
scrollAnchor: scrollAnchor,
|
||||
initialScrollOffset: initialScrollOffset,
|
||||
onScrollStart: onScrollStart,
|
||||
onScroll: onScroll,
|
||||
@ -514,18 +516,12 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
|
||||
));
|
||||
}
|
||||
|
||||
Offset get _scrollOffsetVector {
|
||||
if (config.scrollDirection == Axis.horizontal)
|
||||
return new Offset(scrollOffset, 0.0);
|
||||
return new Offset(0.0, scrollOffset);
|
||||
}
|
||||
|
||||
Widget buildContent(BuildContext context) {
|
||||
return new SizeObserver(
|
||||
onSizeChanged: _handleViewportSizeChanged,
|
||||
child: new Viewport(
|
||||
scrollOffset: _scrollOffsetVector,
|
||||
scrollDirection: config.scrollDirection,
|
||||
paintOffset: scrollOffsetToPixelDelta(scrollOffset),
|
||||
child: new SizeObserver(
|
||||
onSizeChanged: _handleChildSizeChanged,
|
||||
child: config.child
|
||||
|
||||
@ -24,7 +24,7 @@ void main() {
|
||||
),
|
||||
child: size);
|
||||
|
||||
RenderViewport viewport = new RenderViewport(child: red, scrollOffset: new Offset(0.0, -10.0));
|
||||
RenderViewport viewport = new RenderViewport(child: red, paintOffset: new Offset(0.0, 10.0));
|
||||
layout(viewport);
|
||||
|
||||
HitTestResult result;
|
||||
|
||||
@ -298,6 +298,7 @@ class AnalyzeCommand extends FlutterCommand {
|
||||
new RegExp(r'\[lint\] Prefer using lowerCamelCase for constant names.'), // sometimes we have no choice (e.g. when matching other platforms)
|
||||
new RegExp(r'\[lint\] Avoid defining a one-member abstract class when a simple function will do.'), // too many false-positives; code review should catch real instances
|
||||
new RegExp(r'\[info\] TODO.+'),
|
||||
new RegExp('\\[warning\\] Missing concrete implementation of \'RenderObject\\.applyPaintTransform\''), // https://github.com/dart-lang/sdk/issues/25232
|
||||
new RegExp(r'[0-9]+ (error|warning|hint|lint).+found\.'),
|
||||
new RegExp(r'^$'),
|
||||
];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user