blob: d46da5f13a64112796da09d9e821764f64d05470 [file] [log] [blame]
// Copyright 2014 The Flutter 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:flutter_test/flutter_test.dart';
import 'rendering_tester.dart';
void main() {
TestRenderingFlutterBinding.ensureInitialized();
test('RenderViewport calculates correct constraints, RenderSliverToBoxAdapter calculates correct geometry', () {
final List<RenderSliver> children = List<RenderSliver>.generate(30, (int index) {
return RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(400.0, 100.0)),
);
});
// Viewport is 800x600, can show 6 children at a time.
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: 250.0,
children: children,
);
layout(root);
RenderSliver firstVisible = children[0];
expectSliverConstraints(
sliver: firstVisible,
cacheOrigin: 0.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 600.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisible,
paintExtent: 100.0,
cacheExtent: 100.0,
visible: true,
);
RenderSliver lastVisible = children[5];
expectSliverConstraints(
sliver: lastVisible,
cacheOrigin: 0.0,
remainingPaintExtent: 100.0,
remainingCacheExtent: 350.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastVisible,
paintExtent: 100.0,
cacheExtent: 100.0,
visible: true,
);
RenderSliver firstInCache = children[6];
expectSliverConstraints(
sliver: firstInCache,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstInCache,
paintExtent: 0.0,
cacheExtent: 100.0,
visible: false,
);
RenderSliver lastInCache = children[8];
expectSliverConstraints(
sliver: lastInCache,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 50.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastInCache,
paintExtent: 0.0,
cacheExtent: 50.0,
visible: false,
);
RenderSliver outsideCache = children[9];
expectSliverConstraints(
sliver: outsideCache,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 0.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: outsideCache,
paintExtent: 0.0,
cacheExtent: 0.0,
visible: false,
);
// scroll down half a sliver
root.offset = ViewportOffset.fixed(50.0);
pumpFrame();
firstVisible = children[0];
expectSliverConstraints(
sliver: firstVisible,
cacheOrigin: -50.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 50.0 + 600.0 + 250.0,
scrollOffset: 50.0,
);
expectSliverGeometry(
sliver: firstVisible,
paintExtent: 50.0,
cacheExtent: 100.0,
visible: true,
);
lastVisible = children[6];
expectSliverConstraints(
sliver: lastVisible,
cacheOrigin: 0.0,
remainingPaintExtent: 50.0,
remainingCacheExtent: 300.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastVisible,
paintExtent: 50.0,
cacheExtent: 100.0,
visible: true,
);
firstInCache = children[7];
expectSliverConstraints(
sliver: firstInCache,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 200.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstInCache,
paintExtent: 0.0,
cacheExtent: 100.0,
visible: false,
);
lastInCache = children[8];
expectSliverConstraints(
sliver: lastInCache,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 100.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastInCache,
paintExtent: 0.0,
cacheExtent: 100.0,
visible: false,
);
outsideCache = children[9];
expectSliverConstraints(
sliver: outsideCache,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 0.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: outsideCache,
paintExtent: 0.0,
cacheExtent: 0.0,
visible: false,
);
// scroll down 1.5 slivers
root.offset = ViewportOffset.fixed(150.0);
pumpFrame();
RenderSliver firstInPreCache = children[0];
expectSliverConstraints(
sliver: firstInPreCache,
cacheOrigin: -150.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 150.0 + 600.0 + 250.0,
scrollOffset: 150.0,
);
expectSliverGeometry(
sliver: firstInPreCache,
paintExtent: 0.0,
cacheExtent: 100.0,
visible: false,
);
firstVisible = children[1];
expectSliverConstraints(
sliver: firstVisible,
cacheOrigin: -50.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 50.0 + 600.0 + 250.0,
scrollOffset: 50.0,
);
expectSliverGeometry(
sliver: firstVisible,
paintExtent: 50.0,
cacheExtent: 100.0,
visible: true,
);
// scroll down 10 slivers
root.offset = ViewportOffset.fixed(1000.0);
pumpFrame();
final RenderSliver first = children[0];
expectSliverConstraints(
sliver: first,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 1000.0,
);
expectSliverGeometry(
sliver: first,
paintExtent: 0.0,
cacheExtent: 0.0,
visible: false,
);
firstInPreCache = children[7];
expectSliverConstraints(
sliver: firstInPreCache,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 300.0,
);
expectSliverGeometry(
sliver: firstInPreCache,
paintExtent: 0.0,
cacheExtent: 50.0,
visible: false,
);
final RenderSliver lastInPreCache = children[9];
expectSliverConstraints(
sliver: lastInPreCache,
cacheOrigin: -100.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 100.0 + 600.0 + 250.0,
scrollOffset: 100.0,
);
expectSliverGeometry(
sliver: lastInPreCache,
paintExtent: 0.0,
cacheExtent: 100.0,
visible: false,
);
firstVisible = children[10];
expectSliverConstraints(
sliver: firstVisible,
cacheOrigin: 0.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 600.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisible,
paintExtent: 100.0,
cacheExtent: 100.0,
visible: true,
);
});
test('RenderSliverFixedExtentList calculates correct geometry', () {
// Viewport is 800x600, can show 6 full children at a time
final List<RenderBox> children = List<RenderBox>.generate(30, (int index) {
return RenderSizedBox(const Size(400.0, 100.0));
});
final TestRenderSliverBoxChildManager childManager = TestRenderSliverBoxChildManager(
children: children,
);
RenderSliverFixedExtentList inner;
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: 250.0,
children: <RenderSliver>[
inner = childManager.createRenderSliverFixedExtentList(),
],
);
layout(root);
expectSliverConstraints(
sliver: inner,
cacheOrigin: 0.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 600.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 850.0,
visible: true,
);
expect(children.sublist(0, 9).every((RenderBox r) => r.attached), true);
expect(children.sublist(9, 30).any((RenderBox r) => r.attached), false);
// scroll half an item down
root.offset = ViewportOffset.fixed(50.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -50.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 50.0 + 600.0 + 250.0,
scrollOffset: 50.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 900.0,
visible: true,
);
expect(children.sublist(0, 9).every((RenderBox r) => r.attached), true);
expect(children.sublist(9, 30).any((RenderBox r) => r.attached), false);
// scroll to the middle
root.offset = ViewportOffset.fixed(1500.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 1500.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 1100.0,
visible: true,
);
expect(children.sublist(0, 12).any((RenderBox r) => r.attached), false);
expect(children.sublist(12, 24).every((RenderBox r) => r.attached), true);
expect(children.sublist(24, 30).any((RenderBox r) => r.attached), false);
// scroll to the end
root.offset = ViewportOffset.fixed(2400.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 2400.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 850.0,
visible: true,
);
expect(children.sublist(0, 21).any((RenderBox r) => r.attached), false);
expect(children.sublist(21, 30).every((RenderBox r) => r.attached), true);
});
test('RenderSliverList calculates correct geometry', () {
// Viewport is 800x600, can show 6 full children at a time
final List<RenderBox> children = List<RenderBox>.generate(30, (int index) {
return RenderSizedBox(const Size(400.0, 100.0));
});
final TestRenderSliverBoxChildManager childManager = TestRenderSliverBoxChildManager(
children: children,
);
RenderSliverList inner;
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: 250.0,
children: <RenderSliver>[
inner = childManager.createRenderSliverList(),
],
);
layout(root);
expectSliverConstraints(
sliver: inner,
cacheOrigin: 0.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 600.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 850.0,
visible: true,
);
expect(children.sublist(0, 9).every((RenderBox r) => r.attached), true);
expect(children.sublist(9, 30).any((RenderBox r) => r.attached), false);
// scroll half an item down
root.offset = ViewportOffset.fixed(50.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -50.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 50.0 + 600.0 + 250.0,
scrollOffset: 50.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 900.0,
visible: true,
);
expect(children.sublist(0, 9).every((RenderBox r) => r.attached), true);
expect(children.sublist(9, 30).any((RenderBox r) => r.attached), false);
// scroll to the middle
root.offset = ViewportOffset.fixed(1500.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 1500.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 1100.0,
visible: true,
);
expect(children.sublist(0, 12).any((RenderBox r) => r.attached), false);
expect(children.sublist(12, 24).every((RenderBox r) => r.attached), true);
expect(children.sublist(24, 30).any((RenderBox r) => r.attached), false);
// scroll to the end
root.offset = ViewportOffset.fixed(2400.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 2400.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 850.0,
visible: true,
);
expect(children.sublist(0, 21).any((RenderBox r) => r.attached), false);
expect(children.sublist(21, 30).every((RenderBox r) => r.attached), true);
});
test('RenderSliverGrid calculates correct geometry', () {
// Viewport is 800x600, each grid element is 400x100, giving us space for 12 visible children
final List<RenderBox> children = List<RenderBox>.generate(60, (int index) {
return RenderSizedBox(const Size(400.0, 100.0));
});
final TestRenderSliverBoxChildManager childManager = TestRenderSliverBoxChildManager(
children: children,
);
RenderSliverGrid inner;
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: 250.0,
children: <RenderSliver>[
inner = childManager.createRenderSliverGrid(),
],
);
layout(root);
expectSliverConstraints(
sliver: inner,
cacheOrigin: 0.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 600.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 850.0,
visible: true,
);
expect(children.sublist(0, 18).every((RenderBox r) => r.attached), true);
expect(children.sublist(18, 60).any((RenderBox r) => r.attached), false);
// scroll half an item down
root.offset = ViewportOffset.fixed(50.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -50.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 50.0 + 600.0 + 250.0,
scrollOffset: 50.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 900.0,
visible: true,
);
expect(children.sublist(0, 18).every((RenderBox r) => r.attached), true);
expect(children.sublist(18, 60).any((RenderBox r) => r.attached), false);
// scroll to the middle
root.offset = ViewportOffset.fixed(1500.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 1500.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 1100.0,
visible: true,
);
expect(children.sublist(0, 24).any((RenderBox r) => r.attached), false);
expect(children.sublist(24, 48).every((RenderBox r) => r.attached), true);
expect(children.sublist(48, 60).any((RenderBox r) => r.attached), false);
// scroll to the end
root.offset = ViewportOffset.fixed(2400.0);
pumpFrame();
expectSliverConstraints(
sliver: inner,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 2400.0,
);
expectSliverGeometry(
sliver: inner,
paintExtent: 600.0,
cacheExtent: 850.0,
visible: true,
);
expect(children.sublist(0, 42).any((RenderBox r) => r.attached), false);
expect(children.sublist(42, 60).every((RenderBox r) => r.attached), true);
});
test('RenderSliverPadding calculates correct geometry', () {
// Viewport is 800x600, each item is 100px high with 50px before and after = 200px
final List<RenderSliverToBoxAdapter> adapters = <RenderSliverToBoxAdapter>[];
final List<RenderSliverPadding> paddings = List<RenderSliverPadding>.generate(30, (int index) {
RenderSliverToBoxAdapter adapter;
final RenderSliverPadding padding = RenderSliverPadding(
padding: const EdgeInsets.symmetric(vertical: 50.0),
child: adapter = RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(400.0, 100.0)),
),
);
adapters.add(adapter);
return padding;
});
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: 250.0,
children: paddings,
);
layout(root);
RenderSliverPadding firstVisiblePadding = paddings[0];
expectSliverConstraints(
sliver: firstVisiblePadding,
cacheOrigin: 0.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 600.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisiblePadding,
paintExtent: 200.0,
cacheExtent: 200.0,
visible: true,
);
RenderSliverToBoxAdapter firstVisiblePadded = adapters[0];
expectSliverConstraints(
sliver: firstVisiblePadded,
cacheOrigin: 0.0,
remainingPaintExtent: 550.0,
remainingCacheExtent: 600.0 + 250.0 - 50.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisiblePadded,
paintExtent: 100.0,
cacheExtent: 100.0,
visible: true,
);
RenderSliverPadding lastVisiblePadding = paddings[2];
expectSliverConstraints(
sliver: lastVisiblePadding,
cacheOrigin: 0.0,
remainingPaintExtent: 200.0,
remainingCacheExtent: 200.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastVisiblePadding,
paintExtent: 200.0,
cacheExtent: 200.0,
visible: true,
);
RenderSliverToBoxAdapter lastVisiblePadded = adapters[2];
expectSliverConstraints(
sliver: lastVisiblePadded,
cacheOrigin: 0.0,
remainingPaintExtent: 150.0,
remainingCacheExtent: 200.0 + 250.0 - 50.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastVisiblePadded,
paintExtent: 100.0,
cacheExtent: 100.0,
visible: true,
);
final RenderSliverPadding firstCachePadding = paddings[3];
expectSliverConstraints(
sliver: firstCachePadding,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstCachePadding,
paintExtent: 0.0,
cacheExtent: 200.0,
visible: false,
);
final RenderSliverToBoxAdapter firstCachePadded = adapters[3];
expectSliverConstraints(
sliver: firstCachePadded,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 250.0 - 50.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstCachePadded,
paintExtent: 0.0,
cacheExtent: 100.0,
visible: false,
);
final RenderSliverPadding lastCachePadding = paddings[4];
expectSliverConstraints(
sliver: lastCachePadding,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 50.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastCachePadding,
paintExtent: 0.0,
cacheExtent: 50.0,
visible: false,
);
final RenderSliverToBoxAdapter lastCachePadded = adapters[4];
expectSliverConstraints(
sliver: lastCachePadded,
cacheOrigin: 0.0,
remainingPaintExtent: 0.0,
remainingCacheExtent: 0.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastCachePadded,
paintExtent: 0.0,
cacheExtent: 0.0,
visible: false,
);
// scroll first padding off screen
root.offset = ViewportOffset.fixed(50.0);
pumpFrame();
firstVisiblePadding = paddings[0];
expectSliverConstraints(
sliver: firstVisiblePadding,
cacheOrigin: -50.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 50.0 + 600.0 + 250.0,
scrollOffset: 50.0,
);
expectSliverGeometry(
sliver: firstVisiblePadding,
paintExtent: 150.0,
cacheExtent: 200.0,
visible: true,
);
firstVisiblePadded = adapters[0];
expectSliverConstraints(
sliver: firstVisiblePadded,
cacheOrigin: 0.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 600.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisiblePadded,
paintExtent: 100.0,
cacheExtent: 100.0,
visible: true,
);
// scroll to the end
root.offset = ViewportOffset.fixed(5400.0);
pumpFrame();
final RenderSliverPadding firstPadding = paddings[0];
expectSliverConstraints(
sliver: firstPadding,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 5400.0,
);
expectSliverGeometry(
sliver: firstPadding,
paintExtent: 0.0,
cacheExtent: 0.0,
visible: false,
);
final RenderSliverToBoxAdapter firstPadded = adapters[0];
expectSliverConstraints(
sliver: firstPadded,
cacheOrigin: -200.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 5350.0,
);
expectSliverGeometry(
sliver: firstPadded,
paintExtent: 0.0,
cacheExtent: 0.0,
visible: false,
);
final RenderSliverPadding firstPreCachePadding = paddings[25];
expectSliverConstraints(
sliver: firstPreCachePadding,
cacheOrigin: -250.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 400.0,
);
expectSliverGeometry(
sliver: firstPreCachePadding,
paintExtent: 0.0,
cacheExtent: 50.0,
visible: false,
);
final RenderSliverToBoxAdapter firstPreCachePadded = adapters[25];
expectSliverConstraints(
sliver: firstPreCachePadded,
cacheOrigin: -200.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 250.0 + 600.0 + 250.0,
scrollOffset: 350.0,
);
expectSliverGeometry(
sliver: firstPreCachePadded,
paintExtent: 0.0,
cacheExtent: 0.0,
visible: false,
);
final RenderSliverPadding lastPreCachePadding = paddings[26];
expectSliverConstraints(
sliver: lastPreCachePadding,
cacheOrigin: -200.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 200.0 + 600.0 + 250.0,
scrollOffset: 200.0,
);
expectSliverGeometry(
sliver: lastPreCachePadding,
paintExtent: 0.0,
cacheExtent: 200.0,
visible: false,
);
final RenderSliverToBoxAdapter lastPreCachePadded = adapters[26];
expectSliverConstraints(
sliver: lastPreCachePadded,
cacheOrigin: -150.0,
remainingPaintExtent: 600.0,
remainingCacheExtent: 150.0 + 600.0 + 250.0,
scrollOffset: 150.0,
);
expectSliverGeometry(
sliver: lastPreCachePadded,
paintExtent: 0.0,
cacheExtent: 100.0,
visible: false,
);
lastVisiblePadding = paddings[29];
expectSliverConstraints(
sliver: lastVisiblePadding,
cacheOrigin: 0.0,
remainingPaintExtent: 200.0,
remainingCacheExtent: 200.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastVisiblePadding,
paintExtent: 200.0,
cacheExtent: 200.0,
visible: true,
);
lastVisiblePadded = adapters[29];
expectSliverConstraints(
sliver: lastVisiblePadded,
cacheOrigin: 0.0,
remainingPaintExtent: 150.0,
remainingCacheExtent: 150.0 + 250.0,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: lastVisiblePadded,
paintExtent: 100.0,
cacheExtent: 100.0,
visible: true,
);
});
group('RenderSliverFillRemaining calculates correct geometry', () {
test('when initially in view', () {
// Viewport is 800x600
const double viewportHeight = 600;
const double viewportWidth = 800;
const double cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = 400;
const double sliverFillRemainingChildHeight = 100.0;
final List<RenderSliver> slivers = <RenderSliver>[
RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(400.0, firstSliverHeight)),
),
RenderSliverFillRemaining(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
)
];
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: cacheExtent,
children: slivers,
);
layout(root);
final RenderSliver firstVisibleSliver = slivers[0];
expectSliverConstraints(
sliver: firstVisibleSliver,
cacheOrigin: 0.0,
remainingPaintExtent: viewportHeight,
remainingCacheExtent: beginningViewportCacheExtent,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisibleSliver,
paintExtent: firstSliverHeight,
cacheExtent: firstSliverHeight,
visible: true,
);
// With RenderSliverFillRemaining:
// * The child has a minExtent and maxExtent of the remaining space of the
// viewportMainAxisExtent or the height of the child - whichever is larger.
// * The sliver has a paintExtent of the child's minExtent/maxExtent or the
// remainingPaintExtent - whichever is smaller.
// * The sliver has a cacheExtent of the child's minExtent/maxExtent or the
// remainingCacheExtent - whichever is smaller.
final RenderSliverSingleBoxAdapter sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
const double extentOfChild = viewportHeight - firstSliverHeight;
double remainingPaintExtent = viewportHeight - firstSliverHeight;
double remainingCacheExtent = beginningViewportCacheExtent - firstSliverHeight;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: extentOfChild,
minHeight: extentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: extentOfChild,
cacheExtent: extentOfChild,
visible: true,
);
// Overscroll
const double scrollOffset = 50;
root.offset = ViewportOffset.fixed(scrollOffset);
pumpFrame();
remainingPaintExtent = viewportHeight - firstSliverHeight + scrollOffset;
remainingCacheExtent = beginningViewportCacheExtent - firstSliverHeight + scrollOffset;
// With RenderSliverFillRemaining, when you overscroll, the extent of the
// child does not change and therefore neither does paintExtent or cacheExtent.
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: extentOfChild,
minHeight: extentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: extentOfChild,
cacheExtent: extentOfChild,
visible: true,
);
});
test('when scrolled into view', () {
// Viewport is 800x600
const double viewportHeight = 600;
const double viewportWidth = 800;
const double cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = beginningViewportCacheExtent;
const double sliverFillRemainingChildHeight = 100.0;
final List<RenderSliver> slivers = <RenderSliver>[
RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(400.0, firstSliverHeight)),
),
RenderSliverFillRemaining(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
)
];
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: cacheExtent,
children: slivers,
);
layout(root);
final RenderSliver firstVisibleSliver = slivers[0];
expectSliverConstraints(
sliver: firstVisibleSliver,
cacheOrigin: 0.0,
remainingPaintExtent: viewportHeight,
remainingCacheExtent: beginningViewportCacheExtent,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisibleSliver,
paintExtent: viewportHeight,
cacheExtent: firstSliverHeight,
visible: true,
);
// With RenderSliverFillRemaining:
// * The child has a minExtent and maxExtent of the remaining space of the
// viewportMainAxisExtent or the height of the child - whichever is larger.
// * The sliver has a paintExtent of the child's minExtent/maxExtent or the
// remainingPaintExtent - whichever is smaller.
// * The sliver has a cacheExtent of the child's minExtent/maxExtent or the
// remainingCacheExtent - whichever is smaller.
final RenderSliverSingleBoxAdapter sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
const double extentOfChild = sliverFillRemainingChildHeight;
double remainingPaintExtent = 0;
double remainingCacheExtent = 0;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: extentOfChild,
minHeight: extentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: remainingPaintExtent,
cacheExtent: remainingCacheExtent,
visible: false,
);
// Scroll so RenderSliverFillRemaining is not within viewport, but is
// within remainingCacheExtent.
root.offset = ViewportOffset.fixed(cacheExtent);
pumpFrame();
remainingPaintExtent = 0;
remainingCacheExtent = cacheExtent;
// When within the remainingCacheExtent, the sliver will have a cacheExtent
// of the child's extent.
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: extentOfChild,
minHeight: extentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: remainingPaintExtent,
cacheExtent: extentOfChild,
visible: false,
);
// Scroll so RenderSliverFillRemaining is partially within viewport.
root.offset = ViewportOffset.fixed(cacheExtent + 50);
pumpFrame();
remainingPaintExtent = 50;
remainingCacheExtent = cacheExtent + 50;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: extentOfChild,
minHeight: extentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: remainingPaintExtent,
cacheExtent: extentOfChild,
visible: true,
);
// Scroll so RenderSliverFillRemaining is completely within viewport.
root.offset = ViewportOffset.fixed(cacheExtent + sliverFillRemainingChildHeight);
pumpFrame();
remainingPaintExtent = sliverFillRemainingChildHeight;
remainingCacheExtent = cacheExtent + sliverFillRemainingChildHeight;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: extentOfChild,
minHeight: extentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: extentOfChild,
cacheExtent: extentOfChild,
visible: true,
);
// Overscroll
root.offset = ViewportOffset.fixed(cacheExtent + sliverFillRemainingChildHeight + 50);
pumpFrame();
remainingPaintExtent = sliverFillRemainingChildHeight + 50;
remainingCacheExtent = cacheExtent + sliverFillRemainingChildHeight + 50;
// With RenderSliverFillRemaining, when you overscroll, the extent of the
// child does not change and therefore neither does paintExtent or cacheExtent.
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: extentOfChild,
minHeight: extentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: extentOfChild,
cacheExtent: extentOfChild,
visible: true,
);
});
});
group('RenderSliverFillRemainingAndOverscroll calculates correct geometry', () {
test('when initially in view', () {
// Viewport is 800x600
const double viewportHeight = 600;
const double viewportWidth = 800;
const double cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = 400;
const double sliverFillRemainingChildHeight = 100.0;
final List<RenderSliver> slivers = <RenderSliver>[
RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(400.0, firstSliverHeight)),
),
RenderSliverFillRemainingAndOverscroll(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
)
];
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: cacheExtent,
children: slivers,
);
layout(root);
final RenderSliver firstVisibleSliver = slivers[0];
expectSliverConstraints(
sliver: firstVisibleSliver,
cacheOrigin: 0.0,
remainingPaintExtent: viewportHeight,
remainingCacheExtent: beginningViewportCacheExtent,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisibleSliver,
paintExtent: firstSliverHeight,
cacheExtent: firstSliverHeight,
visible: true,
);
// With RenderSliverFillRemainingAndOverscroll:
// * The child has a minExtent of the remaining space of the viewportMainAxisExtent
// or the height of the child - whichever is larger.
// * The child has a maxExtent of the remaining space of the viewportMainAxisExtent,
// the height of the child, or the remainingPaintExtent - whichever is larger.
// * The sliver has a paintExtent of the child's maxExtent or the
// remainingPaintExtent - whichever is smaller.
// * The sliver has a cacheExtent of the child's minExtent or the
// remainingCacheExtent - whichever is smaller.
final RenderSliverSingleBoxAdapter sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
const double minExtentOfChild = viewportHeight - firstSliverHeight;
double maxExtentOfChild = viewportHeight - firstSliverHeight;
double remainingPaintExtent = viewportHeight - firstSliverHeight;
double remainingCacheExtent = beginningViewportCacheExtent - firstSliverHeight;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: maxExtentOfChild,
cacheExtent: minExtentOfChild,
visible: true,
);
// Overscroll
const double scrollOffset = 50;
root.offset = ViewportOffset.fixed(scrollOffset);
pumpFrame();
remainingPaintExtent = viewportHeight - firstSliverHeight + scrollOffset;
remainingCacheExtent = beginningViewportCacheExtent - firstSliverHeight + scrollOffset;
// When you overscroll, the child's maxExtent is the
// remainingPaintExtent, since it's the higher value.
maxExtentOfChild = remainingPaintExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: maxExtentOfChild,
cacheExtent: minExtentOfChild,
visible: true,
);
});
test('when scrolled into view', () {
// Viewport is 800x600
const double viewportHeight = 600;
const double viewportWidth = 800;
const double cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = beginningViewportCacheExtent;
const double sliverFillRemainingChildHeight = 100.0;
final List<RenderSliver> slivers = <RenderSliver>[
RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(400.0, firstSliverHeight)),
),
RenderSliverFillRemainingAndOverscroll(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
)
];
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: cacheExtent,
children: slivers,
);
layout(root);
final RenderSliver firstVisibleSliver = slivers[0];
expectSliverConstraints(
sliver: firstVisibleSliver,
cacheOrigin: 0.0,
remainingPaintExtent: viewportHeight,
remainingCacheExtent: beginningViewportCacheExtent,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisibleSliver,
paintExtent: viewportHeight,
cacheExtent: firstSliverHeight,
visible: true,
);
// With RenderSliverFillRemainingAndOverscroll:
// * The child has a minExtent of the remaining space of the viewportMainAxisExtent
// or the height of the child - whichever is larger.
// * The child has a maxExtent of the remaining space of the viewportMainAxisExtent,
// the height of the child, or the remainingPaintExtent - whichever is larger.
// * The sliver has a paintExtent of the child's maxExtent or the
// remainingPaintExtent - whichever is smaller.
// * The sliver has a cacheExtent of the child's minExtent or the
// remainingCacheExtent - whichever is smaller.
final RenderSliverSingleBoxAdapter sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
const double minExtentOfChild = sliverFillRemainingChildHeight;
double maxExtentOfChild = sliverFillRemainingChildHeight;
double remainingPaintExtent = 0;
double remainingCacheExtent = 0;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: remainingPaintExtent,
cacheExtent: remainingCacheExtent,
visible: false,
);
// Scroll so RenderSliverFillRemainingAndOverscroll is not within viewport,
// but is within remainingCacheExtent.
root.offset = ViewportOffset.fixed(cacheExtent);
pumpFrame();
remainingPaintExtent = 0;
remainingCacheExtent = cacheExtent;
// When within the remainingCacheExtent, the sliver will have a cacheExtent
// of the child's minExtent.
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: remainingPaintExtent,
cacheExtent: minExtentOfChild,
visible: false,
);
// Scroll so RenderSliverFillRemainingAndOverscroll is partially within
// the viewport.
root.offset = ViewportOffset.fixed(cacheExtent + 50);
pumpFrame();
remainingPaintExtent = 50;
remainingCacheExtent = cacheExtent + 50;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: remainingPaintExtent,
cacheExtent: minExtentOfChild,
visible: true,
);
// Scroll so RenderSliverFillRemainingAndOverscroll is completely within
// the viewport.
root.offset = ViewportOffset.fixed(cacheExtent + sliverFillRemainingChildHeight);
pumpFrame();
remainingPaintExtent = sliverFillRemainingChildHeight;
remainingCacheExtent = cacheExtent + sliverFillRemainingChildHeight;
// When completely in view, the slivers's paintExtent is the child's
// maxExtentOfChild, since it's the smaller value.
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: maxExtentOfChild,
cacheExtent: minExtentOfChild,
visible: true,
);
// Overscroll
root.offset = ViewportOffset.fixed(cacheExtent + sliverFillRemainingChildHeight + 50);
pumpFrame();
remainingPaintExtent = sliverFillRemainingChildHeight + 50;
remainingCacheExtent = cacheExtent + sliverFillRemainingChildHeight + 50;
// When you overscroll, the child's maxExtent is the remainingPaintExtent,
// since it's the higher value.
maxExtentOfChild = remainingPaintExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: maxExtentOfChild,
cacheExtent: minExtentOfChild,
visible: true,
);
});
});
group('RenderSliverFillRemainingWithScrollable calculates correct geometry', () {
test('when initially in view', () {
// Viewport is 800x600
const double viewportHeight = 600;
const double viewportWidth = 800;
const double cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = 400;
const double sliverFillRemainingChildHeight = 100.0;
final List<RenderSliver> slivers = <RenderSliver>[
RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(400.0, firstSliverHeight)),
),
RenderSliverFillRemainingWithScrollable(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
)
];
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: cacheExtent,
children: slivers,
);
layout(root);
final RenderSliver firstVisibleSliver = slivers[0];
expectSliverConstraints(
sliver: firstVisibleSliver,
cacheOrigin: 0.0,
remainingPaintExtent: viewportHeight,
remainingCacheExtent: beginningViewportCacheExtent,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisibleSliver,
paintExtent: firstSliverHeight,
cacheExtent: firstSliverHeight,
visible: true,
);
// With RenderSliverFillRemainingWithScrollable:
// * The child has a minExtent of the remainingPaintExtent.
// * If not within the viewport but within the cacheExtent, the child has
// a maxExtent of the sliver's cacheExtent. Otherwise, the child has a
// maxExtent of the remainingPaintExtent
// * The sliver has a paintExtent of the child's minExtent or the
// remainingPaintExtent - whichever is smaller.
// * The sliver has a cacheExtent of either the viewportMainAxisExtent or
// the remainingCacheExtent - whichever is smaller.
final RenderSliverSingleBoxAdapter sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
double remainingPaintExtent = viewportHeight - firstSliverHeight;
double remainingCacheExtent = beginningViewportCacheExtent - firstSliverHeight;
double minExtentOfChild = remainingPaintExtent;
double maxExtentOfChild = remainingPaintExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: minExtentOfChild,
cacheExtent: remainingCacheExtent,
visible: true,
);
// Overscroll so first sliver is partially out of view.
root.offset = ViewportOffset.fixed(50);
pumpFrame();
remainingPaintExtent = viewportHeight - firstSliverHeight + 50;
remainingCacheExtent = beginningViewportCacheExtent - firstSliverHeight + 50;
minExtentOfChild = remainingPaintExtent;
maxExtentOfChild = remainingPaintExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: minExtentOfChild,
cacheExtent: remainingCacheExtent,
visible: true,
);
// Overscroll so only RenderSliverFillRemainingWithScrollable is visible.
root.offset = ViewportOffset.fixed(firstSliverHeight);
pumpFrame();
remainingPaintExtent = viewportHeight;
remainingCacheExtent = beginningViewportCacheExtent;
minExtentOfChild = remainingPaintExtent;
maxExtentOfChild = remainingPaintExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: minExtentOfChild,
cacheExtent: viewportHeight,
visible: true,
);
});
test('when scrolled into view', () {
// Viewport is 800x600
const double viewportHeight = 600;
const double viewportWidth = 800;
const double cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = beginningViewportCacheExtent;
const double sliverFillRemainingChildHeight = 100.0;
final List<RenderSliver> slivers = <RenderSliver>[
RenderSliverToBoxAdapter(
child: RenderSizedBox(const Size(400.0, firstSliverHeight)),
),
RenderSliverFillRemainingWithScrollable(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
)
];
final RenderViewport root = RenderViewport(
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
cacheExtent: cacheExtent,
children: slivers,
);
layout(root);
final RenderSliver firstVisibleSliver = slivers[0];
expectSliverConstraints(
sliver: firstVisibleSliver,
cacheOrigin: 0.0,
remainingPaintExtent: viewportHeight,
remainingCacheExtent: beginningViewportCacheExtent,
scrollOffset: 0.0,
);
expectSliverGeometry(
sliver: firstVisibleSliver,
paintExtent: viewportHeight,
cacheExtent: firstSliverHeight,
visible: true,
);
// With RenderSliverFillRemainingWithScrollable:
// * The child has a minExtent of the remainingPaintExtent.
// * If not within the viewport but within the cacheExtent, the child has
// a maxExtent of the sliver's cacheExtent. Otherwise, the child has a
// maxExtent of the remainingPaintExtent
// * The sliver has a paintExtent of the child's minExtent or the
// remainingPaintExtent - whichever is smaller.
// * The sliver has a cacheExtent of either the viewportMainAxisExtent or
// the remainingCacheExtent - whichever is smaller.
final RenderSliverSingleBoxAdapter sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
double remainingPaintExtent = 0;
double remainingCacheExtent = 0;
double minExtentOfChild = remainingPaintExtent;
double maxExtentOfChild = remainingPaintExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: minExtentOfChild,
cacheExtent: remainingCacheExtent,
visible: false,
);
// Scroll so RenderSliverFillRemainingWithScrollable is not within
// viewport, but is within remainingCacheExtent.
root.offset = ViewportOffset.fixed(cacheExtent);
pumpFrame();
remainingPaintExtent = 0;
remainingCacheExtent = cacheExtent;
minExtentOfChild = remainingPaintExtent;
// When RenderSliverFillRemainingWithScrollable is completely outside the
// viewport, but is within the remainingCacheExtent, the child has a
// maxExtent of the slivers's cacheExtent.
maxExtentOfChild = remainingCacheExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: minExtentOfChild,
cacheExtent: remainingCacheExtent,
visible: false,
);
// Scroll so RenderSliverFillRemainingWithScrollable is partially within
// viewport.
root.offset = ViewportOffset.fixed(cacheExtent + 50);
pumpFrame();
remainingPaintExtent = 50;
remainingCacheExtent = cacheExtent + 50;
minExtentOfChild = remainingPaintExtent;
maxExtentOfChild = remainingPaintExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: minExtentOfChild,
cacheExtent: remainingCacheExtent,
visible: true,
);
// Scroll so RenderSliverFillRemainingWithScrollable takes the entire
// viewport.
root.offset = ViewportOffset.fixed(firstSliverHeight);
pumpFrame();
remainingPaintExtent = viewportHeight;
remainingCacheExtent = beginningViewportCacheExtent;
minExtentOfChild = remainingPaintExtent;
maxExtentOfChild = remainingPaintExtent;
expectSliverConstraints(
sliver: sliverFillRemaining,
cacheOrigin: 0.0,
remainingPaintExtent: remainingPaintExtent,
remainingCacheExtent: remainingCacheExtent,
scrollOffset: 0.0,
);
expectSliverChildConstraints(
sliver: sliverFillRemaining,
maxHeight: maxExtentOfChild,
minHeight: minExtentOfChild,
maxWidth: viewportWidth,
minWidth: viewportWidth,
);
expectSliverGeometry(
sliver: sliverFillRemaining,
paintExtent: minExtentOfChild,
cacheExtent: viewportHeight,
visible: true,
);
});
});
}
void expectSliverConstraints({
required RenderSliver sliver,
required double cacheOrigin,
required double remainingPaintExtent,
required double remainingCacheExtent,
required double scrollOffset,
}) {
expect(sliver.constraints.cacheOrigin, cacheOrigin, reason: 'cacheOrigin');
expect(sliver.constraints.remainingPaintExtent, remainingPaintExtent, reason: 'remainingPaintExtent');
expect(sliver.constraints.remainingCacheExtent, remainingCacheExtent, reason: 'remainingCacheExtent');
expect(sliver.constraints.scrollOffset, scrollOffset, reason: 'scrollOffset');
}
void expectSliverGeometry({
required RenderSliver sliver,
required double paintExtent,
required double cacheExtent,
required bool visible,
}) {
expect(sliver.geometry!.paintExtent, paintExtent, reason: 'paintExtent');
expect(sliver.geometry!.cacheExtent, cacheExtent, reason: 'cacheExtent');
expect(sliver.geometry!.visible, visible, reason: 'visible');
}
void expectSliverChildConstraints({
required RenderSliverSingleBoxAdapter sliver,
required double maxWidth,
required double maxHeight,
required double minWidth,
required double minHeight,
}) {
expect(sliver.child!.constraints.maxWidth, maxWidth, reason: 'maxWidth');
expect(sliver.child!.constraints.maxHeight, maxHeight, reason: 'maxHeight');
expect(sliver.child!.constraints.minWidth, minWidth, reason: 'minWidth');
expect(sliver.child!.constraints.minHeight, minHeight, reason: 'minHeight');
}
class TestRenderSliverBoxChildManager extends RenderSliverBoxChildManager {
TestRenderSliverBoxChildManager({
required this.children,
});
RenderSliverMultiBoxAdaptor? _renderObject;
List<RenderBox> children;
RenderSliverList createRenderSliverList() {
assert(_renderObject == null);
_renderObject = RenderSliverList(childManager: this);
return _renderObject! as RenderSliverList;
}
RenderSliverFixedExtentList createRenderSliverFixedExtentList() {
assert(_renderObject == null);
_renderObject = RenderSliverFixedExtentList(
childManager: this,
itemExtent: 100.0,
);
return _renderObject! as RenderSliverFixedExtentList;
}
RenderSliverGrid createRenderSliverGrid() {
assert(_renderObject == null);
_renderObject = RenderSliverGrid(
childManager: this,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 4.0,
),
);
return _renderObject! as RenderSliverGrid;
}
int? _currentlyUpdatingChildIndex;
@override
void createChild(int index, { required RenderBox? after }) {
if (index < 0 || index >= children.length) {
return;
}
try {
_currentlyUpdatingChildIndex = index;
_renderObject!.insert(children[index], after: after);
} finally {
_currentlyUpdatingChildIndex = null;
}
}
@override
void removeChild(RenderBox child) {
_renderObject!.remove(child);
}
@override
double estimateMaxScrollOffset(
SliverConstraints constraints, {
int? firstIndex,
int? lastIndex,
double? leadingScrollOffset,
double? trailingScrollOffset,
}) {
assert(lastIndex! >= firstIndex!);
return children.length * (trailingScrollOffset! - leadingScrollOffset!) / (lastIndex! - firstIndex! + 1);
}
@override
int get childCount => children.length;
@override
void didAdoptChild(RenderBox child) {
assert(_currentlyUpdatingChildIndex != null);
final SliverMultiBoxAdaptorParentData childParentData = child.parentData! as SliverMultiBoxAdaptorParentData;
childParentData.index = _currentlyUpdatingChildIndex;
}
@override
void setDidUnderflow(bool value) { }
}