keep_alive_test.dart 28.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2017 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 'dart:io' show Platform;

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';

class Leaf extends StatefulWidget {
11
  const Leaf({ Key key, this.child }) : super(key: key);
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
  final Widget child;
  @override
  _LeafState createState() => new _LeafState();
}

class _LeafState extends State<Leaf> {
  bool _keepAlive = false;

  void setKeepAlive(bool value) {
    setState(() { _keepAlive = value; });
  }

  @override
  Widget build(BuildContext context) {
    return new KeepAlive(
      keepAlive: _keepAlive,
      child: widget.child,
    );
  }
}

List<Widget> generateList(Widget child) {
  return new List<Widget>.generate(
    100,
    (int index) => new Leaf(
      key: new GlobalObjectKey<_LeafState>(index),
      child: child,
    ),
    growable: false,
  );
}

void main() {
  testWidgets('KeepAlive with ListView with itemExtent', (WidgetTester tester) async {
46 47 48 49
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new ListView(
50
          cacheExtent: 0.0,
51 52 53 54 55 56 57
          addAutomaticKeepAlives: false,
          addRepaintBoundaries: false,
          itemExtent: 12.3, // about 50 widgets visible
          children: generateList(const Placeholder()),
        ),
      ),
    );
58 59
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
60 61 62 63
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
64 65
    await tester.drag(find.byType(ListView), const Offset(0.0, -300.0)); // about 25 widgets' worth
    await tester.pump();
66
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
67 68 69 70
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
71
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
72 73 74 75 76
    const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
    await tester.drag(find.byType(ListView), const Offset(0.0, 300.0)); // back to top
    await tester.pump();
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
77 78 79 80 81
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
82 83 84 85
    const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
    await tester.pump();
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
86 87 88 89
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
90 91 92
  });

  testWidgets('KeepAlive with ListView without itemExtent', (WidgetTester tester) async {
93 94 95 96
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new ListView(
97
          cacheExtent: 0.0,
98 99 100 101 102 103
          addAutomaticKeepAlives: false,
          addRepaintBoundaries: false,
          children: generateList(new Container(height: 12.3, child: const Placeholder())), // about 50 widgets visible
        ),
      ),
    );
104 105
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
106 107 108 109
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
110 111
    await tester.drag(find.byType(ListView), const Offset(0.0, -300.0)); // about 25 widgets' worth
    await tester.pump();
112
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
113 114 115 116
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
117
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
118 119 120 121 122
    const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
    await tester.drag(find.byType(ListView), const Offset(0.0, 300.0)); // back to top
    await tester.pump();
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
123 124 125 126 127
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
128 129 130 131
    const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
    await tester.pump();
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
132 133 134 135
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
136 137 138
  });

  testWidgets('KeepAlive with GridView', (WidgetTester tester) async {
139 140 141 142
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new GridView.count(
143
          cacheExtent: 0.0,
144 145 146 147 148 149 150 151
          addAutomaticKeepAlives: false,
          addRepaintBoundaries: false,
          crossAxisCount: 2,
          childAspectRatio: 400.0 / 24.6, // about 50 widgets visible
          children: generateList(new Container(child: const Placeholder())),
        ),
      ),
    );
152 153
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
154 155 156 157
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
158 159
    await tester.drag(find.byType(GridView), const Offset(0.0, -300.0)); // about 25 widgets' worth
    await tester.pump();
160
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3), skipOffstage: false), findsNothing);
161 162 163 164
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61)), findsOneWidget);
165
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
166 167 168 169 170
    const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(true);
    await tester.drag(find.byType(GridView), const Offset(0.0, 300.0)); // back to top
    await tester.pump();
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
171 172 173 174 175
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60)), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
176 177 178 179
    const GlobalObjectKey<_LeafState>(60).currentState.setKeepAlive(false);
    await tester.pump();
    expect(find.byKey(const GlobalObjectKey<_LeafState>(3)), findsOneWidget);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(30)), findsOneWidget);
180 181 182 183
    expect(find.byKey(const GlobalObjectKey<_LeafState>(59), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(60), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(61), skipOffstage: false), findsNothing);
    expect(find.byKey(const GlobalObjectKey<_LeafState>(90), skipOffstage: false), findsNothing);
184 185 186
  });

  testWidgets('KeepAlive render tree description', (WidgetTester tester) async {
187 188 189 190 191 192 193 194 195 196 197
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new ListView(
          addAutomaticKeepAlives: false,
          addRepaintBoundaries: false,
          itemExtent: 400.0, // 2 visible children
          children: generateList(const Placeholder()),
        ),
      ),
    );
198
    // The important lines below are the ones marked with "<----"
199
    expect(tester.binding.renderView.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes(
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
      'RenderView#00000\n'
      ' │ debug mode enabled - ${Platform.operatingSystem}\n'
      ' │ window size: Size(2400.0, 1800.0) (in physical pixels)\n'
      ' │ device pixel ratio: 3.0 (physical pixels per logical pixel)\n'
      ' │ configuration: Size(800.0, 600.0) at 3.0x (in logical pixels)\n'
      ' │\n'
      ' └─child: RenderRepaintBoundary#00000\n'
      '   │ parentData: <none>\n'
      '   │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '   │ layer: OffsetLayer#00000\n'
      '   │ size: Size(800.0, 600.0)\n'
      '   │ metrics: 0.0% useful (1 bad vs 0 good)\n'
      '   │ diagnosis: insufficient data to draw conclusion (less than five\n'
      '   │   repaints)\n'
      '   │\n'
      '   └─child: RenderCustomPaint#00000\n'
      '     │ parentData: <none> (can use size)\n'
      '     │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '     │ size: Size(800.0, 600.0)\n'
      '     │\n'
      '     └─child: RenderRepaintBoundary#00000\n'
      '       │ parentData: <none> (can use size)\n'
      '       │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '       │ layer: OffsetLayer#00000\n'
      '       │ size: Size(800.0, 600.0)\n'
      '       │ metrics: 0.0% useful (1 bad vs 0 good)\n'
      '       │ diagnosis: insufficient data to draw conclusion (less than five\n'
      '       │   repaints)\n'
      '       │\n'
Ian Hickson's avatar
Ian Hickson committed
229
      '       └─child: _RenderScrollSemantics#00000\n'
230 231
      '         │ parentData: <none> (can use size)\n'
      '         │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
232
      '         │ semantic boundary\n'
233 234
      '         │ size: Size(800.0, 600.0)\n'
      '         │\n'
235
      '         └─child: RenderSemanticsGestureHandler#00000\n'
236 237 238
      '           │ parentData: <none> (can use size)\n'
      '           │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '           │ size: Size(800.0, 600.0)\n'
239
      '           │ gestures: vertical scroll\n'
240
      '           │\n'
241
      '           └─child: RenderPointerListener#00000\n'
242 243 244
      '             │ parentData: <none> (can use size)\n'
      '             │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '             │ size: Size(800.0, 600.0)\n'
245 246
      '             │ behavior: opaque\n'
      '             │ listeners: down\n'
247
      '             │\n'
248
      '             └─child: RenderSemanticsAnnotations#00000\n'
249 250 251 252
      '               │ parentData: <none> (can use size)\n'
      '               │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '               │ size: Size(800.0, 600.0)\n'
      '               │\n'
253 254 255 256 257 258
      '               └─child: RenderIgnorePointer#00000\n'
      '                 │ parentData: <none> (can use size)\n'
      '                 │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                 │ size: Size(800.0, 600.0)\n'
      '                 │ ignoring: false\n'
      '                 │ ignoringSemantics: false\n'
259
      '                 │\n'
260 261 262 263 264 265 266 267 268 269 270 271
      '                 └─child: RenderViewport#00000\n'
      '                   │ parentData: <none> (can use size)\n'
      '                   │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                   │ layer: OffsetLayer#00000\n'
      '                   │ size: Size(800.0, 600.0)\n'
      '                   │ axisDirection: down\n'
      '                   │ crossAxisDirection: right\n'
      '                   │ offset: ScrollPositionWithSingleContext#00000(offset: 0.0, range:\n'
      '                   │   0.0..39400.0, viewport: 600.0, ScrollableState,\n'
      '                   │   AlwaysScrollableScrollPhysics -> ClampingScrollPhysics,\n'
      '                   │   IdleScrollActivity#00000, ScrollDirection.idle)\n'
      '                   │ anchor: 0.0\n'
272
      '                   │\n'
273 274 275 276 277 278
      '                   └─center child: RenderSliverFixedExtentList#00000 relayoutBoundary=up1\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'
      '                     │   crossAxisDirection: AxisDirection.right,\n'
279 280
      '                     │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0\n'
      '                     │   cacheOrigin: 0.0 )\n'
281
      '                     │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
282 283 284
      '                     │   600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true,\n'
      '                     │   cacheExtent: 850.0)\n'
      '                     │ currently live children: 0 to 2\n'
285 286 287 288 289 290 291 292 293 294 295 296 297
      '                     │\n'
      '                     ├─child with index 0: RenderLimitedBox#00000\n'
      '                     │ │ parentData: index=0; layoutOffset=0.0\n'
      '                     │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │ │ size: Size(800.0, 400.0)\n'
      '                     │ │ maxWidth: 400.0\n'
      '                     │ │ maxHeight: 400.0\n'
      '                     │ │\n'
      '                     │ └─child: RenderCustomPaint#00000\n'
      '                     │     parentData: <none> (can use size)\n'
      '                     │     constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │     size: Size(800.0, 400.0)\n'
      '                     │\n'
298 299 300 301 302 303 304 305 306 307 308 309
      '                     ├─child with index 1: RenderLimitedBox#00000\n'                                       // <----- no dashed line starts here
      '                     │ │ parentData: index=1; layoutOffset=400.0\n'
      '                     │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │ │ size: Size(800.0, 400.0)\n'
      '                     │ │ maxWidth: 400.0\n'
      '                     │ │ maxHeight: 400.0\n'
      '                     │ │\n'
      '                     │ └─child: RenderCustomPaint#00000\n'
      '                     │     parentData: <none> (can use size)\n'
      '                     │     constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │     size: Size(800.0, 400.0)\n'
      '                     │\n'
310
      '                     └─child with index 2: RenderLimitedBox#00000 NEEDS-PAINT\n'
311
      '                       │ parentData: index=2; layoutOffset=800.0\n'
312 313 314 315 316
      '                       │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                       │ size: Size(800.0, 400.0)\n'
      '                       │ maxWidth: 400.0\n'
      '                       │ maxHeight: 400.0\n'
      '                       │\n'
317
      '                       └─child: RenderCustomPaint#00000 NEEDS-PAINT\n'
318 319 320
      '                           parentData: <none> (can use size)\n'
      '                           constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                           size: Size(800.0, 400.0)\n'
321 322 323 324 325 326 327
    ));
    const GlobalObjectKey<_LeafState>(0).currentState.setKeepAlive(true);
    await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0));
    await tester.pump();
    const GlobalObjectKey<_LeafState>(3).currentState.setKeepAlive(true);
    await tester.drag(find.byType(ListView), const Offset(0.0, -1000.0));
    await tester.pump();
328
    expect(tester.binding.renderView.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes(
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
      'RenderView#00000\n'
      ' │ debug mode enabled - ${Platform.operatingSystem}\n'
      ' │ window size: Size(2400.0, 1800.0) (in physical pixels)\n'
      ' │ device pixel ratio: 3.0 (physical pixels per logical pixel)\n'
      ' │ configuration: Size(800.0, 600.0) at 3.0x (in logical pixels)\n'
      ' │\n'
      ' └─child: RenderRepaintBoundary#00000\n'
      '   │ parentData: <none>\n'
      '   │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '   │ layer: OffsetLayer#00000\n'
      '   │ size: Size(800.0, 600.0)\n'
      '   │ metrics: 0.0% useful (1 bad vs 0 good)\n'
      '   │ diagnosis: insufficient data to draw conclusion (less than five\n'
      '   │   repaints)\n'
      '   │\n'
      '   └─child: RenderCustomPaint#00000\n'
      '     │ parentData: <none> (can use size)\n'
      '     │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '     │ size: Size(800.0, 600.0)\n'
      '     │\n'
      '     └─child: RenderRepaintBoundary#00000\n'
      '       │ parentData: <none> (can use size)\n'
      '       │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '       │ layer: OffsetLayer#00000\n'
      '       │ size: Size(800.0, 600.0)\n'
      '       │ metrics: 0.0% useful (1 bad vs 0 good)\n'
      '       │ diagnosis: insufficient data to draw conclusion (less than five\n'
      '       │   repaints)\n'
      '       │\n'
Ian Hickson's avatar
Ian Hickson committed
358
      '       └─child: _RenderScrollSemantics#00000\n'
359 360
      '         │ parentData: <none> (can use size)\n'
      '         │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
361
      '         │ semantic boundary\n'
362 363
      '         │ size: Size(800.0, 600.0)\n'
      '         │\n'
364
      '         └─child: RenderSemanticsGestureHandler#00000\n'
365 366 367
      '           │ parentData: <none> (can use size)\n'
      '           │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '           │ size: Size(800.0, 600.0)\n'
368
      '           │ gestures: vertical scroll\n'
369
      '           │\n'
370
      '           └─child: RenderPointerListener#00000\n'
371 372 373
      '             │ parentData: <none> (can use size)\n'
      '             │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '             │ size: Size(800.0, 600.0)\n'
374 375
      '             │ behavior: opaque\n'
      '             │ listeners: down\n'
376
      '             │\n'
377
      '             └─child: RenderSemanticsAnnotations#00000\n'
378 379 380 381
      '               │ parentData: <none> (can use size)\n'
      '               │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '               │ size: Size(800.0, 600.0)\n'
      '               │\n'
382 383 384 385 386 387
      '               └─child: RenderIgnorePointer#00000\n'
      '                 │ parentData: <none> (can use size)\n'
      '                 │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                 │ size: Size(800.0, 600.0)\n'
      '                 │ ignoring: false\n'
      '                 │ ignoringSemantics: false\n'
388
      '                 │\n'
389 390 391 392 393 394 395 396 397 398 399 400
      '                 └─child: RenderViewport#00000\n'
      '                   │ parentData: <none> (can use size)\n'
      '                   │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                   │ layer: OffsetLayer#00000\n'
      '                   │ size: Size(800.0, 600.0)\n'
      '                   │ axisDirection: down\n'
      '                   │ crossAxisDirection: right\n'
      '                   │ offset: ScrollPositionWithSingleContext#00000(offset: 2000.0,\n'
      '                   │   range: 0.0..39400.0, viewport: 600.0, ScrollableState,\n'
      '                   │   AlwaysScrollableScrollPhysics -> ClampingScrollPhysics,\n'
      '                   │   IdleScrollActivity#00000, ScrollDirection.idle)\n'
      '                   │ anchor: 0.0\n'
401
      '                   │\n'
402 403 404 405 406 407
      '                   └─center child: RenderSliverFixedExtentList#00000 relayoutBoundary=up1\n'
      '                     │ parentData: paintOffset=Offset(0.0, 0.0) (can use size)\n'
      '                     │ constraints: SliverConstraints(AxisDirection.down,\n'
      '                     │   GrowthDirection.forward, ScrollDirection.idle, scrollOffset:\n'
      '                     │   2000.0, remainingPaintExtent: 600.0, crossAxisExtent: 800.0,\n'
      '                     │   crossAxisDirection: AxisDirection.right,\n'
408 409
      '                     │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 1100.0\n'
      '                     │   cacheOrigin: -250.0 )\n'
410
      '                     │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
411 412 413 414
      '                     │   600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true,\n'
      '                     │   cacheExtent: 1100.0)\n'
      '                     │ currently live children: 4 to 7\n'
      '                     │\n'
415
      '                     ├─child with index 4: RenderLimitedBox#00000 NEEDS-PAINT\n'
416 417 418 419 420 421
      '                     │ │ parentData: index=4; layoutOffset=1600.0\n'
      '                     │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │ │ size: Size(800.0, 400.0)\n'
      '                     │ │ maxWidth: 400.0\n'
      '                     │ │ maxHeight: 400.0\n'
      '                     │ │\n'
422
      '                     │ └─child: RenderCustomPaint#00000 NEEDS-PAINT\n'
423 424 425
      '                     │     parentData: <none> (can use size)\n'
      '                     │     constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │     size: Size(800.0, 400.0)\n'
426 427 428 429 430 431 432 433 434 435 436 437 438 439
      '                     │\n'
      '                     ├─child with index 5: RenderLimitedBox#00000\n'                                       // <----- this is index 5, not 0
      '                     │ │ parentData: index=5; layoutOffset=2000.0\n'
      '                     │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │ │ size: Size(800.0, 400.0)\n'
      '                     │ │ maxWidth: 400.0\n'
      '                     │ │ maxHeight: 400.0\n'
      '                     │ │\n'
      '                     │ └─child: RenderCustomPaint#00000\n'
      '                     │     parentData: <none> (can use size)\n'
      '                     │     constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │     size: Size(800.0, 400.0)\n'
      '                     │\n'
      '                     ├─child with index 6: RenderLimitedBox#00000\n'
440 441 442 443 444 445 446 447 448 449 450
      '                     │ │ parentData: index=6; layoutOffset=2400.0\n'
      '                     │ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │ │ size: Size(800.0, 400.0)\n'
      '                     │ │ maxWidth: 400.0\n'
      '                     │ │ maxHeight: 400.0\n'
      '                     │ │\n'
      '                     │ └─child: RenderCustomPaint#00000\n'
      '                     │     parentData: <none> (can use size)\n'
      '                     │     constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     │     size: Size(800.0, 400.0)\n'
      '                     │\n'
451
      '                     ├─child with index 7: RenderLimitedBox#00000 NEEDS-PAINT\n'
452
      '                     ╎ │ parentData: index=7; layoutOffset=2800.0\n'
453 454 455 456 457
      '                     ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     ╎ │ size: Size(800.0, 400.0)\n'
      '                     ╎ │ maxWidth: 400.0\n'
      '                     ╎ │ maxHeight: 400.0\n'
      '                     ╎ │\n'
458
      '                     ╎ └─child: RenderCustomPaint#00000 NEEDS-PAINT\n'
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
      '                     ╎     parentData: <none> (can use size)\n'
      '                     ╎     constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     ╎     size: Size(800.0, 400.0)\n'
      '                     ╎\n'
      '                     ╎╌child with index 0 (kept alive offstage): RenderLimitedBox#00000\n'                 // <----- this one is index 0 and is marked as being offstage
      '                     ╎ │ parentData: index=0; keepAlive; layoutOffset=0.0\n'
      '                     ╎ │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     ╎ │ size: Size(800.0, 400.0)\n'
      '                     ╎ │ maxWidth: 400.0\n'
      '                     ╎ │ maxHeight: 400.0\n'
      '                     ╎ │\n'
      '                     ╎ └─child: RenderCustomPaint#00000\n'
      '                     ╎     parentData: <none> (can use size)\n'
      '                     ╎     constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                     ╎     size: Size(800.0, 400.0)\n'
      '                     ╎\n'                                                                                  // <----- dashed line ends here
      '                     └╌child with index 3 (kept alive offstage): RenderLimitedBox#00000\n'
      '                       │ parentData: index=3; keepAlive; layoutOffset=1200.0\n'
      '                       │ constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                       │ size: Size(800.0, 400.0)\n'
      '                       │ maxWidth: 400.0\n'
      '                       │ maxHeight: 400.0\n'
      '                       │\n'
      '                       └─child: RenderCustomPaint#00000\n'
      '                           parentData: <none> (can use size)\n'
      '                           constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                           size: Size(800.0, 400.0)\n'
486 487 488 489
    ));
  });

}