slivers_padding_test.dart 23.5 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Ian Hickson's avatar
Ian Hickson committed
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/material.dart';
Ian Hickson's avatar
Ian Hickson committed
6
import 'package:flutter/rendering.dart';
7
import 'package:flutter_test/flutter_test.dart';
8
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
Ian Hickson's avatar
Ian Hickson committed
9

10 11 12 13 14 15 16 17 18 19 20 21
class _MockRenderSliver extends RenderSliver {
  @override
  void performLayout() {
    geometry = const SliverGeometry(
      paintOrigin: 10,
      paintExtent: 10,
      maxPaintExtent: 10,
    );
  }

}

22
Future<void> test(WidgetTester tester, double offset, EdgeInsetsGeometry padding, AxisDirection axisDirection, TextDirection textDirection) {
23 24
  final ViewportOffset viewportOffset = ViewportOffset.fixed(offset);
  addTearDown(viewportOffset.dispose);
25
  return tester.pumpWidget(
26
    Directionality(
27
      textDirection: textDirection,
28
      child: Viewport(
29
        offset: viewportOffset,
30 31
        axisDirection: axisDirection,
        slivers: <Widget>[
32
          const SliverToBoxAdapter(child: SizedBox(width: 400.0, height: 400.0, child: Text('before'))),
33
          SliverPadding(
34
            padding: padding,
35
            sliver: const SliverToBoxAdapter(child: SizedBox(width: 400.0, height: 400.0, child: Text('padded'))),
36
          ),
37
          const SliverToBoxAdapter(child: SizedBox(width: 400.0, height: 400.0, child: Text('after'))),
38
        ],
Ian Hickson's avatar
Ian Hickson committed
39
      ),
40
    ),
41
  );
Ian Hickson's avatar
Ian Hickson committed
42 43 44
}

void verify(WidgetTester tester, List<Rect> answerKey) {
45
  final List<Rect> testAnswers = tester.renderObjectList<RenderBox>(find.byType(SizedBox, skipOffstage: false)).map<Rect>(
Ian Hickson's avatar
Ian Hickson committed
46
    (RenderBox target) {
47 48
      final Offset topLeft = target.localToGlobal(Offset.zero);
      final Offset bottomRight = target.localToGlobal(target.size.bottomRight(Offset.zero));
49
      return Rect.fromPoints(topLeft, bottomRight);
50
    },
Ian Hickson's avatar
Ian Hickson committed
51 52 53 54 55
  ).toList();
  expect(testAnswers, equals(answerKey));
}

void main() {
56
  testWidgetsWithLeakTracking('Viewport+SliverPadding basic test (VISUAL)', (WidgetTester tester) async {
57
    const EdgeInsets padding = EdgeInsets.fromLTRB(25.0, 20.0, 15.0, 35.0);
58
    await test(tester, 0.0, padding, AxisDirection.down, TextDirection.ltr);
Adam Barth's avatar
Adam Barth committed
59
    expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
Ian Hickson's avatar
Ian Hickson committed
60
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
61 62 63
      const Rect.fromLTWH(0.0, 0.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, 420.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 855.0, 800.0, 400.0),
Ian Hickson's avatar
Ian Hickson committed
64 65
    ]);

66
    await test(tester, 200.0, padding, AxisDirection.down, TextDirection.ltr);
Ian Hickson's avatar
Ian Hickson committed
67
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
68 69 70
      const Rect.fromLTWH(0.0, -200.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, 220.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 655.0, 800.0, 400.0),
Ian Hickson's avatar
Ian Hickson committed
71 72
    ]);

73
    await test(tester, 390.0, padding, AxisDirection.down, TextDirection.ltr);
Ian Hickson's avatar
Ian Hickson committed
74
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
75 76 77
      const Rect.fromLTWH(0.0, -390.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, 30.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 465.0, 800.0, 400.0),
Ian Hickson's avatar
Ian Hickson committed
78 79
    ]);

80
    await test(tester, 490.0, padding, AxisDirection.down, TextDirection.ltr);
Ian Hickson's avatar
Ian Hickson committed
81
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
82 83 84
      const Rect.fromLTWH(0.0, -490.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, -70.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 365.0, 800.0, 400.0),
Ian Hickson's avatar
Ian Hickson committed
85 86
    ]);

87
    await test(tester, 10000.0, padding, AxisDirection.down, TextDirection.ltr);
Ian Hickson's avatar
Ian Hickson committed
88
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
89 90 91
      const Rect.fromLTWH(0.0, -10000.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, -9580.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, -9145.0, 800.0, 400.0),
Ian Hickson's avatar
Ian Hickson committed
92 93 94
    ]);
  });

95
  testWidgetsWithLeakTracking('Viewport+SliverPadding basic test (LTR)', (WidgetTester tester) async {
96
    const EdgeInsetsDirectional padding = EdgeInsetsDirectional.fromSTEB(25.0, 20.0, 15.0, 35.0);
97
    await test(tester, 0.0, padding, AxisDirection.down, TextDirection.ltr);
98 99
    expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
100 101 102
      const Rect.fromLTWH(0.0, 0.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, 420.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 855.0, 800.0, 400.0),
103 104
    ]);

105
    await test(tester, 200.0, padding, AxisDirection.down, TextDirection.ltr);
106
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
107 108 109
      const Rect.fromLTWH(0.0, -200.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, 220.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 655.0, 800.0, 400.0),
110 111
    ]);

112
    await test(tester, 390.0, padding, AxisDirection.down, TextDirection.ltr);
113
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
114 115 116
      const Rect.fromLTWH(0.0, -390.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, 30.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 465.0, 800.0, 400.0),
117 118
    ]);

119
    await test(tester, 490.0, padding, AxisDirection.down, TextDirection.ltr);
120
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
121 122 123
      const Rect.fromLTWH(0.0, -490.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, -70.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 365.0, 800.0, 400.0),
124 125
    ]);

126
    await test(tester, 10000.0, padding, AxisDirection.down, TextDirection.ltr);
127
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
128 129 130
      const Rect.fromLTWH(0.0, -10000.0, 800.0, 400.0),
      const Rect.fromLTWH(25.0, -9580.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, -9145.0, 800.0, 400.0),
131 132 133
    ]);
  });

134
  testWidgetsWithLeakTracking('Viewport+SliverPadding basic test (RTL)', (WidgetTester tester) async {
135
    const EdgeInsetsDirectional padding = EdgeInsetsDirectional.fromSTEB(25.0, 20.0, 15.0, 35.0);
136
    await test(tester, 0.0, padding, AxisDirection.down, TextDirection.rtl);
137 138
    expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
139 140 141
      const Rect.fromLTWH(0.0, 0.0, 800.0, 400.0),
      const Rect.fromLTWH(15.0, 420.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 855.0, 800.0, 400.0),
142 143
    ]);

144
    await test(tester, 200.0, padding, AxisDirection.down, TextDirection.rtl);
145
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
146 147 148
      const Rect.fromLTWH(0.0, -200.0, 800.0, 400.0),
      const Rect.fromLTWH(15.0, 220.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 655.0, 800.0, 400.0),
149 150
    ]);

151
    await test(tester, 390.0, padding, AxisDirection.down, TextDirection.rtl);
152
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
153 154 155
      const Rect.fromLTWH(0.0, -390.0, 800.0, 400.0),
      const Rect.fromLTWH(15.0, 30.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 465.0, 800.0, 400.0),
156 157
    ]);

158
    await test(tester, 490.0, padding, AxisDirection.down, TextDirection.rtl);
159
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
160 161 162
      const Rect.fromLTWH(0.0, -490.0, 800.0, 400.0),
      const Rect.fromLTWH(15.0, -70.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, 365.0, 800.0, 400.0),
163 164
    ]);

165
    await test(tester, 10000.0, padding, AxisDirection.down, TextDirection.rtl);
166
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
167 168 169
      const Rect.fromLTWH(0.0, -10000.0, 800.0, 400.0),
      const Rect.fromLTWH(15.0, -9580.0, 760.0, 400.0),
      const Rect.fromLTWH(0.0, -9145.0, 800.0, 400.0),
170 171 172
    ]);
  });

173
  testWidgetsWithLeakTracking('Viewport+SliverPadding hit testing', (WidgetTester tester) async {
174
    const EdgeInsets padding = EdgeInsets.all(30.0);
175
    await test(tester, 350.0, padding, AxisDirection.down, TextDirection.ltr);
Adam Barth's avatar
Adam Barth committed
176
    expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
Ian Hickson's avatar
Ian Hickson committed
177
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
178 179 180
      const Rect.fromLTWH(0.0, -350.0, 800.0, 400.0),
      const Rect.fromLTWH(30.0, 80.0, 740.0, 400.0),
      const Rect.fromLTWH(0.0, 510.0, 800.0, 400.0),
Ian Hickson's avatar
Ian Hickson committed
181 182
    ]);
    HitTestResult result;
183
    result = tester.hitTestOnBinding(const Offset(10.0, 10.0));
184
    expectIsTextSpan(result.path.first.target, 'before');
185
    result = tester.hitTestOnBinding(const Offset(10.0, 60.0));
Dan Field's avatar
Dan Field committed
186
    expect(result.path.first.target, isA<RenderView>());
187
    result = tester.hitTestOnBinding(const Offset(100.0, 100.0));
188
    expectIsTextSpan(result.path.first.target, 'padded');
189
    result = tester.hitTestOnBinding(const Offset(100.0, 490.0));
Dan Field's avatar
Dan Field committed
190
    expect(result.path.first.target, isA<RenderView>());
191
    result = tester.hitTestOnBinding(const Offset(10.0, 520.0));
192
    expectIsTextSpan(result.path.first.target, 'after');
Ian Hickson's avatar
Ian Hickson committed
193 194
  });

195
  testWidgetsWithLeakTracking('Viewport+SliverPadding hit testing up', (WidgetTester tester) async {
196
    const EdgeInsets padding = EdgeInsets.all(30.0);
197
    await test(tester, 350.0, padding, AxisDirection.up, TextDirection.ltr);
Adam Barth's avatar
Adam Barth committed
198
    expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
Ian Hickson's avatar
Ian Hickson committed
199
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
200 201 202
      const Rect.fromLTWH(0.0, 600.0+350.0-400.0, 800.0, 400.0),
      const Rect.fromLTWH(30.0, 600.0-80.0-400.0, 740.0, 400.0),
      const Rect.fromLTWH(0.0, 600.0-510.0-400.0, 800.0, 400.0),
Ian Hickson's avatar
Ian Hickson committed
203 204
    ]);
    HitTestResult result;
205
    result = tester.hitTestOnBinding(const Offset(10.0, 600.0-10.0));
206
    expectIsTextSpan(result.path.first.target, 'before');
207
    result = tester.hitTestOnBinding(const Offset(10.0, 600.0-60.0));
Dan Field's avatar
Dan Field committed
208
    expect(result.path.first.target, isA<RenderView>());
209
    result = tester.hitTestOnBinding(const Offset(100.0, 600.0-100.0));
210
    expectIsTextSpan(result.path.first.target, 'padded');
211
    result = tester.hitTestOnBinding(const Offset(100.0, 600.0-490.0));
Dan Field's avatar
Dan Field committed
212
    expect(result.path.first.target, isA<RenderView>());
213
    result = tester.hitTestOnBinding(const Offset(10.0, 600.0-520.0));
214
    expectIsTextSpan(result.path.first.target, 'after');
Ian Hickson's avatar
Ian Hickson committed
215 216
  });

217
  testWidgetsWithLeakTracking('Viewport+SliverPadding hit testing left', (WidgetTester tester) async {
218
    const EdgeInsets padding = EdgeInsets.all(30.0);
219
    await test(tester, 350.0, padding, AxisDirection.left, TextDirection.ltr);
Adam Barth's avatar
Adam Barth committed
220
    expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
Ian Hickson's avatar
Ian Hickson committed
221
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
222 223 224
      const Rect.fromLTWH(800.0+350.0-400.0, 0.0, 400.0, 600.0),
      const Rect.fromLTWH(800.0-80.0-400.0, 30.0, 400.0, 540.0),
      const Rect.fromLTWH(800.0-510.0-400.0, 0.0, 400.0, 600.0),
Ian Hickson's avatar
Ian Hickson committed
225 226
    ]);
    HitTestResult result;
227
    result = tester.hitTestOnBinding(const Offset(800.0-10.0, 10.0));
228
    expectIsTextSpan(result.path.first.target, 'before');
229
    result = tester.hitTestOnBinding(const Offset(800.0-60.0, 10.0));
Dan Field's avatar
Dan Field committed
230
    expect(result.path.first.target, isA<RenderView>());
231
    result = tester.hitTestOnBinding(const Offset(800.0-100.0, 100.0));
232
    expectIsTextSpan(result.path.first.target, 'padded');
233
    result = tester.hitTestOnBinding(const Offset(800.0-490.0, 100.0));
Dan Field's avatar
Dan Field committed
234
    expect(result.path.first.target, isA<RenderView>());
235
    result = tester.hitTestOnBinding(const Offset(800.0-520.0, 10.0));
236
    expectIsTextSpan(result.path.first.target, 'after');
Ian Hickson's avatar
Ian Hickson committed
237 238
  });

239
  testWidgetsWithLeakTracking('Viewport+SliverPadding hit testing right', (WidgetTester tester) async {
240
    const EdgeInsets padding = EdgeInsets.all(30.0);
241
    await test(tester, 350.0, padding, AxisDirection.right, TextDirection.ltr);
Adam Barth's avatar
Adam Barth committed
242
    expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
Ian Hickson's avatar
Ian Hickson committed
243
    verify(tester, <Rect>[
Dan Field's avatar
Dan Field committed
244 245 246
      const Rect.fromLTWH(-350.0, 0.0, 400.0, 600.0),
      const Rect.fromLTWH(80.0, 30.0, 400.0, 540.0),
      const Rect.fromLTWH(510.0, 0.0, 400.0, 600.0),
Ian Hickson's avatar
Ian Hickson committed
247 248
    ]);
    HitTestResult result;
249
    result = tester.hitTestOnBinding(const Offset(10.0, 10.0));
250
    expectIsTextSpan(result.path.first.target, 'before');
251
    result = tester.hitTestOnBinding(const Offset(60.0, 10.0));
Dan Field's avatar
Dan Field committed
252
    expect(result.path.first.target, isA<RenderView>());
253
    result = tester.hitTestOnBinding(const Offset(100.0, 100.0));
254
    expectIsTextSpan(result.path.first.target, 'padded');
255
    result = tester.hitTestOnBinding(const Offset(490.0, 100.0));
Dan Field's avatar
Dan Field committed
256
    expect(result.path.first.target, isA<RenderView>());
257
    result = tester.hitTestOnBinding(const Offset(520.0, 10.0));
258
    expectIsTextSpan(result.path.first.target, 'after');
Ian Hickson's avatar
Ian Hickson committed
259 260
  });

261 262 263 264
  testWidgetsWithLeakTracking('Viewport+SliverPadding no child', (WidgetTester tester) async {
    final ViewportOffset offset = ViewportOffset.fixed(0.0);
    addTearDown(offset.dispose);

265
    await tester.pumpWidget(
266
      Directionality(
267
        textDirection: TextDirection.ltr,
268
        child: Viewport(
269
          offset: offset,
270
          slivers: const <Widget>[
271 272
            SliverPadding(padding: EdgeInsets.all(100.0)),
            SliverToBoxAdapter(child: SizedBox(width: 400.0, height: 400.0, child: Text('x'))),
273 274 275 276
          ],
        ),
      ),
    );
277
    expect(tester.renderObject<RenderBox>(find.text('x')).localToGlobal(Offset.zero), const Offset(0.0, 200.0));
Ian Hickson's avatar
Ian Hickson committed
278 279
  });

280
  testWidgetsWithLeakTracking('SliverPadding with no child reports correct geometry as scroll offset changes', (WidgetTester tester) async {
281 282
    // Regression test for https://github.com/flutter/flutter/issues/64506
    final ScrollController controller = ScrollController();
283
    addTearDown(controller.dispose);
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: CustomScrollView(
          controller: controller,
          slivers: const <Widget>[
            SliverPadding(padding: EdgeInsets.all(100.0)),
            SliverToBoxAdapter(child: SizedBox(width: 400.0, height: 400.0, child: Text('x'))),
          ],
        ),
      ),
    );
    expect(tester.renderObject<RenderBox>(find.text('x')).localToGlobal(Offset.zero), const Offset(0.0, 200.0));
    expect(
      tester.renderObject<RenderSliverPadding>(find.byType(SliverPadding)).geometry!.paintExtent,
      200.0,
    );
    controller.jumpTo(50.0);
    await tester.pump();
    expect(
      tester.renderObject<RenderSliverPadding>(find.byType(SliverPadding)).geometry!.paintExtent,
      150.0,
    );
  });

309 310 311 312
  testWidgetsWithLeakTracking('Viewport+SliverPadding changing padding', (WidgetTester tester) async {
    final ViewportOffset offset1 = ViewportOffset.fixed(0.0);
    addTearDown(offset1.dispose);

313
    await tester.pumpWidget(
314
      Directionality(
315
        textDirection: TextDirection.ltr,
316
        child: Viewport(
317
          axisDirection: AxisDirection.left,
318
          offset: offset1,
319
          slivers: const <Widget>[
320 321
            SliverPadding(padding: EdgeInsets.fromLTRB(90.0, 1.0, 110.0, 2.0)),
            SliverToBoxAdapter(child: SizedBox(width: 201.0, child: Text('x'))),
322 323 324 325
          ],
        ),
      ),
    );
326

327
    expect(tester.renderObject<RenderBox>(find.text('x')).localToGlobal(Offset.zero), const Offset(399.0, 0.0));
328 329 330 331

    final ViewportOffset offset2 = ViewportOffset.fixed(0.0);
    addTearDown(offset2.dispose);

332
    await tester.pumpWidget(
333
      Directionality(
334
        textDirection: TextDirection.ltr,
335
        child: Viewport(
336
          axisDirection: AxisDirection.left,
337
          offset: offset2,
338
          slivers: const <Widget>[
339 340
            SliverPadding(padding: EdgeInsets.fromLTRB(110.0, 1.0, 80.0, 2.0)),
            SliverToBoxAdapter(child: SizedBox(width: 201.0, child: Text('x'))),
341 342 343 344
          ],
        ),
      ),
    );
345

346
    expect(tester.renderObject<RenderBox>(find.text('x')).localToGlobal(Offset.zero), const Offset(409.0, 0.0));
Ian Hickson's avatar
Ian Hickson committed
347 348
  });

349 350 351 352
  testWidgetsWithLeakTracking('Viewport+SliverPadding changing direction', (WidgetTester tester) async {
    final ViewportOffset offset1 = ViewportOffset.fixed(0.0);
    addTearDown(offset1.dispose);

353
    await tester.pumpWidget(
354
      Directionality(
355
        textDirection: TextDirection.ltr,
356
        child: Viewport(
357
          axisDirection: AxisDirection.up,
358
          offset: offset1,
359
          slivers: const <Widget>[
360
            SliverPadding(padding: EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)),
361 362 363 364
          ],
        ),
      ),
    );
365

366
    expect(tester.renderObject<RenderSliverPadding>(find.byType(SliverPadding)).afterPadding, 2.0);
367 368 369 370

    final ViewportOffset offset2 = ViewportOffset.fixed(0.0);
    addTearDown(offset2.dispose);

371
    await tester.pumpWidget(
372
      Directionality(
373
        textDirection: TextDirection.ltr,
374
        child: Viewport(
375
          offset: offset2,
376
          slivers: const <Widget>[
377
            SliverPadding(padding: EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)),
378 379 380 381
          ],
        ),
      ),
    );
382

383
    expect(tester.renderObject<RenderSliverPadding>(find.byType(SliverPadding)).afterPadding, 8.0);
384 385 386 387

    final ViewportOffset offset3 = ViewportOffset.fixed(0.0);
    addTearDown(offset3.dispose);

388
    await tester.pumpWidget(
389
      Directionality(
390
        textDirection: TextDirection.ltr,
391
        child: Viewport(
392
          axisDirection: AxisDirection.right,
393
          offset: offset3,
394
          slivers: const <Widget>[
395
            SliverPadding(padding: EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)),
396 397 398 399
          ],
        ),
      ),
    );
400

401
    expect(tester.renderObject<RenderSliverPadding>(find.byType(SliverPadding)).afterPadding, 4.0);
402 403 404 405

    final ViewportOffset offset4 = ViewportOffset.fixed(0.0);
    addTearDown(offset4.dispose);

406
    await tester.pumpWidget(
407
      Directionality(
408
        textDirection: TextDirection.ltr,
409
        child: Viewport(
410
          axisDirection: AxisDirection.left,
411
          offset: offset4,
412
          slivers: const <Widget>[
413
            SliverPadding(padding: EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)),
414 415 416 417
          ],
        ),
      ),
    );
418

419
    expect(tester.renderObject<RenderSliverPadding>(find.byType(SliverPadding)).afterPadding, 1.0);
420 421 422 423

    final ViewportOffset offset5 = ViewportOffset.fixed(99999.9);
    addTearDown(offset5.dispose);

424
    await tester.pumpWidget(
425
      Directionality(
426
        textDirection: TextDirection.ltr,
427
        child: Viewport(
428
          axisDirection: AxisDirection.left,
429
          offset: offset5,
430
          slivers: const <Widget>[
431
            SliverPadding(padding: EdgeInsets.fromLTRB(1.0, 2.0, 4.0, 8.0)),
432 433 434 435
          ],
        ),
      ),
    );
436

437
    expect(tester.renderObject<RenderSliverPadding>(find.byType(SliverPadding, skipOffstage: false)).afterPadding, 1.0);
Ian Hickson's avatar
Ian Hickson committed
438
  });
439

440
  testWidgetsWithLeakTracking('SliverPadding propagates geometry offset corrections', (WidgetTester tester) async {
441
    Widget listBuilder(IndexedWidgetBuilder sliverChildBuilder) {
442
      return Directionality(
443
        textDirection: TextDirection.ltr,
444
        child: CustomScrollView(
445
          cacheExtent: 0.0,
446
          slivers: <Widget>[
447
            SliverPadding(
448
              padding: EdgeInsets.zero,
449 450
              sliver: SliverList(
                delegate: SliverChildBuilderDelegate(
451 452 453 454 455 456 457 458 459 460 461 462 463
                  sliverChildBuilder,
                  childCount: 10,
                ),
              ),
            ),
          ],
        ),
      );
    }

    await tester.pumpWidget(
      listBuilder(
        (BuildContext context, int index) {
464
          return SizedBox(
465
            height: 200.0,
466 467
            child: Center(
              child: Text(index.toString()),
468 469 470 471 472 473 474 475 476 477
            ),
          );
        },
      ),
    );

    await tester.drag(find.text('2'), const Offset(0.0, -300.0));
    await tester.pump();

    expect(
478
      tester.getRect(find.widgetWithText(SizedBox, '2')),
Dan Field's avatar
Dan Field committed
479
      const Rect.fromLTRB(0.0, 100.0, 800.0, 300.0),
480 481 482 483 484 485
    );

    // Now item 0 is 400.0px and going back will underflow.
    await tester.pumpWidget(
      listBuilder(
        (BuildContext context, int index) {
486
          return SizedBox(
487
            height: index == 0 ? 400.0 : 200.0,
488 489
            child: Center(
              child: Text(index.toString()),
490 491 492 493 494 495 496 497 498 499 500
            ),
          );
        },
      ),
    );

    await tester.drag(find.text('2'), const Offset(0.0, 300.0));
    // On this one frame, the scroll correction must properly propagate.
    await tester.pump();

    expect(
501
      tester.getRect(find.widgetWithText(SizedBox, '0')),
Dan Field's avatar
Dan Field committed
502
      const Rect.fromLTRB(0.0, -200.0, 800.0, 200.0),
503 504
    );
  });
505

506
  testWidgetsWithLeakTracking('SliverPadding includes preceding padding in the precedingScrollExtent provided to child', (WidgetTester tester) async {
507 508 509 510 511 512 513 514 515 516 517 518 519 520
    // Regression test for https://github.com/flutter/flutter/issues/49195
    final UniqueKey key = UniqueKey();
    await tester.pumpWidget(Directionality(
      textDirection: TextDirection.ltr,
      child: CustomScrollView(
        slivers: <Widget>[
          SliverPadding(
            padding: const EdgeInsets.only(top: 30),
            sliver: SliverFillRemaining(
              hasScrollBody: false,
              child: Container(
                key: key,
                color: Colors.red,
              ),
521
            ),
522
          ),
523
        ],
524 525 526 527 528 529 530 531 532 533 534 535 536 537
      ),
    ));
    await tester.pump();

    // The value of 570 is expected since SliverFillRemaining will fill all of
    // the space available to it. In this test, the extent of the viewport is
    // 600 pixels. If the SliverPadding widget provides the right constraints
    // to SliverFillRemaining, with 30 pixels preceding it, it should only have
    // a height of 570.
    expect(
      tester.renderObject<RenderBox>(find.byKey(key)).size.height,
      equals(570),
    );
  });
538

539
  testWidgetsWithLeakTracking("SliverPadding consumes only its padding from the overlap of its parent's constraints", (WidgetTester tester) async {
540
    final _MockRenderSliver mock = _MockRenderSliver();
541
    addTearDown(mock.dispose);
542 543 544
    final RenderSliverPadding renderObject = RenderSliverPadding(
      padding: const EdgeInsets.only(top: 20),
    );
545
    addTearDown(renderObject.dispose);
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
    renderObject.child = mock;
    renderObject.layout(const SliverConstraints(
        viewportMainAxisExtent: 100.0,
        overlap: 100.0,
        cacheOrigin: 0.0,
        scrollOffset: 0.0,
        axisDirection: AxisDirection.down,
        growthDirection: GrowthDirection.forward,
        crossAxisExtent: 100.0,
        crossAxisDirection: AxisDirection.right,
        userScrollDirection: ScrollDirection.idle,
        remainingPaintExtent: 100.0,
        remainingCacheExtent: 100.0,
        precedingScrollExtent: 0.0,
      ),
      parentUsesSize: true,
    );
    expect(mock.constraints.overlap, 80.0);
  });

566
  testWidgetsWithLeakTracking("SliverPadding passes the overlap to the child if it's negative", (WidgetTester tester) async {
567
    final _MockRenderSliver mock = _MockRenderSliver();
568
    addTearDown(mock.dispose);
569 570 571
    final RenderSliverPadding renderObject = RenderSliverPadding(
      padding: const EdgeInsets.only(top: 20),
    );
572
    addTearDown(renderObject.dispose);
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
    renderObject.child = mock;
    renderObject.layout(const SliverConstraints(
        viewportMainAxisExtent: 100.0,
        overlap: -100.0,
        cacheOrigin: 0.0,
        scrollOffset: 0.0,
        axisDirection: AxisDirection.down,
        growthDirection: GrowthDirection.forward,
        crossAxisExtent: 100.0,
        crossAxisDirection: AxisDirection.right,
        userScrollDirection: ScrollDirection.idle,
        remainingPaintExtent: 100.0,
        remainingCacheExtent: 100.0,
        precedingScrollExtent: 0.0,
      ),
      parentUsesSize: true,
    );
    expect(mock.constraints.overlap, -100.0);
  });

593
  testWidgetsWithLeakTracking('SliverPadding passes the paintOrigin of the child on', (WidgetTester tester) async {
594
    final _MockRenderSliver mock = _MockRenderSliver();
595
    addTearDown(mock.dispose);
596 597 598
    final RenderSliverPadding renderObject = RenderSliverPadding(
      padding: const EdgeInsets.only(top: 20),
    );
599
    addTearDown(renderObject.dispose);
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    renderObject.child = mock;
    renderObject.layout(const SliverConstraints(
        viewportMainAxisExtent: 100.0,
        overlap: 100.0,
        cacheOrigin: 0.0,
        scrollOffset: 0.0,
        axisDirection: AxisDirection.down,
        growthDirection: GrowthDirection.forward,
        crossAxisExtent: 100.0,
        crossAxisDirection: AxisDirection.right,
        userScrollDirection: ScrollDirection.idle,
        remainingPaintExtent: 100.0,
        remainingCacheExtent: 100.0,
        precedingScrollExtent: 0.0,
      ),
      parentUsesSize: true,
    );
617
    expect(renderObject.geometry!.paintOrigin, 10.0);
618
  });
Ian Hickson's avatar
Ian Hickson committed
619
}
620 621 622 623 624

void expectIsTextSpan(Object target, String text) {
  expect(target, isA<TextSpan>());
  expect((target as TextSpan).text, text);
}