flutter_flutter/packages/flutter/test/rendering/sliver_cache_test.dart
Kate Lovett 9d96df2364
Modernize framework lints (#179089)
WIP

Commits separated as follows:
- Update lints in analysis_options files
- Run `dart fix --apply`
- Clean up leftover analysis issues 
- Run `dart format .` in the right places.

Local analysis and testing passes. Checking CI now.

Part of https://github.com/flutter/flutter/issues/178827
- Adoption of flutter_lints in examples/api coming in a separate change
(cc @loic-sharma)

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 01:10:39 +00:00

1800 lines
59 KiB
Dart

// 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 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 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 children = List<RenderBox>.generate(30, (int index) {
return RenderSizedBox(const Size(400.0, 100.0));
});
final childManager = TestRenderSliverBoxChildManager(children: children);
RenderSliverFixedExtentList inner;
final 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 children = List<RenderBox>.generate(30, (int index) {
return RenderSizedBox(const Size(400.0, 100.0));
});
final childManager = TestRenderSliverBoxChildManager(children: children);
RenderSliverList inner;
final 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 children = List<RenderBox>.generate(60, (int index) {
return RenderSizedBox(const Size(400.0, 100.0));
});
final childManager = TestRenderSliverBoxChildManager(children: children);
RenderSliverGrid inner;
final 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 adapters = <RenderSliverToBoxAdapter>[];
final paddings = List<RenderSliverPadding>.generate(30, (int index) {
RenderSliverToBoxAdapter adapter;
final 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 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 cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = 400;
const sliverFillRemainingChildHeight = 100.0;
final slivers = <RenderSliver>[
RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(400.0, firstSliverHeight))),
RenderSliverFillRemaining(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
),
];
final 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 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 cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const firstSliverHeight = beginningViewportCacheExtent;
const sliverFillRemainingChildHeight = 100.0;
final slivers = <RenderSliver>[
RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(400.0, firstSliverHeight))),
RenderSliverFillRemaining(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
),
];
final 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 sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
const 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 cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = 400;
const sliverFillRemainingChildHeight = 100.0;
final slivers = <RenderSliver>[
RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(400.0, firstSliverHeight))),
RenderSliverFillRemainingAndOverscroll(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
),
];
final 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 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 cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const firstSliverHeight = beginningViewportCacheExtent;
const sliverFillRemainingChildHeight = 100.0;
final slivers = <RenderSliver>[
RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(400.0, firstSliverHeight))),
RenderSliverFillRemainingAndOverscroll(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
),
];
final 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 sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
const minExtentOfChild = sliverFillRemainingChildHeight;
var 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 cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const double firstSliverHeight = 400;
const sliverFillRemainingChildHeight = 100.0;
final slivers = <RenderSliver>[
RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(400.0, firstSliverHeight))),
RenderSliverFillRemainingWithScrollable(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
),
];
final 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 sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
double remainingPaintExtent = viewportHeight - firstSliverHeight;
double remainingCacheExtent = beginningViewportCacheExtent - firstSliverHeight;
var minExtentOfChild = remainingPaintExtent;
var 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 cacheExtent = 250.0;
const double beginningViewportCacheExtent = viewportHeight + cacheExtent;
const firstSliverHeight = beginningViewportCacheExtent;
const sliverFillRemainingChildHeight = 100.0;
final slivers = <RenderSliver>[
RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(400.0, firstSliverHeight))),
RenderSliverFillRemainingWithScrollable(
child: RenderSizedBox(const Size(100.0, sliverFillRemainingChildHeight)),
),
];
final 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 sliverFillRemaining = slivers[1] as RenderSliverSingleBoxAdapter;
double remainingPaintExtent = 0;
double remainingCacheExtent = 0;
var minExtentOfChild = remainingPaintExtent;
var 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 childParentData = child.parentData! as SliverMultiBoxAdaptorParentData;
childParentData.index = _currentlyUpdatingChildIndex;
}
@override
void setDidUnderflow(bool value) {}
}