keep_alive_test.dart 30.6 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 292 293 294
      '                     │ 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'
295
      '                     │\n'
296 297 298 299 300 301
      '                     └─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'
302 303
      '                       │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 850.0,\n'
      '                       │   cacheOrigin: 0.0)\n'
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
      '                       │ 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'
320
      '                       │\n'
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
      '                       ├─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'
344 345 346 347 348 349 350
    ));
    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();
351
    expect(tester.binding.renderView.toStringDeep(minLevel: DiagnosticLevel.info), equalsIgnoringHashCodes(
352 353 354 355 356 357 358
      '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'
359
      '   │ needs compositing\n'
360 361 362 363 364 365 366 367 368
      '   │ 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'
369
      '     │ needs compositing\n'
370 371 372 373 374
      '     │ 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'
375
      '       │ needs compositing\n'
376 377 378 379 380 381 382 383
      '       │ 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
384
      '       └─child: _RenderScrollSemantics#00000\n'
385
      '         │ needs compositing\n'
386 387
      '         │ parentData: <none> (can use size)\n'
      '         │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
388
      '         │ semantics node: SemanticsNode#1\n'
389
      '         │ semantic boundary\n'
390 391
      '         │ size: Size(800.0, 600.0)\n'
      '         │\n'
392
      '         └─child: RenderPointerListener#00000\n'
393
      '           │ needs compositing\n'
394 395 396
      '           │ parentData: <none> (can use size)\n'
      '           │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '           │ size: Size(800.0, 600.0)\n'
397 398
      '           │ behavior: deferToChild\n'
      '           │ listeners: signal\n'
399
      '           │\n'
400
      '           └─child: RenderSemanticsGestureHandler#00000\n'
401
      '             │ needs compositing\n'
402 403 404
      '             │ parentData: <none> (can use size)\n'
      '             │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '             │ size: Size(800.0, 600.0)\n'
405
      '             │ gestures: vertical scroll\n'
406
      '             │\n'
407
      '             └─child: RenderPointerListener#00000\n'
408
      '               │ needs compositing\n'
409 410 411
      '               │ parentData: <none> (can use size)\n'
      '               │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '               │ size: Size(800.0, 600.0)\n'
412 413
      '               │ behavior: opaque\n'
      '               │ listeners: down\n'
414
      '               │\n'
415
      '               └─child: RenderSemanticsAnnotations#00000\n'
416
      '                 │ needs compositing\n'
417 418 419
      '                 │ parentData: <none> (can use size)\n'
      '                 │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                 │ size: Size(800.0, 600.0)\n'
420
      '                 │\n'
421
      '                 └─child: RenderIgnorePointer#00000\n'
422
      '                   │ needs compositing\n'
423 424 425
      '                   │ parentData: <none> (can use size)\n'
      '                   │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
      '                   │ size: Size(800.0, 600.0)\n'
426 427
      '                   │ ignoring: false\n'
      '                   │ ignoringSemantics: false\n'
428
      '                   │\n'
429
      '                   └─child: RenderViewport#00000\n'
430
      '                     │ needs compositing\n'
431 432 433 434 435 436 437 438 439 440 441
      '                     │ 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'
442
      '                     │\n'
443 444 445 446 447 448
      '                     └─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'
449 450
      '                       │   viewportMainAxisExtent: 600.0, remainingCacheExtent: 1100.0,\n'
      '                       │   cacheOrigin: -250.0)\n'
451 452 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
      '                       │ 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'
491
      '                       │\n'
492 493 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
      '                       ├─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'
527 528 529 530
    ));
  });

}