Commit 47715c50 authored by Adam Barth's avatar Adam Barth

Add padding support to LazyBlock (#3272)

Also fill in some related dartdocs.
parent d9b73a2b
......@@ -108,7 +108,7 @@ class AppBar extends StatelessWidget {
/// Defaults to [ThemeData.primaryTextTheme].
final TextTheme textTheme;
/// The amount of space to inset the contents of the app bar.
/// The amount of space by which to inset the contents of the app bar.
final EdgeInsets padding;
final double _expandedHeight;
......
......@@ -63,6 +63,9 @@ class Tooltip extends StatefulWidget {
final double height;
/// The amount of space by which to inset the child.
///
/// Defaults to 16.0 logical pixels in each direction.
final EdgeInsets padding;
final double verticalOffset;
......
......@@ -414,7 +414,7 @@ class Padding extends SingleChildRenderObjectWidget {
assert(padding != null);
}
/// The amount to pad the child in each dimension.
/// The amount of space by which to inset the child.
final EdgeInsets padding;
@override
......
......@@ -2,11 +2,14 @@
// 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 'package:flutter/rendering.dart';
import 'basic.dart';
import 'framework.dart';
import 'scrollable.dart';
import 'scrollable_list.dart';
import 'scroll_behavior.dart';
/// Provides children for [LazyBlock] or [LazyBlockViewport].
......@@ -77,6 +80,35 @@ class LazyBlockBuilder extends LazyBlockDelegate {
bool shouldRebuild(LazyBlockDelegate oldDelegate) => true;
}
/// Uses a [List<Widget>] to provide children for [LazyBlock].
///
/// See also [LazyBlockViewport].
class LazyBlockChildren extends LazyBlockDelegate {
/// Creates a LazyBlockChildren that displays the given children.
///
/// The list of children must not be modified after being passed to this
/// constructor.
LazyBlockChildren({ this.children }) {
assert(children != null);
}
/// The widgets to display.
///
/// This list must not be modified after being stored in this field.
final List<Widget> children;
@override
Widget buildItem(BuildContext context, int index) {
assert(index >= 0);
return index < children.length ? children[index] : null;
}
@override
bool shouldRebuild(LazyBlockChildren oldDelegate) {
return children != oldDelegate.children;
}
}
/// An infinite scrolling list of variable height children.
///
/// [LazyBlock] is a general-purpose scrollable list for a large (or infinite)
......@@ -105,7 +137,8 @@ class LazyBlock extends Scrollable {
Axis scrollDirection: Axis.vertical,
ScrollListener onScroll,
SnapOffsetCallback snapOffsetCallback,
this.delegate
this.delegate,
this.padding
}) : super(
key: key,
initialScrollOffset: initialScrollOffset,
......@@ -119,6 +152,9 @@ class LazyBlock extends Scrollable {
/// See [LazyBlockDelegate] for details.
final LazyBlockDelegate delegate;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding;
@override
ScrollableState<LazyBlock> createState() => new _LazyBlockState();
}
......@@ -143,12 +179,20 @@ class _LazyBlockState extends ScrollableState<LazyBlock> {
@override
Widget buildContent(BuildContext context) {
return new LazyBlockViewport(
startOffset: scrollOffset,
final bool clampOverscrolls = ClampOverscrolls.of(context);
final double startOffset = clampOverscrolls
? scrollOffset.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset)
: scrollOffset;
Widget viewport = new LazyBlockViewport(
startOffset: startOffset,
mainAxis: config.scrollDirection,
padding: config.padding,
onExtentsChanged: _handleExtentsChanged,
delegate: config.delegate
);
if (clampOverscrolls)
viewport = new ClampOverscrolls(value: false, child: viewport);
return viewport;
}
}
......@@ -181,6 +225,7 @@ class LazyBlockViewport extends RenderObjectWidget {
Key key,
this.startOffset: 0.0,
this.mainAxis: Axis.vertical,
this.padding,
this.onExtentsChanged,
this.delegate
}) : super(key: key) {
......@@ -203,6 +248,9 @@ class LazyBlockViewport extends RenderObjectWidget {
/// axis is vertical).
final Axis mainAxis;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding;
/// Called when the interior or exterior dimensions of the viewport change.
final LazyBlockExtentsChangedCallback onExtentsChanged;
......@@ -211,6 +259,17 @@ class LazyBlockViewport extends RenderObjectWidget {
/// See [LazyBlockDelegate] for details.
final LazyBlockDelegate delegate;
double get _mainAxisPadding {
if (padding == null)
return 0.0;
switch (mainAxis) {
case Axis.horizontal:
return padding.horizontal;
case Axis.vertical:
return padding.vertical;
}
}
@override
_LazyBlockElement createElement() => new _LazyBlockElement(this);
......@@ -383,11 +442,15 @@ class _LazyBlockElement extends RenderObjectElement {
// currently represented in _children, we just need to update the paint
// offset. Otherwise, we need to trigger a layout in order to change the
// set of explicitly represented children.
if (widget.startOffset >= _startOffsetLowerLimit && widget.startOffset < _startOffsetUpperLimit)
double startOffset = widget.startOffset;
if (startOffset >= _startOffsetLowerLimit &&
startOffset < _startOffsetUpperLimit &&
newWidget.padding == oldWidget.padding) {
_updatePaintOffset();
else
} else {
renderObject.markNeedsLayout();
}
}
@override
void unmount() {
......@@ -536,7 +599,7 @@ class _LazyBlockElement extends RenderObjectElement {
if (currentLogicalOffset < endLogicalOffset) {
// The last element is visible. We need to update our reckoning of where
// the max scroll offset is.
_maxScrollOffset = currentLogicalOffset - blockExtent;
_maxScrollOffset = currentLogicalOffset + widget._mainAxisPadding - blockExtent;
_startOffsetUpperLimit = double.INFINITY;
} else {
// The last element is not visible. Ensure that we have one blockExtent
......@@ -561,7 +624,7 @@ class _LazyBlockElement extends RenderObjectElement {
// position the first physical child at Offset.zero and use the paintOffset
// on the render object to adjust the final paint location of the children.
Offset currentChildOffset = Offset.zero;
Offset currentChildOffset = _initialChildOffset;
child = block.firstChild;
while (child != null) {
final _LazyBlockParentData childParentData = child.parentData;
......@@ -590,10 +653,20 @@ class _LazyBlockElement extends RenderObjectElement {
BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
switch (widget.mainAxis) {
case Axis.horizontal:
return new BoxConstraints.tightFor(height: constraints.maxHeight);
double padding = widget.padding?.vertical ?? 0.0;
double height = math.max(0.0, constraints.maxHeight - padding);
return new BoxConstraints.tightFor(height: height);
case Axis.vertical:
return new BoxConstraints.tightFor(width: constraints.maxWidth);
double padding = widget.padding?.horizontal ?? 0.0;
double width = math.max(0.0, constraints.maxWidth - padding);
return new BoxConstraints.tightFor(width: width);
}
}
Offset get _initialChildOffset {
if (widget.padding == null)
return Offset.zero;
return new Offset(widget.padding.left, widget.padding.top);
}
double _getMainAxisExtent(Size size) {
......
......@@ -741,7 +741,10 @@ class Block extends StatelessWidget {
}
final List<Widget> children;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding;
final double initialScrollOffset;
final Axis scrollDirection;
final ViewportAnchor scrollAnchor;
......
......@@ -70,7 +70,10 @@ class ScrollableList extends Scrollable {
final double itemExtent;
final bool itemsWrap;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding;
final Iterable<Widget> children;
@override
......@@ -339,6 +342,8 @@ class ScrollableLazyList extends Scrollable {
final double itemExtent;
final int itemCount;
final ItemListBuilder itemBuilder;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding;
@override
......
......@@ -4,6 +4,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:test/test.dart';
import 'test_widgets.dart';
......@@ -251,4 +252,33 @@ void main() {
expect(decoraton.backgroundColor, equals(Colors.green[500]));
});
});
test('LazyBlockViewport padding', () {
testWidgets((WidgetTester tester) {
IndexedBuilder itemBuilder = (BuildContext context, int i) {
return new Container(
key: new ValueKey<int>(i),
width: 500.0, // this should be ignored
height: 220.0,
decoration: new BoxDecoration(
backgroundColor: Colors.green[500]
),
child: new Text("$i")
);
};
tester.pumpWidget(
new LazyBlockViewport(
padding: new EdgeInsets.fromLTRB(7.0, 3.0, 5.0, 11.0),
delegate: new LazyBlockBuilder(builder: itemBuilder)
)
);
RenderBox firstBox = tester.findText('0').findRenderObject();
Point upperLeft = firstBox.localToGlobal(Point.origin);
expect(upperLeft, equals(new Point(7.0, 3.0)));
expect(firstBox.size.width, equals(800.0 - 12.0));
});
});
}
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