Add padding support to LazyBlock (#3272)
Also fill in some related dartdocs.
This commit is contained in:
parent
d9b73a2b7a
commit
47715c5076
@ -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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 @@
|
|||||||
// 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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
/// 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> {
|
|||||||
|
|
||||||
@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 {
|
|||||||
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 {
|
|||||||
/// 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 {
|
|||||||
/// 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,10 +442,14 @@ 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
|
||||||
@ -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 {
|
|||||||
// 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,12 +653,22 @@ 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) {
|
||||||
switch (widget.mainAxis) {
|
switch (widget.mainAxis) {
|
||||||
case Axis.horizontal:
|
case Axis.horizontal:
|
||||||
|
@ -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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 @@
|
|||||||
|
|
||||||
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() {
|
|||||||
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));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user