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

5
@TestOn('!chrome') // diagnostics use Platform.operatingSystem.
6 7 8 9 10 11
import 'dart:io' show Platform;

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

class Leaf extends StatefulWidget {
12
  const Leaf({ Key key, this.child }) : super(key: key);
13 14
  final Widget child;
  @override
15
  _LeafState createState() => _LeafState();
16 17 18 19 20 21 22 23 24 25 26
}

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

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

  @override
  Widget build(BuildContext context) {
27
    return KeepAlive(
28 29 30 31 32 33 34
      keepAlive: _keepAlive,
      child: widget.child,
    );
  }
}

List<Widget> generateList(Widget child) {
35
  return List<Widget>.generate(
36
    100,
37 38
    (int index) => Leaf(
      key: GlobalObjectKey<_LeafState>(index),
39 40 41 42 43 44 45 46
      child: child,
    ),
    growable: false,
  );
}

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

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

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

  testWidgets('KeepAlive render tree description', (WidgetTester tester) async {
191
    await tester.pumpWidget(
192
      Directionality(
193
        textDirection: TextDirection.ltr,
194
        child: ListView(
195 196
          addAutomaticKeepAlives: false,
          addRepaintBoundaries: false,
197
          addSemanticIndexes: false,
198 199 200 201 202
          itemExtent: 400.0, // 2 visible children
          children: generateList(const Placeholder()),
        ),
      ),
    );
203
    // The important lines below are the ones marked with "<----"
204
    expect(tester.binding.renderView.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes(
205 206 207 208 209 210 211
      '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'
212
      '   │ needs compositing\n'
213 214 215 216 217 218 219 220 221
      '   │ 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'
222
      '     │ needs compositing\n'
223 224 225 226 227
      '     │ 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'
228
      '       │ needs compositing\n'
229 230 231 232 233 234 235 236
      '       │ 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
237
      '       └─child: _RenderScrollSemantics#00000\n'
238
      '         │ needs compositing\n'
239 240
      '         │ parentData: <none> (can use size)\n'
      '         │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
241
      '         │ semantics node: SemanticsNode#1\n'
242
      '         │ semantic boundary\n'
243 244
      '         │ size: Size(800.0, 600.0)\n'
      '         │\n'
245
      '         └─child: RenderPointerListener#00000\n'
246
      '           │ needs compositing\n'
247 248 249
      '           │ parentData: <none> (can use size)\n'
      '           │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '           │ size: Size(800.0, 600.0)\n'
250 251
      '           │ behavior: deferToChild\n'
      '           │ listeners: signal\n'
252
      '           │\n'
253
      '           └─child: RenderSemanticsGestureHandler#00000\n'
254
      '             │ needs compositing\n'
255 256 257
      '             │ parentData: <none> (can use size)\n'
      '             │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '             │ size: Size(800.0, 600.0)\n'
258
      '             │ gestures: vertical scroll\n'
259
      '             │\n'
260
      '             └─child: RenderPointerListener#00000\n'
261
      '               │ needs compositing\n'
262 263 264
      '               │ parentData: <none> (can use size)\n'
      '               │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '               │ size: Size(800.0, 600.0)\n'
265 266
      '               │ behavior: opaque\n'
      '               │ listeners: down\n'
267
      '               │\n'
268
      '               └─child: RenderSemanticsAnnotations#00000\n'
269
      '                 │ needs compositing\n'
270 271 272
      '                 │ parentData: <none> (can use size)\n'
      '                 │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                 │ size: Size(800.0, 600.0)\n'
273
      '                 │\n'
274
      '                 └─child: RenderIgnorePointer#00000\n'
275
      '                   │ needs compositing\n'
276 277 278
      '                   │ parentData: <none> (can use size)\n'
      '                   │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                   │ size: Size(800.0, 600.0)\n'
279 280
      '                   │ ignoring: false\n'
      '                   │ ignoringSemantics: false\n'
281
      '                   │\n'
282
      '                   └─child: RenderViewport#00000\n'
283
      '                     │ needs compositing\n'
284 285 286 287 288 289 290 291
      '                     │ 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'
292 293 294
      '                     │   AlwaysScrollableScrollPhysics -> ClampingScrollPhysics ->\n'
      '                     │   RangeMaintainingScrollPhysics, IdleScrollActivity#00000,\n'
      '                     │   ScrollDirection.idle)\n'
295
      '                     │ anchor: 0.0\n'
296
      '                     │\n'
297 298 299 300 301 302
      '                     └─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'
303 304
      '                       │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0,\n'
      '                       │   cacheOrigin: 0.0)\n'
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
      '                       │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
      '                       │   600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true,\n'
      '                       │   cacheExtent: 850.0)\n'
      '                       │ currently live children: 0 to 2\n'
      '                       │\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'
321
      '                       │\n'
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
      '                       ├─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'
      '                       └─child with index 2: RenderLimitedBox#00000 NEEDS-PAINT\n'
      '                         │ parentData: index=2; layoutOffset=800.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 NEEDS-PAINT\n'
      '                             parentData: <none> (can use size)\n'
      '                             constraints: BoxConstraints(w=800.0, h=400.0)\n'
      '                             size: Size(800.0, 400.0)\n'
345 346 347 348 349 350 351
    ));
    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();
352
    expect(tester.binding.renderView.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes(
353 354 355 356 357 358 359
      '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'
360
      '   │ needs compositing\n'
361 362 363 364 365 366 367 368 369
      '   │ 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'
370
      '     │ needs compositing\n'
371 372 373 374 375
      '     │ 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'
376
      '       │ needs compositing\n'
377 378 379 380 381 382 383 384
      '       │ 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
385
      '       └─child: _RenderScrollSemantics#00000\n'
386
      '         │ needs compositing\n'
387 388
      '         │ parentData: <none> (can use size)\n'
      '         │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
389
      '         │ semantics node: SemanticsNode#1\n'
390
      '         │ semantic boundary\n'
391 392
      '         │ size: Size(800.0, 600.0)\n'
      '         │\n'
393
      '         └─child: RenderPointerListener#00000\n'
394
      '           │ needs compositing\n'
395 396 397
      '           │ parentData: <none> (can use size)\n'
      '           │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '           │ size: Size(800.0, 600.0)\n'
398 399
      '           │ behavior: deferToChild\n'
      '           │ listeners: signal\n'
400
      '           │\n'
401
      '           └─child: RenderSemanticsGestureHandler#00000\n'
402
      '             │ needs compositing\n'
403 404 405
      '             │ parentData: <none> (can use size)\n'
      '             │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '             │ size: Size(800.0, 600.0)\n'
406
      '             │ gestures: vertical scroll\n'
407
      '             │\n'
408
      '             └─child: RenderPointerListener#00000\n'
409
      '               │ needs compositing\n'
410 411 412
      '               │ parentData: <none> (can use size)\n'
      '               │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '               │ size: Size(800.0, 600.0)\n'
413 414
      '               │ behavior: opaque\n'
      '               │ listeners: down\n'
415
      '               │\n'
416
      '               └─child: RenderSemanticsAnnotations#00000\n'
417
      '                 │ needs compositing\n'
418 419 420
      '                 │ parentData: <none> (can use size)\n'
      '                 │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                 │ size: Size(800.0, 600.0)\n'
421
      '                 │\n'
422
      '                 └─child: RenderIgnorePointer#00000\n'
423
      '                   │ needs compositing\n'
424 425 426
      '                   │ parentData: <none> (can use size)\n'
      '                   │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                   │ size: Size(800.0, 600.0)\n'
427 428
      '                   │ ignoring: false\n'
      '                   │ ignoringSemantics: false\n'
429
      '                   │\n'
430
      '                   └─child: RenderViewport#00000\n'
431
      '                     │ needs compositing\n'
432 433 434 435 436 437 438 439
      '                     │ 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'
440 441 442
      '                     │   AlwaysScrollableScrollPhysics -> ClampingScrollPhysics ->\n'
      '                     │   RangeMaintainingScrollPhysics, IdleScrollActivity#00000,\n'
      '                     │   ScrollDirection.idle)\n'
443
      '                     │ anchor: 0.0\n'
444
      '                     │\n'
445 446 447 448 449 450
      '                     └─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'
451 452
      '                       │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 1100.0,\n'
      '                       │   cacheOrigin: -250.0)\n'
453 454 455 456 457 458 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 486 487 488 489 490 491 492
      '                       │ geometry: SliverGeometry(scrollExtent: 40000.0, paintExtent:\n'
      '                       │   600.0, maxPaintExtent: 40000.0, hasVisualOverflow: true,\n'
      '                       │   cacheExtent: 1100.0)\n'
      '                       │ currently live children: 4 to 7\n'
      '                       │\n'
      '                       ├─child with index 4: RenderLimitedBox#00000 NEEDS-PAINT\n'
      '                       │ │ 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'
      '                       │ └─child: RenderCustomPaint#00000 NEEDS-PAINT\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 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'
      '                       │ │ 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'
493
      '                       │\n'
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
      '                       ├─child with index 7: RenderLimitedBox#00000 NEEDS-PAINT\n'
      '                       ╎ │ parentData: index=7; layoutOffset=2800.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 NEEDS-PAINT\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 0 (kept alive but not laid out): RenderLimitedBox#00000\n'               // <----- this one is index 0 and is marked as being kept alive but not laid out
      '                       ╎ │ 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 but not laid out): 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'
529 530 531 532
    ));
  });

}