Commit 41f1f8a4 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add SliverFill (#7776)

SliverFill fills the remaining space in the viewport with each box
child. We'll use this sliver as a building block for pageable lists.
parent 69530202
......@@ -10,30 +10,21 @@ import 'box.dart';
import 'sliver.dart';
import 'sliver_multi_box_adaptor.dart';
class RenderSliverList extends RenderSliverMultiBoxAdaptor {
RenderSliverList({
abstract class RenderSliverFixedExtentBoxAdaptor extends RenderSliverMultiBoxAdaptor {
RenderSliverFixedExtentBoxAdaptor({
@required RenderSliverBoxChildManager childManager,
double itemExtent,
}) : _itemExtent = itemExtent, super(childManager: childManager) {
assert(itemExtent != null);
}
/// The main-axis extent of each item in the list.
double get itemExtent => _itemExtent;
double _itemExtent;
set itemExtent (double newValue) {
assert(newValue != null);
if (_itemExtent == newValue)
return;
_itemExtent = newValue;
markNeedsLayout();
}
}) : super(childManager: childManager);
double _indexToScrollOffset(int index) => _itemExtent * index;
/// The main-axis extent of each item.
double get itemExtent;
@override
void performLayout() {
assert(childManager.debugAssertChildListLocked());
final double itemExtent = this.itemExtent;
double indexToScrollOffset(int index) => itemExtent * index;
final double scrollOffset = constraints.scrollOffset;
assert(scrollOffset >= 0.0);
final double remainingPaintExtent = constraints.remainingPaintExtent;
......@@ -45,8 +36,8 @@ class RenderSliverList extends RenderSliverMultiBoxAdaptor {
maxExtent: itemExtent,
);
final int firstIndex = math.max(0, scrollOffset ~/ _itemExtent);
final int targetLastIndex = math.max(0, (targetEndScrollOffset / itemExtent).ceil());
final int firstIndex = math.max(0, scrollOffset ~/ itemExtent);
final int targetLastIndex = math.max(0, (targetEndScrollOffset / itemExtent).ceil() - 1);
if (firstChild != null) {
final int oldFirstIndex = indexOf(firstChild);
......@@ -58,7 +49,7 @@ class RenderSliverList extends RenderSliverMultiBoxAdaptor {
}
if (firstChild == null) {
if (!addInitialChild(index: firstIndex, scrollOffset: _indexToScrollOffset(firstIndex))) {
if (!addInitialChild(index: firstIndex, scrollOffset: indexToScrollOffset(firstIndex))) {
// There are no children.
geometry = SliverGeometry.zero;
return;
......@@ -70,7 +61,7 @@ class RenderSliverList extends RenderSliverMultiBoxAdaptor {
for (int index = indexOf(firstChild) - 1; index >= firstIndex; --index) {
final RenderBox child = insertAndLayoutLeadingChild(childConstraints);
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
childParentData.scrollOffset = _indexToScrollOffset(index);
childParentData.scrollOffset = indexToScrollOffset(index);
assert(childParentData.index == index);
trailingChildWithLayout ??= child;
}
......@@ -96,12 +87,12 @@ class RenderSliverList extends RenderSliverMultiBoxAdaptor {
trailingChildWithLayout = child;
assert(child != null);
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
childParentData.scrollOffset = _indexToScrollOffset(childParentData.index);
childParentData.scrollOffset = indexToScrollOffset(childParentData.index);
}
final int lastIndex = indexOf(lastChild);
final double leadingScrollOffset = _indexToScrollOffset(firstIndex);
final double trailingScrollOffset = _indexToScrollOffset(lastIndex + 1);
final double leadingScrollOffset = indexToScrollOffset(firstIndex);
final double trailingScrollOffset = indexToScrollOffset(lastIndex + 1);
assert(debugAssertChildListIsNonEmptyAndContiguous());
assert(indexOf(firstChild) == firstIndex);
......@@ -132,3 +123,30 @@ class RenderSliverList extends RenderSliverMultiBoxAdaptor {
assert(childManager.debugAssertChildListLocked());
}
}
class RenderSliverList extends RenderSliverFixedExtentBoxAdaptor {
RenderSliverList({
@required RenderSliverBoxChildManager childManager,
double itemExtent,
}) : _itemExtent = itemExtent, super(childManager: childManager);
@override
double get itemExtent => _itemExtent;
double _itemExtent;
set itemExtent (double newValue) {
assert(newValue != null);
if (_itemExtent == newValue)
return;
_itemExtent = newValue;
markNeedsLayout();
}
}
class RenderSliverFill extends RenderSliverFixedExtentBoxAdaptor {
RenderSliverFill({
@required RenderSliverBoxChildManager childManager,
}) : super(childManager: childManager);
@override
double get itemExtent => constraints.remainingPaintExtent;
}
......@@ -188,6 +188,19 @@ class SliverGrid extends SliverMultiBoxAdaptorWidget {
}
}
class SliverFill extends SliverMultiBoxAdaptorWidget {
SliverFill({
Key key,
@required SliverChildDelegate delegate,
}) : super(key: key, delegate: delegate);
@override
RenderSliverFill createRenderObject(BuildContext context) {
final SliverMultiBoxAdaptorElement element = context;
return new RenderSliverFill(childManager: element);
}
}
class SliverMultiBoxAdaptorElement extends RenderObjectElement implements RenderSliverBoxChildManager {
SliverMultiBoxAdaptorElement(SliverMultiBoxAdaptorWidget widget) : super(widget);
......
......@@ -24,7 +24,7 @@ void main() {
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
await tester.scroll(find.byType(ScrollView), const Offset(0.0, -250.0));
......@@ -35,7 +35,7 @@ void main() {
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('5'), findsOneWidget);
expect(find.text('5'), findsNothing);
expect(find.text('6'), findsNothing);
await tester.scroll(find.byType(ScrollView), const Offset(0.0, 200.0));
......@@ -45,7 +45,7 @@ void main() {
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('4'), findsOneWidget);
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
});
......@@ -68,7 +68,7 @@ void main() {
),
);
expect(log, equals(<int>[0, 1, 2, 3]));
expect(log, equals(<int>[0, 1, 2]));
log.clear();
Scrollable2State state = tester.state(find.byType(Scrollable2));
......@@ -78,7 +78,7 @@ void main() {
expect(log, isEmpty);
await tester.pump();
expect(log, equals(<int>[10, 11, 12, 13, 14]));
expect(log, equals(<int>[10, 11, 12, 13]));
log.clear();
position.jumpTo(975.0);
......@@ -86,7 +86,7 @@ void main() {
expect(log, isEmpty);
await tester.pump();
expect(log, equals(<int>[4, 5, 6, 7, 8]));
expect(log, equals(<int>[4, 5, 6, 7]));
log.clear();
});
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
void main() {
testWidgets('SliverFillRemaining control test', (WidgetTester tester) async {
List<Widget> children = new List<Widget>.generate(20, (int i) {
return new Container(child: new Text('$i'));
});
await tester.pumpWidget(
new ScrollableViewport2(
slivers: <Widget>[
new SliverFill(
delegate: new SliverChildListDelegate(children),
),
],
),
);
RenderBox box = tester.renderObject<RenderBox>(find.byType(Container).first);
expect(box.size.height, equals(600.0));
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
await tester.scroll(find.byType(ScrollableViewport2), const Offset(0.0, -700.0));
await tester.pump();
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsOneWidget);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
await tester.scroll(find.byType(ScrollableViewport2), const Offset(0.0, 200.0));
await tester.pump();
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsNothing);
expect(find.text('3'), findsNothing);
});
}
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