| // 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) { } |
| } |