slivers_test.dart 38.3 KB
Newer Older
1 2 3 4 5
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/rendering.dart';
6
import 'package:flutter/widgets.dart';
7
import 'package:flutter_test/flutter_test.dart';
8
import 'package:vector_math/vector_math_64.dart';
9 10 11 12

import 'rendering_tester.dart';

void main() {
Adam Barth's avatar
Adam Barth committed
13
  test('RenderViewport basic test - no children', () {
14
    final RenderViewport root = RenderViewport(
15
      crossAxisDirection: AxisDirection.right,
16
      offset: ViewportOffset.zero(),
Ian Hickson's avatar
Ian Hickson committed
17
    );
18
    expect(root, hasAGoodToStringDeep);
19
    expect(
20
      root.toStringDeep(minLevel: DiagnosticLevel.info),
21 22
      equalsIgnoringHashCodes(
        'RenderViewport#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
Ian Hickson's avatar
Ian Hickson committed
23 24
        '   parentData: MISSING\n'
        '   constraints: MISSING\n'
25 26
        '   size: MISSING\n'
        '   axisDirection: down\n'
27
        '   crossAxisDirection: right\n'
28 29
        '   offset: _FixedViewportOffset#00000(offset: 0.0)\n'
        '   anchor: 0.0\n'
30 31
      ),
    );
32
    layout(root);
33
    root.offset = ViewportOffset.fixed(900.0);
34
    expect(root, hasAGoodToStringDeep);
35
    expect(
36
      root.toStringDeep(minLevel: DiagnosticLevel.info),
37 38
      equalsIgnoringHashCodes(
        'RenderViewport#00000 NEEDS-LAYOUT NEEDS-PAINT\n'
39 40 41 42
        '   parentData: <none>\n'
        '   constraints: BoxConstraints(w=800.0, h=600.0)\n'
        '   size: Size(800.0, 600.0)\n'
        '   axisDirection: down\n'
43
        '   crossAxisDirection: right\n'
44 45
        '   offset: _FixedViewportOffset#00000(offset: 900.0)\n'
        '   anchor: 0.0\n',
46 47 48
      ),
    );

49 50 51
    pumpFrame();
  });

Adam Barth's avatar
Adam Barth committed
52
  test('RenderViewport basic test - down', () {
53
    RenderBox a, b, c, d, e;
54
    final RenderViewport root = RenderViewport(
55
      crossAxisDirection: AxisDirection.right,
56
      offset: ViewportOffset.zero(),
57
      children: <RenderSliver>[
58 59 60 61 62
        RenderSliverToBoxAdapter(child: a = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: b = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: c = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: d = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: e = RenderSizedBox(const Size(100.0, 400.0))),
63 64
      ],
    );
65
    expect(root, hasAGoodToStringDeep);
66 67 68 69 70
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));

71 72
    expect(root, hasAGoodToStringDeep);
    expect(
73
      root.toStringDeep(minLevel: DiagnosticLevel.info),
74 75 76 77 78
      equalsIgnoringHashCodes(
        'RenderViewport#00000 NEEDS-PAINT\n'
        ' │ parentData: <none>\n'
        ' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
        ' │ size: Size(800.0, 600.0)\n'
79
        ' │ axisDirection: down\n'
80
        ' │ crossAxisDirection: right\n'
81 82 83 84 85 86 87 88
        ' │ offset: _FixedViewportOffset#00000(offset: 0.0)\n'
        ' │ anchor: 0.0\n'
        ' │\n'
        ' ├─center child: RenderSliverToBoxAdapter#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
        ' │ │ parentData: paintOffset=Offset(0.0, 0.0) (can use size)\n'
        ' │ │ constraints: SliverConstraints(AxisDirection.down,\n'
        ' │ │   GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
        ' │ │   0.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
89
        ' │ │   crossAxisDirection: AxisDirection.right,\n'
90 91
        ' │ │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0\n'
        ' │ │   cacheOrigin: 0.0 )\n'
92
        ' │ │ geometry: SliverGeometry(scrollExtent: 400.0, paintExtent: 400.0,\n'
93
        ' │ │   maxPaintExtent: 400.0, cacheExtent: 400.0)\n'
94 95 96 97 98 99 100 101 102 103 104
        ' │ │\n'
        ' │ └─child: RenderSizedBox#00000 NEEDS-PAINT\n'
        ' │     parentData: paintOffset=Offset(0.0, -0.0) (can use size)\n'
        ' │     constraints: BoxConstraints(w=800.0, 0.0<=h<=Infinity)\n'
        ' │     size: Size(800.0, 400.0)\n'
        ' │\n'
        ' ├─child 1: RenderSliverToBoxAdapter#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
        ' │ │ parentData: paintOffset=Offset(0.0, 400.0) (can use size)\n'
        ' │ │ constraints: SliverConstraints(AxisDirection.down,\n'
        ' │ │   GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
        ' │ │   0.0, remainingPaintExtent: 200.0, crossAxisExtent: 800.0,\n'
105
        ' │ │   crossAxisDirection: AxisDirection.right,\n'
106 107
        ' │ │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 450.0\n'
        ' │ │   cacheOrigin: 0.0 )\n'
108
        ' │ │ geometry: SliverGeometry(scrollExtent: 400.0, paintExtent: 200.0,\n'
109 110
        ' │ │   maxPaintExtent: 400.0, hasVisualOverflow: true, cacheExtent:\n'
        ' │ │   400.0)\n'
111 112 113 114 115 116 117
        ' │ │\n'
        ' │ └─child: RenderSizedBox#00000 NEEDS-PAINT\n'
        ' │     parentData: paintOffset=Offset(0.0, -0.0) (can use size)\n'
        ' │     constraints: BoxConstraints(w=800.0, 0.0<=h<=Infinity)\n'
        ' │     size: Size(800.0, 400.0)\n'
        ' │\n'
        ' ├─child 2: RenderSliverToBoxAdapter#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
118
        ' │ │ parentData: paintOffset=Offset(0.0, 800.0) (can use size)\n'
119 120 121
        ' │ │ constraints: SliverConstraints(AxisDirection.down,\n'
        ' │ │   GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
        ' │ │   0.0, remainingPaintExtent: 0.0, crossAxisExtent: 800.0,\n'
122
        ' │ │   crossAxisDirection: AxisDirection.right,\n'
123 124
        ' │ │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 50.0\n'
        ' │ │   cacheOrigin: 0.0 )\n'
125
        ' │ │ geometry: SliverGeometry(scrollExtent: 400.0, hidden,\n'
126 127
        ' │ │   maxPaintExtent: 400.0, hasVisualOverflow: true, cacheExtent:\n'
        ' │ │   50.0)\n'
128 129 130 131 132 133 134
        ' │ │\n'
        ' │ └─child: RenderSizedBox#00000 NEEDS-PAINT\n'
        ' │     parentData: paintOffset=Offset(0.0, -0.0) (can use size)\n'
        ' │     constraints: BoxConstraints(w=800.0, 0.0<=h<=Infinity)\n'
        ' │     size: Size(800.0, 400.0)\n'
        ' │\n'
        ' ├─child 3: RenderSliverToBoxAdapter#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
135
        ' │ │ parentData: paintOffset=Offset(0.0, 1200.0) (can use size)\n'
136 137 138
        ' │ │ constraints: SliverConstraints(AxisDirection.down,\n'
        ' │ │   GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
        ' │ │   0.0, remainingPaintExtent: 0.0, crossAxisExtent: 800.0,\n'
139
        ' │ │   crossAxisDirection: AxisDirection.right,\n'
140 141
        ' │ │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 0.0\n'
        ' │ │   cacheOrigin: 0.0 )\n'
142
        ' │ │ geometry: SliverGeometry(scrollExtent: 400.0, hidden,\n'
143
        ' │ │   maxPaintExtent: 400.0, hasVisualOverflow: true)\n'
144 145 146 147 148 149 150
        ' │ │\n'
        ' │ └─child: RenderSizedBox#00000 NEEDS-PAINT\n'
        ' │     parentData: paintOffset=Offset(0.0, -0.0) (can use size)\n'
        ' │     constraints: BoxConstraints(w=800.0, 0.0<=h<=Infinity)\n'
        ' │     size: Size(800.0, 400.0)\n'
        ' │\n'
        ' └─child 4: RenderSliverToBoxAdapter#00000 relayoutBoundary=up1 NEEDS-PAINT\n'
151
        '   │ parentData: paintOffset=Offset(0.0, 1600.0) (can use size)\n'
152 153 154
        '   │ constraints: SliverConstraints(AxisDirection.down,\n'
        '   │   GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
        '   │   0.0, remainingPaintExtent: 0.0, crossAxisExtent: 800.0,\n'
155
        '   │   crossAxisDirection: AxisDirection.right,\n'
156 157
        '   │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 0.0\n'
        '   │   cacheOrigin: 0.0 )\n'
158
        '   │ geometry: SliverGeometry(scrollExtent: 400.0, hidden,\n'
159
        '   │   maxPaintExtent: 400.0, hasVisualOverflow: true)\n'
160 161 162 163
        '   │\n'
        '   └─child: RenderSizedBox#00000 NEEDS-PAINT\n'
        '       parentData: paintOffset=Offset(0.0, -0.0) (can use size)\n'
        '       constraints: BoxConstraints(w=800.0, 0.0<=h<=Infinity)\n'
164
        '       size: Size(800.0, 400.0)\n'
165 166
      ),
    );
167 168
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
169 170 171
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 800.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1200.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1600.0));
172 173 174

    expect(a.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 400.0));
    expect(b.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 800.0));
175 176 177
    expect(c.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1200.0));
    expect(d.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1600.0));
    expect(e.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 2000.0));
178

179
    root.offset = ViewportOffset.fixed(200.0);
180
    pumpFrame();
181 182 183
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
184 185
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1000.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1400.0));
186

187
    root.offset = ViewportOffset.fixed(600.0);
188
    pumpFrame();
189 190 191 192
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -600.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
193
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1000.0));
194

195
    root.offset = ViewportOffset.fixed(900.0);
196
    pumpFrame();
197 198 199 200
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -900.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -500.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -100.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 300.0));
201
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 700.0));
202

203
    final HitTestResult result = HitTestResult();
204
    root.hitTest(result, position: const Offset(130.0, 150.0));
205
    expect(result.path.first.target, equals(c));
206 207
  });

Adam Barth's avatar
Adam Barth committed
208
  test('RenderViewport basic test - up', () {
209
    RenderBox a, b, c, d, e;
210
    final RenderViewport root = RenderViewport(
211
      axisDirection: AxisDirection.up,
212
      crossAxisDirection: AxisDirection.right,
213
      offset: ViewportOffset.zero(),
214
      children: <RenderSliver>[
215 216 217 218 219
        RenderSliverToBoxAdapter(child: a = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: b = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: c = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: d = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: e = RenderSizedBox(const Size(100.0, 400.0))),
220 221 222 223 224 225 226
      ],
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));

227 228
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
229 230 231
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -600.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1000.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1400.0));
232

233
    root.offset = ViewportOffset.fixed(200.0);
234
    pumpFrame();
235 236 237
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
238 239
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -800.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1200.0));
240

241
    root.offset = ViewportOffset.fixed(600.0);
242
    pumpFrame();
243 244 245 246
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 800.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
247
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -800.0));
248

249
    root.offset = ViewportOffset.fixed(900.0);
250
    pumpFrame();
251 252 253 254
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1100.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 700.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 300.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -100.0));
255
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -500.0));
256

257
    final HitTestResult result = HitTestResult();
258
    root.hitTest(result, position: const Offset(150.0, 350.0));
259
    expect(result.path.first.target, equals(c));
260 261
  });

262
  Offset _getPaintOrigin(RenderObject render) {
263 264
    final Vector3 transformed3 = render.getTransformTo(null).perspectiveTransform(Vector3(0.0, 0.0, 0.0));
    return Offset(transformed3.x, transformed3.y);
265 266
  }

Adam Barth's avatar
Adam Barth committed
267
  test('RenderViewport basic test - right', () {
268
    RenderBox a, b, c, d, e;
269
    final RenderViewport root = RenderViewport(
270
      axisDirection: AxisDirection.right,
271
      crossAxisDirection: AxisDirection.down,
272
      offset: ViewportOffset.zero(),
273
      children: <RenderSliver>[
274 275 276 277 278
        RenderSliverToBoxAdapter(child: a = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: b = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: c = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: d = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: e = RenderSizedBox(const Size(400.0, 100.0))),
279 280 281 282 283 284 285
      ],
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));

286 287 288 289 290 291
    final RenderSliver sliverA = a.parent;
    final RenderSliver sliverB = b.parent;
    final RenderSliver sliverC = c.parent;
    final RenderSliver sliverD = d.parent;
    final RenderSliver sliverE = e.parent;

292 293 294
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(400.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
295 296
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(1200.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1600.0, 0.0));
297

298 299 300
    expect(_getPaintOrigin(sliverA), const Offset(0.0, 0.0));
    expect(_getPaintOrigin(sliverB), const Offset(400.0, 0.0));
    expect(_getPaintOrigin(sliverC), const Offset(800.0, 0.0));
301 302
    expect(_getPaintOrigin(sliverD), const Offset(1200.0, 0.0));
    expect(_getPaintOrigin(sliverE), const Offset(1600.0, 0.0));
303

304
    root.offset = ViewportOffset.fixed(200.0);
305
    pumpFrame();
306 307 308
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
309 310
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1400.0, 0.0));
311

312 313 314
    expect(_getPaintOrigin(sliverA), const Offset(000.0, 0.0));
    expect(_getPaintOrigin(sliverB), const Offset(200.0, 0.0));
    expect(_getPaintOrigin(sliverC), const Offset(600.0, 0.0));
315 316
    expect(_getPaintOrigin(sliverD), const Offset(1000.0, 0.0));
    expect(_getPaintOrigin(sliverE), const Offset(1400.0, 0.0));
317

318
    root.offset = ViewportOffset.fixed(600.0);
319
    pumpFrame();
320 321 322 323
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
324
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
325

326 327 328 329
    expect(_getPaintOrigin(sliverA), const Offset(000.0, 0.0));
    expect(_getPaintOrigin(sliverB), const Offset(000.0, 0.0));
    expect(_getPaintOrigin(sliverC), const Offset(200.0, 0.0));
    expect(_getPaintOrigin(sliverD), const Offset(600.0, 0.0));
330
    expect(_getPaintOrigin(sliverE), const Offset(1000.0, 0.0));
331

332
    root.offset = ViewportOffset.fixed(900.0);
333
    pumpFrame();
334 335 336 337 338
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(-900.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(-500.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-100.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(300.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(700.0, 0.0));
339

340 341 342 343 344 345
    expect(_getPaintOrigin(sliverA), const Offset(000.0, 0.0));
    expect(_getPaintOrigin(sliverB), const Offset(000.0, 0.0));
    expect(_getPaintOrigin(sliverC), const Offset(000.0, 0.0));
    expect(_getPaintOrigin(sliverD), const Offset(300.0, 0.0));
    expect(_getPaintOrigin(sliverE), const Offset(700.0, 0.0));

346
    final HitTestResult result = HitTestResult();
347
    root.hitTest(result, position: const Offset(150.0, 450.0));
348
    expect(result.path.first.target, equals(c));
349 350
  });

Adam Barth's avatar
Adam Barth committed
351
  test('RenderViewport basic test - left', () {
352
    RenderBox a, b, c, d, e;
353
    final RenderViewport root = RenderViewport(
354
      axisDirection: AxisDirection.left,
355
      crossAxisDirection: AxisDirection.down,
356
      offset: ViewportOffset.zero(),
357
      children: <RenderSliver>[
358 359 360 361 362
        RenderSliverToBoxAdapter(child: a = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: b = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: c = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: d = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: e = RenderSizedBox(const Size(400.0, 100.0))),
363 364 365 366 367 368 369
      ],
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));

370 371 372
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(400.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
373 374
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-800.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-1200.0, 0.0));
375

376
    root.offset = ViewportOffset.fixed(200.0);
377
    pumpFrame();
378 379 380
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
381 382
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-1000.0, 0.0));
383

384
    root.offset = ViewportOffset.fixed(600.0);
385
    pumpFrame();
386 387 388 389
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
390
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
391

392
    root.offset = ViewportOffset.fixed(900.0);
393
    pumpFrame();
394 395 396 397 398
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(1300.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(900.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(500.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(100.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-300.0, 0.0));
399

400
    final HitTestResult result = HitTestResult();
401
    root.hitTest(result, position: const Offset(550.0, 150.0));
402
    expect(result.path.first.target, equals(c));
403 404 405 406 407 408
  });

  // TODO(ianh): test anchor
  // TODO(ianh): test center
  // TODO(ianh): test semantics

409
  test('RenderShrinkWrappingViewport basic test - no children', () {
410
    final RenderShrinkWrappingViewport root = RenderShrinkWrappingViewport(
411
      crossAxisDirection: AxisDirection.right,
412
      offset: ViewportOffset.zero(),
413
    );
414
    expect(root, hasAGoodToStringDeep);
415
    layout(root);
416
    root.offset = ViewportOffset.fixed(900.0);
417 418 419 420 421
    pumpFrame();
  });

  test('RenderShrinkWrappingViewport basic test - down', () {
    RenderBox a, b, c, d, e;
422
    final RenderShrinkWrappingViewport root = RenderShrinkWrappingViewport(
423
      crossAxisDirection: AxisDirection.right,
424
      offset: ViewportOffset.zero(),
425
      children: <RenderSliver>[
426 427 428 429 430
        RenderSliverToBoxAdapter(child: a = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: b = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: c = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: d = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: e = RenderSizedBox(const Size(100.0, 400.0))),
431 432 433 434 435 436 437
      ],
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));

438 439
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
440 441 442
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 800.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1200.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1600.0));
443

444 445
    expect(a.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 400.0));
    expect(b.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 800.0));
446 447 448
    expect(c.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1200.0));
    expect(d.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 1600.0));
    expect(e.localToGlobal(const Offset(800.0, 400.0)), const Offset(800.0, 2000.0));
449

450
    root.offset = ViewportOffset.fixed(200.0);
451
    pumpFrame();
452 453 454
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
455 456
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1000.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1400.0));
457

458
    root.offset = ViewportOffset.fixed(600.0);
459
    pumpFrame();
460 461 462 463
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -600.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 600.0));
464
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1000.0));
465

466
    root.offset = ViewportOffset.fixed(900.0);
467
    pumpFrame();
468 469 470 471
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -900.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -500.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -100.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 300.0));
472
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 700.0));
473

474
    final HitTestResult result = HitTestResult();
475
    root.hitTest(result, position: const Offset(130.0, 150.0));
476 477 478 479 480
    expect(result.path.first.target, equals(c));
  });

  test('RenderShrinkWrappingViewport basic test - up', () {
    RenderBox a, b, c, d, e;
481
    final RenderShrinkWrappingViewport root = RenderShrinkWrappingViewport(
482
      axisDirection: AxisDirection.up,
483
      crossAxisDirection: AxisDirection.right,
484
      offset: ViewportOffset.zero(),
485
      children: <RenderSliver>[
486 487 488 489 490
        RenderSliverToBoxAdapter(child: a = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: b = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: c = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: d = RenderSizedBox(const Size(100.0, 400.0))),
        RenderSliverToBoxAdapter(child: e = RenderSizedBox(const Size(100.0, 400.0))),
491 492 493 494 495 496 497
      ],
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));

498 499
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 200.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -200.0));
500 501 502
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -600.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1000.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1400.0));
503

504
    root.offset = ViewportOffset.fixed(200.0);
505
    pumpFrame();
506 507 508
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
509 510
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -800.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -1200.0));
511

512
    root.offset = ViewportOffset.fixed(600.0);
513
    pumpFrame();
514 515 516 517
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 800.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 400.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -400.0));
518
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -800.0));
519

520
    root.offset = ViewportOffset.fixed(900.0);
521
    pumpFrame();
522 523 524 525
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 1100.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 700.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 300.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -100.0));
526
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, -500.0));
527

528
    final HitTestResult result = HitTestResult();
529
    root.hitTest(result, position: const Offset(150.0, 350.0));
530 531 532 533 534
    expect(result.path.first.target, equals(c));
  });

  test('RenderShrinkWrappingViewport basic test - right', () {
    RenderBox a, b, c, d, e;
535
    final RenderShrinkWrappingViewport root = RenderShrinkWrappingViewport(
536
      axisDirection: AxisDirection.right,
537
      crossAxisDirection: AxisDirection.down,
538
      offset: ViewportOffset.zero(),
539
      children: <RenderSliver>[
540 541 542 543 544
        RenderSliverToBoxAdapter(child: a = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: b = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: c = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: d = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: e = RenderSizedBox(const Size(400.0, 100.0))),
545 546 547 548 549 550 551
      ],
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));

552 553 554
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(400.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(800.0, 0.0));
555 556
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(1200.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1600.0, 0.0));
557

558
    root.offset = ViewportOffset.fixed(200.0);
559
    pumpFrame();
560 561 562
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
563 564
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1400.0, 0.0));
565

566
    root.offset = ViewportOffset.fixed(600.0);
567
    pumpFrame();
568 569 570 571
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
572
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
573

574
    root.offset = ViewportOffset.fixed(900.0);
575
    pumpFrame();
576 577 578 579 580
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(-900.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(-500.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-100.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(300.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(700.0, 0.0));
581

582
    final HitTestResult result = HitTestResult();
583
    root.hitTest(result, position: const Offset(150.0, 450.0));
584 585 586 587 588
    expect(result.path.first.target, equals(c));
  });

  test('RenderShrinkWrappingViewport basic test - left', () {
    RenderBox a, b, c, d, e;
589
    final RenderShrinkWrappingViewport root = RenderShrinkWrappingViewport(
590
      axisDirection: AxisDirection.left,
591
      crossAxisDirection: AxisDirection.down,
592
      offset: ViewportOffset.zero(),
593
      children: <RenderSliver>[
594 595 596 597 598
        RenderSliverToBoxAdapter(child: a = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: b = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: c = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: d = RenderSizedBox(const Size(400.0, 100.0))),
        RenderSliverToBoxAdapter(child: e = RenderSizedBox(const Size(400.0, 100.0))),
599 600 601 602 603 604 605
      ],
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));

606 607 608
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(400.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(0.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-400.0, 0.0));
609 610
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-800.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-1200.0, 0.0));
611

612
    root.offset = ViewportOffset.fixed(200.0);
613
    pumpFrame();
614 615 616
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
617 618
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-1000.0, 0.0));
619

620
    root.offset = ViewportOffset.fixed(600.0);
621
    pumpFrame();
622 623 624 625
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(1000.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(600.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(200.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(-200.0, 0.0));
626
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-600.0, 0.0));
627

628
    root.offset = ViewportOffset.fixed(900.0);
629
    pumpFrame();
630 631 632 633 634
    expect(a.localToGlobal(const Offset(0.0, 0.0)), const Offset(1300.0, 0.0));
    expect(b.localToGlobal(const Offset(0.0, 0.0)), const Offset(900.0, 0.0));
    expect(c.localToGlobal(const Offset(0.0, 0.0)), const Offset(500.0, 0.0));
    expect(d.localToGlobal(const Offset(0.0, 0.0)), const Offset(100.0, 0.0));
    expect(e.localToGlobal(const Offset(0.0, 0.0)), const Offset(-300.0, 0.0));
635

636
    final HitTestResult result = HitTestResult();
637
    root.hitTest(result, position: const Offset(550.0, 150.0));
638 639 640 641 642
    expect(result.path.first.target, equals(c));
  });

  test('RenderShrinkWrappingViewport shrinkwrap test - 1 child', () {
    RenderBox child;
643 644
    final RenderBox root = RenderPositionedBox(
      child: child = RenderShrinkWrappingViewport(
645
        axisDirection: AxisDirection.left,
646
        crossAxisDirection: AxisDirection.down,
647
        offset: ViewportOffset.fixed(200.0),
648
        children: <RenderSliver>[
649
          RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(400.0, 100.0))),
650 651 652 653 654 655 656 657 658 659 660 661 662
        ],
      ),
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));
    expect(child.size.width, equals(400.0));
    expect(child.size.height, equals(600.0));
  });

  test('RenderShrinkWrappingViewport shrinkwrap test - 2 children', () {
    RenderBox child;
663 664
    final RenderBox root = RenderPositionedBox(
      child: child = RenderShrinkWrappingViewport(
665
        axisDirection: AxisDirection.right,
666
        crossAxisDirection: AxisDirection.down,
667
        offset: ViewportOffset.fixed(200.0),
668
        children: <RenderSliver>[
669 670
          RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(300.0, 100.0))),
          RenderSliverToBoxAdapter(child: RenderSizedBox(const Size(150.0, 100.0))),
671 672 673 674 675 676 677 678 679 680
        ],
      ),
    );
    layout(root);

    expect(root.size.width, equals(800.0));
    expect(root.size.height, equals(600.0));
    expect(child.size.width, equals(450.0));
    expect(child.size.height, equals(600.0));
  });
681 682 683

  test('SliverGeometry toString', () {
    expect(
684 685 686
      const SliverGeometry().toString(),
      equals('SliverGeometry(scrollExtent: 0.0, hidden, maxPaintExtent: 0.0)'),
    );
687
    expect(
688 689 690 691 692 693 694
      const SliverGeometry(
        scrollExtent: 100.0,
        paintExtent: 50.0,
        layoutExtent: 20.0,
        visible: true,
      ).toString(),
      equals(
695
        'SliverGeometry(scrollExtent: 100.0, paintExtent: 50.0, layoutExtent: 20.0, maxPaintExtent: 0.0, cacheExtent: 20.0)',
696 697 698 699 700 701 702 703 704
      ),
    );
    expect(
      const SliverGeometry(
        scrollExtent: 100.0,
        paintExtent: 0.0,
        layoutExtent: 20.0,
      ).toString(),
      equals(
705
        'SliverGeometry(scrollExtent: 100.0, hidden, layoutExtent: 20.0, maxPaintExtent: 0.0, cacheExtent: 20.0)',
706 707
      ),
    );
708
  });
709 710 711 712

  test('Sliver paintBounds and semanticBounds - vertical', () {
    const double height = 150.0;

713 714
    final RenderSliver sliver = RenderSliverToBoxAdapter(
        child: RenderSizedBox(const Size(400.0, height)),
715
    );
716
    final RenderViewport root = RenderViewport(
717
      axisDirection: AxisDirection.down,
718
      crossAxisDirection: AxisDirection.right,
719
      offset: ViewportOffset.zero(),
720 721 722 723 724 725
      children: <RenderSliver>[
        sliver,
      ],
    );
    layout(root);

726
    final Rect expectedRect = Rect.fromLTWH(0.0, 0.0, root.size.width, height);
727 728 729 730 731 732 733 734

    expect(sliver.paintBounds, expectedRect);
    expect(sliver.semanticBounds, expectedRect);
  });

  test('Sliver paintBounds and semanticBounds - horizontal', () {
    const double width = 150.0;

735 736
    final RenderSliver sliver = RenderSliverToBoxAdapter(
      child: RenderSizedBox(const Size(width, 400.0)),
737
    );
738
    final RenderViewport root = RenderViewport(
739
      axisDirection: AxisDirection.right,
740
      crossAxisDirection: AxisDirection.down,
741
      offset: ViewportOffset.zero(),
742 743 744 745 746 747
      children: <RenderSliver>[
        sliver,
      ],
    );
    layout(root);

748
    final Rect expectedRect = Rect.fromLTWH(0.0, 0.0, width, root.size.height);
749 750 751 752

    expect(sliver.paintBounds, expectedRect);
    expect(sliver.semanticBounds, expectedRect);
  });
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826

  test('precedingScrollExtent is 0.0 for first Sliver in list', () {
    const double viewportWidth = 800.0;
    final RenderSliver sliver = RenderSliverToBoxAdapter(
      child: RenderSizedBox(const Size(viewportWidth, 150.0)),
    );
    final RenderViewport root = RenderViewport(
      axisDirection: AxisDirection.down,
      crossAxisDirection: AxisDirection.right,
      offset: ViewportOffset.zero(),
      children: <RenderSliver>[
        sliver,
      ],
    );
    layout(root);

    expect(sliver.constraints.precedingScrollExtent, 0.0);
  });

  test('precedingScrollExtent accumulates over multiple Slivers', () {
    const double viewportWidth = 800.0;
    final RenderSliver sliver1 = RenderSliverToBoxAdapter(
      child: RenderSizedBox(const Size(viewportWidth, 150.0)),
    );
    final RenderSliver sliver2 = RenderSliverToBoxAdapter(
      child: RenderSizedBox(const Size(viewportWidth, 150.0)),
    );
    final RenderSliver sliver3 = RenderSliverToBoxAdapter(
      child: RenderSizedBox(const Size(viewportWidth, 150.0)),
    );
    final RenderViewport root = RenderViewport(
      axisDirection: AxisDirection.down,
      crossAxisDirection: AxisDirection.right,
      offset: ViewportOffset.zero(),
      children: <RenderSliver>[
        sliver1,
        sliver2,
        sliver3,
      ],
    );
    layout(root);

    // The 3rd Sliver comes after 300.0px of preceding scroll extent by first 2 Slivers.
    expect(sliver3.constraints.precedingScrollExtent, 300.0);
  });

  test('precedingScrollExtent is not impacted by scrollOffset', () {
    const double viewportWidth = 800.0;
    final RenderSliver sliver1 = RenderSliverToBoxAdapter(
      child: RenderSizedBox(const Size(viewportWidth, 150.0)),
    );
    final RenderSliver sliver2 = RenderSliverToBoxAdapter(
      child: RenderSizedBox(const Size(viewportWidth, 150.0)),
    );
    final RenderSliver sliver3 = RenderSliverToBoxAdapter(
      child: RenderSizedBox(const Size(viewportWidth, 150.0)),
    );
    final RenderViewport root = RenderViewport(
      axisDirection: AxisDirection.down,
      crossAxisDirection: AxisDirection.right,
      offset: ViewportOffset.fixed(100.0),
      children: <RenderSliver>[
        sliver1,
        sliver2,
        sliver3,
      ],
    );
    layout(root);

    // The 3rd Sliver comes after 300.0px of preceding scroll extent by first 2 Slivers.
    // In this test a ViewportOffset is applied to simulate a scrollOffset. That
    // offset is not expected to impact the precedingScrollExtent.
    expect(sliver3.constraints.precedingScrollExtent, 300.0);
  });
827
}