Commit f683abd7 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Sliver Block (#7618)

parent 2380d854
......@@ -45,6 +45,7 @@ export 'src/rendering/rotated_box.dart';
export 'src/rendering/semantics.dart';
export 'src/rendering/shifted_box.dart';
export 'src/rendering/sliver.dart';
export 'src/rendering/sliver_block.dart';
export 'src/rendering/stack.dart';
export 'src/rendering/table.dart';
export 'src/rendering/tweens.dart';
......
......@@ -1145,22 +1145,26 @@ class RenderViewport2 extends RenderBox with ContainerRenderObjectMixin<RenderSl
///
/// If the [center] is not specified, then the first child in the `children`
/// list, if any, is used.
///
/// The [offset] must be specified. For testing purposes, consider passing a
/// [new ViewportOffset.zero] or [new ViewportOffset.fixed].
RenderViewport2({
AxisDirection axisDirection: AxisDirection.down,
double anchor: 0.0,
ViewportOffset offset,
@required ViewportOffset offset,
List<RenderSliver> children,
RenderSliver center,
}) : _axisDirection = axisDirection,
_anchor = anchor,
_offset = offset ?? new ViewportOffset.zero(),
_offset = offset,
_center = center {
addAll(children);
if (center == null && firstChild != null)
_center = firstChild;
assert(offset != null);
assert(axisDirection != null);
assert(anchor != null);
assert(anchor >= 0.0 && anchor <= 1.0);
addAll(children);
if (center == null && firstChild != null)
_center = firstChild;
}
AxisDirection get axisDirection => _axisDirection;
......
This diff is collapsed.
// Copyright 2016 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 'dart:collection' show SplayTreeMap, HashMap;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'framework.dart';
import 'basic.dart';
abstract class SliverBlockDelegate {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const SliverBlockDelegate();
Widget build(BuildContext context, int index);
bool shouldRebuild(@checked SliverBlockDelegate oldDelegate);
int get childCount;
double estimateScrollOffsetExtent(
int firstIndex,
int lastIndex,
double leadingScrollOffset,
double trailingScrollOffset,
) {
return childCount * (trailingScrollOffset - leadingScrollOffset) / (lastIndex - firstIndex + 1);
}
}
// ///
// /// In general building all the widgets in advance is not efficient. It is
// /// better to create a delegate that builds them on demand by subclassing
// /// [SliverBlockDelegate] directly.
// ///
// /// This class is provided for the cases where either the list of children is
// /// known well in advance (ideally the children are themselves compile-time
// /// constants, for example), and therefore will not be built each time the
// /// delegate itself is created, or the list is small, such that it's likely
// /// always visible (and thus there is nothing to be gained by building it on
// /// demand). For example, the body of a dialog box might fit both of these
// /// conditions.
class SliverBlockChildListDelegate extends SliverBlockDelegate {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const SliverBlockChildListDelegate(this.children);
final List<Widget> children;
@override
Widget build(BuildContext context, int index) {
assert(children != null);
if (index < 0 || index >= children.length)
return null;
return children[index];
}
@override
bool shouldRebuild(@checked SliverBlockChildListDelegate oldDelegate) {
return children != oldDelegate.children;
}
@override
int get childCount => children.length;
}
class SliverBlock extends RenderObjectWidget {
SliverBlock({
Key key,
@required this.delegate,
}) : super(key: key) {
assert(delegate != null);
}
final SliverBlockDelegate delegate;
@override
_SliverBlockElement createElement() => new _SliverBlockElement(this);
@override
_RenderSliverBlockForWidgets createRenderObject(BuildContext context) => new _RenderSliverBlockForWidgets();
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('delegate: $delegate');
}
}
class _SliverBlockElement extends RenderObjectElement {
_SliverBlockElement(SliverBlock widget) : super(widget);
@override
SliverBlock get widget => super.widget;
@override
_RenderSliverBlockForWidgets get renderObject => super.renderObject;
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
renderObject._element = this;
}
@override
void unmount() {
super.unmount();
renderObject._element = null;
}
@override
void update(SliverBlock newWidget) {
final SliverBlock oldWidget = widget;
super.update(newWidget);
final SliverBlockDelegate newDelegate = newWidget.delegate;
final SliverBlockDelegate oldDelegate = oldWidget.delegate;
if (newDelegate != oldDelegate &&
(newDelegate.runtimeType != oldDelegate.runtimeType || newDelegate.shouldRebuild(oldDelegate)))
performRebuild();
}
Map<int, Element> _childElements = new SplayTreeMap<int, Element>();
Map<int, Widget> _childWidgets = new HashMap<int, Widget>();
RenderBox _currentBeforeChild;
bool _debugOpenToChanges = false;
@override
void performRebuild() {
_childWidgets.clear();
super.performRebuild();
_currentBeforeChild = null;
assert(!_debugOpenToChanges);
assert(() { _debugOpenToChanges = true; return true; });
try {
// The "toList()" below is to get a copy of the array so that we can
// mutate _childElements within the loop. Basically we just update all the
// same indexes as we had before. If any of them mutate the tree, then
// this will also trigger a layout and so forth. (We won't call the
// delegate's build function multiple times, though, because we cache the
// delegate's results until the next time we need to rebuild the whole
// block widget.)
for (int index in _childElements.keys.toList()) {
Element newChild;
renderObject._rebuild(index, () {
newChild = updateChild(_childElements[index], _build(index), index);
});
if (newChild != null) {
_childElements[index] = newChild;
_currentBeforeChild = newChild.renderObject;
} else {
_childElements.remove(index);
}
}
} finally {
assert(() { _debugOpenToChanges = false; return true; });
}
}
Widget _build(int index) {
return _childWidgets.putIfAbsent(index, () {
Widget child = widget.delegate.build(this, index);
if (child == null)
return null;
return new RepaintBoundary.wrap(child, index);
});
}
void _createChild(int index, bool insertFirst) {
assert(!_debugOpenToChanges);
owner.buildScope(this, () {
assert(insertFirst || _childElements[index-1] != null);
assert(() { _debugOpenToChanges = true; return true; });
_currentBeforeChild = insertFirst ? null : _childElements[index-1].renderObject;
Element newChild;
try {
newChild = updateChild(_childElements[index], _build(index), index);
} finally {
assert(() { _debugOpenToChanges = false; return true; });
}
if (newChild != null) {
_childElements[index] = newChild;
} else {
_childElements.remove(index);
}
});
}
@override
void forgetChild(Element child) {
assert(child != null);
assert(child.slot != null);
assert(_childElements.containsKey(child.slot));
_childElements.remove(child.slot);
}
void _removeChild(int index) {
assert(!_debugOpenToChanges);
assert(index >= 0);
owner.buildScope(this, () {
assert(_childElements.containsKey(index));
assert(() { _debugOpenToChanges = true; return true; });
try {
Element result = updateChild(_childElements[index], null, index);
assert(result == null);
} finally {
assert(() { _debugOpenToChanges = false; return true; });
}
_childElements.remove(index);
assert(!_childElements.containsKey(index));
});
}
@override
void insertChildRenderObject(@checked RenderObject child, int slot) {
assert(_debugOpenToChanges);
renderObject.insert(child, after: _currentBeforeChild);
assert(() {
SliverBlockParentData childParentData = child.parentData;
assert(slot == childParentData.index);
return true;
});
}
@override
void moveChildRenderObject(@checked RenderObject child, int slot) {
// TODO(ianh): At some point we should be better about noticing when a
// particular LocalKey changes slot, and handle moving the nodes around.
assert(false);
}
@override
void removeChildRenderObject(@checked RenderObject child) {
assert(_debugOpenToChanges);
renderObject.remove(child);
}
@override
void visitChildren(ElementVisitor visitor) {
// The toList() is to make a copy so that the underlying list can be modified by
// the visitor:
assert(!_childElements.values.any((Element child) => child == null));
_childElements.values.toList().forEach(visitor);
}
}
class _RenderSliverBlockForWidgets extends RenderSliverBlock {
_SliverBlockElement _element;
@override
void createChild(int index, { @required RenderBox after }) {
assert(_element != null);
_element._createChild(index, after == null);
}
@override
void removeChild(RenderBox child) {
assert(_element != null);
_element._removeChild(indexOf(child));
}
void _rebuild(int index, VoidCallback callback) {
allowAdditionsFor(index, callback);
}
@override
double estimateScrollOffsetExtent({
int firstIndex,
int lastIndex,
double leadingScrollOffset,
double trailingScrollOffset,
}) {
assert(lastIndex >= firstIndex);
assert(_element != null);
return _element.widget.delegate.estimateScrollOffsetExtent(firstIndex, lastIndex, leadingScrollOffset, trailingScrollOffset);
}
}
......@@ -16,10 +16,11 @@ class Viewport2 extends MultiChildRenderObjectWidget {
Key key,
this.axisDirection: AxisDirection.down,
this.anchor: 0.0,
this.offset,
@required this.offset,
this.center,
List<Widget> children: const <Widget>[],
}) : super(key: key, children: children) {
assert(offset != null);
assert(center == null || children.where((Widget child) => child.key == center).length == 1);
}
......
......@@ -13,6 +13,7 @@ export 'src/widgets/app.dart';
export 'src/widgets/banner.dart';
export 'src/widgets/basic.dart';
export 'src/widgets/binding.dart';
export 'src/widgets/block.dart';
export 'src/widgets/clamp_overscrolls.dart';
export 'src/widgets/container.dart';
export 'src/widgets/debug.dart';
......
// Copyright 2015 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/rendering.dart';
import 'package:meta/meta.dart';
import 'package:test/test.dart';
import 'rendering_tester.dart';
class RenderSliverBlockTest extends RenderSliverBlock {
RenderSliverBlockTest({
this.children,
});
List<RenderBox> children;
@override
void createChild(int index, { @required RenderBox after }) {
assert(index >= 0);
if (index < 0 || index >= children.length)
return null;
insert(children[index], after: after);
}
@override
double estimateScrollOffsetExtent({
int firstIndex,
int lastIndex,
double leadingScrollOffset,
double trailingScrollOffset,
}) {
assert(lastIndex >= firstIndex);
return children.length * (trailingScrollOffset - leadingScrollOffset) / (lastIndex - firstIndex + 1);
}
}
void main() {
test('RenderSliverBlock basic test - down', () {
RenderObject inner;
RenderBox a, b, c, d, e;
RenderViewport2 root = new RenderViewport2(
axisDirection: AxisDirection.down,
offset: new ViewportOffset.zero(),
children: <RenderSliver>[
inner = new RenderSliverBlockTest(
children: <RenderBox>[
a = new RenderSizedBox(const Size(100.0, 400.0)),
b = new RenderSizedBox(const Size(100.0, 400.0)),
c = new RenderSizedBox(const Size(100.0, 400.0)),
d = new RenderSizedBox(const Size(100.0, 400.0)),
e = new RenderSizedBox(const Size(100.0, 400.0)),
],
),
],
);
layout(root);
expect(root.size.width, equals(800.0));
expect(root.size.height, equals(600.0));
expect(a.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 0.0));
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 400.0));
expect(c.attached, false);
expect(d.attached, false);
expect(e.attached, false);
// make sure that layout is stable by laying out again
inner.markNeedsLayout();
pumpFrame();
expect(a.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 0.0));
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 400.0));
expect(c.attached, false);
expect(d.attached, false);
expect(e.attached, false);
// now try various scroll offsets
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, -200.0));
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 200.0));
expect(c.attached, false);
expect(d.attached, false);
expect(e.attached, false);
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
expect(a.attached, false);
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, -200.0));
expect(c.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 200.0));
expect(d.attached, false);
expect(e.attached, false);
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
expect(a.attached, false);
expect(b.attached, false);
expect(c.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, -100.0));
expect(d.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 300.0));
expect(e.attached, false);
// try going back up
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, -200.0));
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 200.0));
expect(c.attached, false);
expect(d.attached, false);
expect(e.attached, false);
});
test('RenderSliverBlock basic test - up', () {
RenderObject inner;
RenderBox a, b, c, d, e;
RenderViewport2 root = new RenderViewport2(
axisDirection: AxisDirection.up,
offset: new ViewportOffset.zero(),
children: <RenderSliver>[
inner = new RenderSliverBlockTest(
children: <RenderBox>[
a = new RenderSizedBox(const Size(100.0, 400.0)),
b = new RenderSizedBox(const Size(100.0, 400.0)),
c = new RenderSizedBox(const Size(100.0, 400.0)),
d = new RenderSizedBox(const Size(100.0, 400.0)),
e = new RenderSizedBox(const Size(100.0, 400.0)),
],
),
],
);
layout(root);
expect(root.size.width, equals(800.0));
expect(root.size.height, equals(600.0));
expect(a.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 200.0));
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, -200.0));
expect(c.attached, false);
expect(d.attached, false);
expect(e.attached, false);
// make sure that layout is stable by laying out again
inner.markNeedsLayout();
pumpFrame();
expect(a.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 200.0));
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, -200.0));
expect(c.attached, false);
expect(d.attached, false);
expect(e.attached, false);
// now try various scroll offsets
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 400.0));
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 0.0));
expect(c.attached, false);
expect(d.attached, false);
expect(e.attached, false);
root.offset = new ViewportOffset.fixed(600.0);
pumpFrame();
expect(a.attached, false);
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 400.0));
expect(c.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 0.0));
expect(d.attached, false);
expect(e.attached, false);
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
expect(a.attached, false);
expect(b.attached, false);
expect(c.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 300.0));
expect(d.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, -100.0));
expect(e.attached, false);
// try going back up
root.offset = new ViewportOffset.fixed(200.0);
pumpFrame();
expect(a.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 400.0));
expect(b.localToGlobal(const Point(0.0, 0.0)), const Point(0.0, 0.0));
expect(c.attached, false);
expect(d.attached, false);
expect(e.attached, false);
});
}
......@@ -25,6 +25,7 @@ void main() {
RenderBox box;
RenderObject root = new RenderLayoutWatcher(
viewport = new RenderViewport2(
offset: new ViewportOffset.zero(),
children: <RenderSliver>[
sliver = new RenderSliverToBoxAdapter(child: box = new RenderSizedBox(const Size(100.0, 400.0))),
],
......
......@@ -9,7 +9,9 @@ import 'rendering_tester.dart';
void main() {
test('RenderViewport2 basic test - no children', () {
RenderViewport2 root = new RenderViewport2();
RenderViewport2 root = new RenderViewport2(
offset: new ViewportOffset.zero(),
);
layout(root);
root.offset = new ViewportOffset.fixed(900.0);
pumpFrame();
......@@ -18,6 +20,7 @@ void main() {
test('RenderViewport2 basic test - down', () {
RenderBox a, b, c, d, e;
RenderViewport2 root = new RenderViewport2(
offset: new ViewportOffset.zero(),
children: <RenderSliver>[
new RenderSliverToBoxAdapter(child: a = new RenderSizedBox(const Size(100.0, 400.0))),
new RenderSliverToBoxAdapter(child: b = new RenderSizedBox(const Size(100.0, 400.0))),
......@@ -66,6 +69,7 @@ void main() {
RenderBox a, b, c, d, e;
RenderViewport2 root = new RenderViewport2(
axisDirection: AxisDirection.up,
offset: new ViewportOffset.zero(),
children: <RenderSliver>[
new RenderSliverToBoxAdapter(child: a = new RenderSizedBox(const Size(100.0, 400.0))),
new RenderSliverToBoxAdapter(child: b = new RenderSizedBox(const Size(100.0, 400.0))),
......@@ -114,6 +118,7 @@ void main() {
RenderBox a, b, c, d, e;
RenderViewport2 root = new RenderViewport2(
axisDirection: AxisDirection.right,
offset: new ViewportOffset.zero(),
children: <RenderSliver>[
new RenderSliverToBoxAdapter(child: a = new RenderSizedBox(const Size(400.0, 100.0))),
new RenderSliverToBoxAdapter(child: b = new RenderSizedBox(const Size(400.0, 100.0))),
......@@ -162,6 +167,7 @@ void main() {
RenderBox a, b, c, d, e;
RenderViewport2 root = new RenderViewport2(
axisDirection: AxisDirection.left,
offset: new ViewportOffset.zero(),
children: <RenderSliver>[
new RenderSliverToBoxAdapter(child: a = new RenderSizedBox(const Size(400.0, 100.0))),
new RenderSliverToBoxAdapter(child: b = new RenderSizedBox(const Size(400.0, 100.0))),
......
// Copyright 2016 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/rendering.dart';
import 'package:flutter/widgets.dart';
int globalGeneration = 0;
class GenerationText extends StatefulWidget {
GenerationText(this.value);
final int value;
@override
_GenerationTextState createState() => new _GenerationTextState();
}
class _GenerationTextState extends State<GenerationText> {
_GenerationTextState() : generation = globalGeneration;
final int generation;
@override
Widget build(BuildContext context) => new Text('${config.value}:$generation ');
}
Future<Null> test(WidgetTester tester, double offset, List<int> keys) {
globalGeneration += 1;
return tester.pumpWidget(new Viewport2(
offset: new ViewportOffset.fixed(offset),
children: <Widget>[
new SliverBlock(
delegate: new SliverBlockChildListDelegate(keys.map((int key) {
return new SizedBox(key: new GlobalObjectKey(key), height: 100.0, child: new GenerationText(key));
}).toList()),
),
],
));
}
void verify(WidgetTester tester, List<Point> answerKey, String text) {
List<Point> testAnswers = tester.renderObjectList/*<RenderBox>*/(find.byType(SizedBox)).map/*<Point>*/(
(RenderBox target) => target.localToGlobal(const Point(0.0, 0.0))
).toList();
expect(testAnswers, equals(answerKey));
final String foundText =
tester.widgetList/*<Text>*/(find.byType(Text))
.map/*<String>*/((Text widget) => widget.data)
.reduce((String value, String element) => value + element);
expect(foundText, equals(text));
}
void main() {
testWidgets('Viewport2+SliverBlock with GlobalKey reparenting', (WidgetTester tester) async {
await test(tester, 0.0, <int>[1,2,3,4,5,6,7,8,9]);
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 100.0),
const Point(0.0, 200.0),
const Point(0.0, 300.0),
const Point(0.0, 400.0),
const Point(0.0, 500.0),
], '1:1 2:1 3:1 4:1 5:1 6:1 ');
// gen 2 - flipping the order:
await test(tester, 0.0, <int>[9,8,7,6,5,4,3,2,1]);
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 100.0),
const Point(0.0, 200.0),
const Point(0.0, 300.0),
const Point(0.0, 400.0),
const Point(0.0, 500.0),
], '9:2 8:2 7:2 6:1 5:1 4:1 ');
// gen 3 - flipping the order back:
await test(tester, 0.0, <int>[1,2,3,4,5,6,7,8,9]);
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 100.0),
const Point(0.0, 200.0),
const Point(0.0, 300.0),
const Point(0.0, 400.0),
const Point(0.0, 500.0),
], '1:3 2:3 3:3 4:1 5:1 6:1 ');
// gen 4 - removal:
await test(tester, 0.0, <int>[1,2,3,5,6,7,8,9]);
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 100.0),
const Point(0.0, 200.0),
const Point(0.0, 300.0),
const Point(0.0, 400.0),
const Point(0.0, 500.0),
], '1:3 2:3 3:3 5:1 6:1 7:4 ');
// gen 5 - insertion:
await test(tester, 0.0, <int>[1,2,3,4,5,6,7,8,9]);
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 100.0),
const Point(0.0, 200.0),
const Point(0.0, 300.0),
const Point(0.0, 400.0),
const Point(0.0, 500.0),
], '1:3 2:3 3:3 4:5 5:1 6:1 ');
// gen 6 - adjacent reordering:
await test(tester, 0.0, <int>[1,2,3,5,4,6,7,8,9]);
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 100.0),
const Point(0.0, 200.0),
const Point(0.0, 300.0),
const Point(0.0, 400.0),
const Point(0.0, 500.0),
], '1:3 2:3 3:3 5:1 4:5 6:1 ');
// gen 7 - scrolling:
await test(tester, 120.0, <int>[1,2,3,5,4,6,7,8,9]);
verify(tester, <Point>[
const Point(0.0, -20.0),
const Point(0.0, 80.0),
const Point(0.0, 180.0),
const Point(0.0, 280.0),
const Point(0.0, 380.0),
const Point(0.0, 480.0),
const Point(0.0, 580.0),
], '2:3 3:3 5:1 4:5 6:1 7:7 8:7 ');
});
}
// Copyright 2016 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/rendering.dart';
import 'package:flutter/widgets.dart';
Future<Null> test(WidgetTester tester, double offset) {
return tester.pumpWidget(new Viewport2(
offset: new ViewportOffset.fixed(offset),
children: <Widget>[
new SliverBlock(
delegate: new SliverBlockChildListDelegate(<Widget>[
new SizedBox(height: 400.0, child: new Text('a')),
new SizedBox(height: 400.0, child: new Text('b')),
new SizedBox(height: 400.0, child: new Text('c')),
new SizedBox(height: 400.0, child: new Text('d')),
new SizedBox(height: 400.0, child: new Text('e')),
]),
),
],
));
}
void verify(WidgetTester tester, List<Point> answerKey, String text) {
List<Point> testAnswers = tester.renderObjectList/*<RenderBox>*/(find.byType(SizedBox)).map/*<Point>*/(
(RenderBox target) => target.localToGlobal(const Point(0.0, 0.0))
).toList();
expect(testAnswers, equals(answerKey));
final String foundText =
tester.widgetList/*<Text>*/(find.byType(Text))
.map/*<String>*/((Text widget) => widget.data)
.reduce((String value, String element) => value + element);
expect(foundText, equals(text));
}
void main() {
testWidgets('Viewport2+SliverBlock basic test', (WidgetTester tester) async {
await test(tester, 0.0);
expect(tester.renderObject/*<RenderBox>*/(find.byType(Viewport2)).size, equals(const Size(800.0, 600.0)));
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 400.0),
], 'ab');
await test(tester, 200.0);
verify(tester, <Point>[
const Point(0.0, -200.0),
const Point(0.0, 200.0),
], 'ab');
await test(tester, 600.0);
verify(tester, <Point>[
const Point(0.0, -200.0),
const Point(0.0, 200.0),
], 'bc');
await test(tester, 900.0);
verify(tester, <Point>[
const Point(0.0, -100.0),
const Point(0.0, 300.0),
], 'cd');
await test(tester, 200.0);
verify(tester, <Point>[
const Point(0.0, -200.0),
const Point(0.0, 200.0),
], 'ab');
});
testWidgets('Viewport2 with GlobalKey reparenting', (WidgetTester tester) async {
Key key1 = new GlobalKey();
ViewportOffset offset = new ViewportOffset.zero();
await tester.pumpWidget(new Viewport2(
offset: offset,
children: <Widget>[
new SliverBlock(
delegate: new SliverBlockChildListDelegate(<Widget>[
new SizedBox(height: 251.0, child: new Text('a')),
new SizedBox(height: 252.0, child: new Text('b')),
new SizedBox(key: key1, height: 253.0, child: new Text('c')),
]),
),
],
));
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 251.0),
const Point(0.0, 503.0),
], 'abc');
await tester.pumpWidget(new Viewport2(
offset: offset,
children: <Widget>[
new SliverBlock(
delegate: new SliverBlockChildListDelegate(<Widget>[
new SizedBox(key: key1, height: 253.0, child: new Text('c')),
new SizedBox(height: 251.0, child: new Text('a')),
new SizedBox(height: 252.0, child: new Text('b')),
]),
),
],
));
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 253.0),
const Point(0.0, 504.0),
], 'cab');
await tester.pumpWidget(new Viewport2(
offset: offset,
children: <Widget>[
new SliverBlock(
delegate: new SliverBlockChildListDelegate(<Widget>[
new SizedBox(height: 251.0, child: new Text('a')),
new SizedBox(key: key1, height: 253.0, child: new Text('c')),
new SizedBox(height: 252.0, child: new Text('b')),
]),
),
],
));
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 251.0),
const Point(0.0, 504.0),
], 'acb');
await tester.pumpWidget(new Viewport2(
offset: offset,
children: <Widget>[
new SliverBlock(
delegate: new SliverBlockChildListDelegate(<Widget>[
new SizedBox(height: 251.0, child: new Text('a')),
new SizedBox(height: 252.0, child: new Text('b')),
]),
),
],
));
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 251.0),
], 'ab');
await tester.pumpWidget(new Viewport2(
offset: offset,
children: <Widget>[
new SliverBlock(
delegate: new SliverBlockChildListDelegate(<Widget>[
new SizedBox(height: 251.0, child: new Text('a')),
new SizedBox(key: key1, height: 253.0, child: new Text('c')),
new SizedBox(height: 252.0, child: new Text('b')),
]),
),
],
));
verify(tester, <Point>[
const Point(0.0, 0.0),
const Point(0.0, 251.0),
const Point(0.0, 504.0),
], 'acb');
});
}
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