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 { ...@@ -108,7 +108,7 @@ class AppBar extends StatelessWidget {
/// Defaults to [ThemeData.primaryTextTheme]. /// Defaults to [ThemeData.primaryTextTheme].
final TextTheme textTheme; 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 EdgeInsets padding;
final double _expandedHeight; final double _expandedHeight;
......
...@@ -63,6 +63,9 @@ class Tooltip extends StatefulWidget { ...@@ -63,6 +63,9 @@ class Tooltip extends StatefulWidget {
final double height; 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 EdgeInsets padding;
final double verticalOffset; final double verticalOffset;
......
...@@ -414,7 +414,7 @@ class Padding extends SingleChildRenderObjectWidget { ...@@ -414,7 +414,7 @@ class Padding extends SingleChildRenderObjectWidget {
assert(padding != null); 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; final EdgeInsets padding;
@override @override
......
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'basic.dart'; import 'basic.dart';
import 'framework.dart'; import 'framework.dart';
import 'scrollable.dart'; import 'scrollable.dart';
import 'scrollable_list.dart';
import 'scroll_behavior.dart'; import 'scroll_behavior.dart';
/// Provides children for [LazyBlock] or [LazyBlockViewport]. /// Provides children for [LazyBlock] or [LazyBlockViewport].
...@@ -77,6 +80,35 @@ class LazyBlockBuilder extends LazyBlockDelegate { ...@@ -77,6 +80,35 @@ class LazyBlockBuilder extends LazyBlockDelegate {
bool shouldRebuild(LazyBlockDelegate oldDelegate) => true; 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. /// An infinite scrolling list of variable height children.
/// ///
/// [LazyBlock] is a general-purpose scrollable list for a large (or infinite) /// [LazyBlock] is a general-purpose scrollable list for a large (or infinite)
...@@ -105,7 +137,8 @@ class LazyBlock extends Scrollable { ...@@ -105,7 +137,8 @@ class LazyBlock extends Scrollable {
Axis scrollDirection: Axis.vertical, Axis scrollDirection: Axis.vertical,
ScrollListener onScroll, ScrollListener onScroll,
SnapOffsetCallback snapOffsetCallback, SnapOffsetCallback snapOffsetCallback,
this.delegate this.delegate,
this.padding
}) : super( }) : super(
key: key, key: key,
initialScrollOffset: initialScrollOffset, initialScrollOffset: initialScrollOffset,
...@@ -119,6 +152,9 @@ class LazyBlock extends Scrollable { ...@@ -119,6 +152,9 @@ class LazyBlock extends Scrollable {
/// See [LazyBlockDelegate] for details. /// See [LazyBlockDelegate] for details.
final LazyBlockDelegate delegate; final LazyBlockDelegate delegate;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding;
@override @override
ScrollableState<LazyBlock> createState() => new _LazyBlockState(); ScrollableState<LazyBlock> createState() => new _LazyBlockState();
} }
...@@ -143,12 +179,20 @@ class _LazyBlockState extends ScrollableState<LazyBlock> { ...@@ -143,12 +179,20 @@ class _LazyBlockState extends ScrollableState<LazyBlock> {
@override @override
Widget buildContent(BuildContext context) { Widget buildContent(BuildContext context) {
return new LazyBlockViewport( final bool clampOverscrolls = ClampOverscrolls.of(context);
startOffset: scrollOffset, final double startOffset = clampOverscrolls
? scrollOffset.clamp(scrollBehavior.minScrollOffset, scrollBehavior.maxScrollOffset)
: scrollOffset;
Widget viewport = new LazyBlockViewport(
startOffset: startOffset,
mainAxis: config.scrollDirection, mainAxis: config.scrollDirection,
padding: config.padding,
onExtentsChanged: _handleExtentsChanged, onExtentsChanged: _handleExtentsChanged,
delegate: config.delegate delegate: config.delegate
); );
if (clampOverscrolls)
viewport = new ClampOverscrolls(value: false, child: viewport);
return viewport;
} }
} }
...@@ -181,6 +225,7 @@ class LazyBlockViewport extends RenderObjectWidget { ...@@ -181,6 +225,7 @@ class LazyBlockViewport extends RenderObjectWidget {
Key key, Key key,
this.startOffset: 0.0, this.startOffset: 0.0,
this.mainAxis: Axis.vertical, this.mainAxis: Axis.vertical,
this.padding,
this.onExtentsChanged, this.onExtentsChanged,
this.delegate this.delegate
}) : super(key: key) { }) : super(key: key) {
...@@ -203,6 +248,9 @@ class LazyBlockViewport extends RenderObjectWidget { ...@@ -203,6 +248,9 @@ class LazyBlockViewport extends RenderObjectWidget {
/// axis is vertical). /// axis is vertical).
final Axis mainAxis; 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. /// Called when the interior or exterior dimensions of the viewport change.
final LazyBlockExtentsChangedCallback onExtentsChanged; final LazyBlockExtentsChangedCallback onExtentsChanged;
...@@ -211,6 +259,17 @@ class LazyBlockViewport extends RenderObjectWidget { ...@@ -211,6 +259,17 @@ class LazyBlockViewport extends RenderObjectWidget {
/// See [LazyBlockDelegate] for details. /// See [LazyBlockDelegate] for details.
final LazyBlockDelegate delegate; 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 @override
_LazyBlockElement createElement() => new _LazyBlockElement(this); _LazyBlockElement createElement() => new _LazyBlockElement(this);
...@@ -383,11 +442,15 @@ class _LazyBlockElement extends RenderObjectElement { ...@@ -383,11 +442,15 @@ class _LazyBlockElement extends RenderObjectElement {
// currently represented in _children, we just need to update the paint // currently represented in _children, we just need to update the paint
// offset. Otherwise, we need to trigger a layout in order to change the // offset. Otherwise, we need to trigger a layout in order to change the
// set of explicitly represented children. // 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(); _updatePaintOffset();
else } else {
renderObject.markNeedsLayout(); renderObject.markNeedsLayout();
} }
}
@override @override
void unmount() { void unmount() {
...@@ -536,7 +599,7 @@ class _LazyBlockElement extends RenderObjectElement { ...@@ -536,7 +599,7 @@ class _LazyBlockElement extends RenderObjectElement {
if (currentLogicalOffset < endLogicalOffset) { if (currentLogicalOffset < endLogicalOffset) {
// The last element is visible. We need to update our reckoning of where // The last element is visible. We need to update our reckoning of where
// the max scroll offset is. // the max scroll offset is.
_maxScrollOffset = currentLogicalOffset - blockExtent; _maxScrollOffset = currentLogicalOffset + widget._mainAxisPadding - blockExtent;
_startOffsetUpperLimit = double.INFINITY; _startOffsetUpperLimit = double.INFINITY;
} else { } else {
// The last element is not visible. Ensure that we have one blockExtent // The last element is not visible. Ensure that we have one blockExtent
...@@ -561,7 +624,7 @@ class _LazyBlockElement extends RenderObjectElement { ...@@ -561,7 +624,7 @@ class _LazyBlockElement extends RenderObjectElement {
// position the first physical child at Offset.zero and use the paintOffset // 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. // on the render object to adjust the final paint location of the children.
Offset currentChildOffset = Offset.zero; Offset currentChildOffset = _initialChildOffset;
child = block.firstChild; child = block.firstChild;
while (child != null) { while (child != null) {
final _LazyBlockParentData childParentData = child.parentData; final _LazyBlockParentData childParentData = child.parentData;
...@@ -590,10 +653,20 @@ class _LazyBlockElement extends RenderObjectElement { ...@@ -590,10 +653,20 @@ class _LazyBlockElement extends RenderObjectElement {
BoxConstraints _getInnerConstraints(BoxConstraints constraints) { BoxConstraints _getInnerConstraints(BoxConstraints constraints) {
switch (widget.mainAxis) { switch (widget.mainAxis) {
case Axis.horizontal: 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: 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) { double _getMainAxisExtent(Size size) {
......
...@@ -741,7 +741,10 @@ class Block extends StatelessWidget { ...@@ -741,7 +741,10 @@ class Block extends StatelessWidget {
} }
final List<Widget> children; final List<Widget> children;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding; final EdgeInsets padding;
final double initialScrollOffset; final double initialScrollOffset;
final Axis scrollDirection; final Axis scrollDirection;
final ViewportAnchor scrollAnchor; final ViewportAnchor scrollAnchor;
......
...@@ -70,7 +70,10 @@ class ScrollableList extends Scrollable { ...@@ -70,7 +70,10 @@ class ScrollableList extends Scrollable {
final double itemExtent; final double itemExtent;
final bool itemsWrap; final bool itemsWrap;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding; final EdgeInsets padding;
final Iterable<Widget> children; final Iterable<Widget> children;
@override @override
...@@ -339,6 +342,8 @@ class ScrollableLazyList extends Scrollable { ...@@ -339,6 +342,8 @@ class ScrollableLazyList extends Scrollable {
final double itemExtent; final double itemExtent;
final int itemCount; final int itemCount;
final ItemListBuilder itemBuilder; final ItemListBuilder itemBuilder;
/// The amount of space by which to inset the children inside the viewport.
final EdgeInsets padding; final EdgeInsets padding;
@override @override
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'test_widgets.dart'; import 'test_widgets.dart';
...@@ -251,4 +252,33 @@ void main() { ...@@ -251,4 +252,33 @@ void main() {
expect(decoraton.backgroundColor, equals(Colors.green[500])); 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