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

5 6
// @dart = 2.8

Adam Barth's avatar
Adam Barth committed
7
import 'package:flutter_test/flutter_test.dart';
8 9
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
10 11 12 13

import 'test_widgets.dart';

void checkTree(WidgetTester tester, List<BoxDecoration> expectedDecorations) {
14
  final MultiChildRenderObjectElement element = tester.element(find.byElementPredicate(
15 16
    (Element element) => element is MultiChildRenderObjectElement
  ));
17
  expect(element, isNotNull);
Dan Field's avatar
Dan Field committed
18
  expect(element.renderObject, isA<RenderStack>());
19
  final RenderStack renderObject = element.renderObject as RenderStack;
20 21
  try {
    RenderObject child = renderObject.firstChild;
22
    for (final BoxDecoration decoration in expectedDecorations) {
Dan Field's avatar
Dan Field committed
23
      expect(child, isA<RenderDecoratedBox>());
24
      final RenderDecoratedBox decoratedBox = child as RenderDecoratedBox;
25
      expect(decoratedBox.decoration, equals(decoration));
26
      final StackParentData decoratedBoxParentData = decoratedBox.parentData as StackParentData;
27
      child = decoratedBoxParentData.nextSibling;
28 29 30 31 32 33 34 35
    }
    expect(child, isNull);
  } catch (e) {
    print(renderObject.toStringDeep());
    rethrow;
  }
}

36 37 38 39 40 41 42
class MockMultiChildRenderObjectWidget extends MultiChildRenderObjectWidget {
  MockMultiChildRenderObjectWidget({ Key key, List<Widget> children }) : super(key: key, children: children);

  @override
  RenderObject createRenderObject(BuildContext context) => null;
}

43
void main() {
44
  testWidgets('MultiChildRenderObjectElement control test', (WidgetTester tester) async {
45

46
    await tester.pumpWidget(
47
      Stack(
48
        textDirection: TextDirection.ltr,
49
        children: const <Widget>[
50 51 52
          DecoratedBox(decoration: kBoxDecorationA),
          DecoratedBox(decoration: kBoxDecorationB),
          DecoratedBox(decoration: kBoxDecorationC),
53 54
        ],
      ),
55 56 57 58
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]);

59
    await tester.pumpWidget(
60
      Stack(
61
        textDirection: TextDirection.ltr,
62
        children: const <Widget>[
63 64
          DecoratedBox(decoration: kBoxDecorationA),
          DecoratedBox(decoration: kBoxDecorationC),
65 66
        ],
      ),
67 68 69 70
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationC]);

71
    await tester.pumpWidget(
72
      Stack(
73
        textDirection: TextDirection.ltr,
74
        children: const <Widget>[
75 76 77
          DecoratedBox(decoration: kBoxDecorationA),
          DecoratedBox(key: Key('b'), decoration: kBoxDecorationB),
          DecoratedBox(decoration: kBoxDecorationC),
78 79
        ],
      ),
80 81 82 83
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]);

84
    await tester.pumpWidget(
85
      Stack(
86
        textDirection: TextDirection.ltr,
87
        children: const <Widget>[
88 89 90
          DecoratedBox(key: Key('b'), decoration: kBoxDecorationB),
          DecoratedBox(decoration: kBoxDecorationC),
          DecoratedBox(key: Key('a'), decoration: kBoxDecorationA),
91 92
        ],
      ),
93 94 95 96
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationB, kBoxDecorationC, kBoxDecorationA]);

97
    await tester.pumpWidget(
98
      Stack(
99
        textDirection: TextDirection.ltr,
100
        children: const <Widget>[
101 102 103
          DecoratedBox(key: Key('a'), decoration: kBoxDecorationA),
          DecoratedBox(decoration: kBoxDecorationC),
          DecoratedBox(key: Key('b'), decoration: kBoxDecorationB),
104 105
        ],
      ),
106 107 108 109
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationC, kBoxDecorationB]);

110
    await tester.pumpWidget(
111
      Stack(
112
        textDirection: TextDirection.ltr,
113
        children: const <Widget>[
114
          DecoratedBox(decoration: kBoxDecorationC),
115 116
        ],
      ),
117 118 119 120
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationC]);

121
    await tester.pumpWidget(
122
      Stack(textDirection: TextDirection.ltr)
123 124 125 126
    );

    checkTree(tester, <BoxDecoration>[]);

127 128
  });

129
  testWidgets('MultiChildRenderObjectElement with stateless widgets', (WidgetTester tester) async {
130

131
    await tester.pumpWidget(
132
      Stack(
133
        textDirection: TextDirection.ltr,
134
        children: const <Widget>[
135 136 137
          DecoratedBox(decoration: kBoxDecorationA),
          DecoratedBox(decoration: kBoxDecorationB),
          DecoratedBox(decoration: kBoxDecorationC),
138 139
        ],
      ),
140 141 142 143
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]);

144
    await tester.pumpWidget(
145
      Stack(
146
        textDirection: TextDirection.ltr,
147
        children: <Widget>[
148
          const DecoratedBox(decoration: kBoxDecorationA),
149
          Container(
150
            child: const DecoratedBox(decoration: kBoxDecorationB),
151
          ),
152
          const DecoratedBox(decoration: kBoxDecorationC),
153 154
        ],
      ),
155 156 157 158
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]);

159
    await tester.pumpWidget(
160
      Stack(
161
        textDirection: TextDirection.ltr,
162
        children: <Widget>[
163
          const DecoratedBox(decoration: kBoxDecorationA),
164 165
          Container(
            child: Container(
166
              child: const DecoratedBox(decoration: kBoxDecorationB),
167
            ),
168
          ),
169
          const DecoratedBox(decoration: kBoxDecorationC),
170 171
        ],
      ),
172 173 174 175
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationB, kBoxDecorationC]);

176
    await tester.pumpWidget(
177
      Stack(
178
        textDirection: TextDirection.ltr,
179
        children: <Widget>[
180 181
          Container(
            child: Container(
182
              child: const DecoratedBox(decoration: kBoxDecorationB),
183
            ),
184
          ),
185
          Container(
186
            child: const DecoratedBox(decoration: kBoxDecorationA),
187
          ),
188
          const DecoratedBox(decoration: kBoxDecorationC),
189 190
        ],
      ),
191 192 193 194
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationB, kBoxDecorationA, kBoxDecorationC]);

195
    await tester.pumpWidget(
196
      Stack(
197
        textDirection: TextDirection.ltr,
198
        children: <Widget>[
199
          Container(
200
            child: const DecoratedBox(decoration: kBoxDecorationB),
201
          ),
202
          Container(
203
            child: const DecoratedBox(decoration: kBoxDecorationA),
204
          ),
205
          const DecoratedBox(decoration: kBoxDecorationC),
206 207
        ],
      ),
208 209 210 211
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationB, kBoxDecorationA, kBoxDecorationC]);

212
    await tester.pumpWidget(
213
      Stack(
214
        textDirection: TextDirection.ltr,
215
        children: <Widget>[
216
          Container(
217
            key: const Key('b'),
218
            child: const DecoratedBox(decoration: kBoxDecorationB),
219
          ),
220
          Container(
221
            key: const Key('a'),
222
            child: const DecoratedBox(decoration: kBoxDecorationA),
223
          ),
224 225
        ],
      ),
226 227 228 229
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationB, kBoxDecorationA]);

230
    await tester.pumpWidget(
231
      Stack(
232
        textDirection: TextDirection.ltr,
233
        children: <Widget>[
234
          Container(
235
            key: const Key('a'),
236
            child: const DecoratedBox(decoration: kBoxDecorationA),
237
          ),
238
          Container(
239
            key: const Key('b'),
240
            child: const DecoratedBox(decoration: kBoxDecorationB),
241
          ),
242 243
        ],
      ),
244 245 246 247
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationB]);

248
    await tester.pumpWidget(
249
      Stack(textDirection: TextDirection.ltr)
250 251 252
    );

    checkTree(tester, <BoxDecoration>[]);
253 254
  });

255 256
  testWidgets('MultiChildRenderObjectElement with stateful widgets', (WidgetTester tester) async {
    await tester.pumpWidget(
257
      Stack(
258
        textDirection: TextDirection.ltr,
259
        children: const <Widget>[
260 261
          DecoratedBox(decoration: kBoxDecorationA),
          DecoratedBox(decoration: kBoxDecorationB),
262 263
        ],
      ),
264 265 266 267
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationB]);

268
    await tester.pumpWidget(
269
      Stack(
270
        textDirection: TextDirection.ltr,
271
        children: const <Widget>[
272 273 274
          FlipWidget(
            left: DecoratedBox(decoration: kBoxDecorationA),
            right: DecoratedBox(decoration: kBoxDecorationB),
275
          ),
276
          DecoratedBox(decoration: kBoxDecorationC),
277 278
        ],
      ),
279 280 281 282 283
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationA, kBoxDecorationC]);

    flipStatefulWidget(tester);
284
    await tester.pump();
285 286 287

    checkTree(tester, <BoxDecoration>[kBoxDecorationB, kBoxDecorationC]);

288
    await tester.pumpWidget(
289
      Stack(
290
        textDirection: TextDirection.ltr,
291
        children: const <Widget>[
292 293 294
          FlipWidget(
            left: DecoratedBox(decoration: kBoxDecorationA),
            right: DecoratedBox(decoration: kBoxDecorationB),
295
          ),
296 297
        ],
      ),
298 299 300 301 302
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationB]);

    flipStatefulWidget(tester);
303
    await tester.pump();
304 305 306

    checkTree(tester, <BoxDecoration>[kBoxDecorationA]);

307
    await tester.pumpWidget(
308
      Stack(
309
        textDirection: TextDirection.ltr,
310
        children: const <Widget>[
311 312 313 314
          FlipWidget(
            key: Key('flip'),
            left: DecoratedBox(decoration: kBoxDecorationA),
            right: DecoratedBox(decoration: kBoxDecorationB),
315
          ),
316 317
        ],
      ),
318 319
    );

320
    await tester.pumpWidget(
321
      Stack(
322
        textDirection: TextDirection.ltr,
323
        children: const <Widget>[
324 325 326 327 328
          DecoratedBox(key: Key('c'), decoration: kBoxDecorationC),
          FlipWidget(
            key: Key('flip'),
            left: DecoratedBox(decoration: kBoxDecorationA),
            right: DecoratedBox(decoration: kBoxDecorationB),
329
          ),
330 331
        ],
      ),
332 333 334 335 336
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationC, kBoxDecorationA]);

    flipStatefulWidget(tester);
337
    await tester.pump();
338 339 340

    checkTree(tester, <BoxDecoration>[kBoxDecorationC, kBoxDecorationB]);

341
    await tester.pumpWidget(
342
      Stack(
343
        textDirection: TextDirection.ltr,
344
        children: const <Widget>[
345 346 347 348
          FlipWidget(
            key: Key('flip'),
            left: DecoratedBox(decoration: kBoxDecorationA),
            right: DecoratedBox(decoration: kBoxDecorationB),
349
          ),
350
          DecoratedBox(key: Key('c'), decoration: kBoxDecorationC),
351 352
        ],
      ),
353 354 355
    );

    checkTree(tester, <BoxDecoration>[kBoxDecorationB, kBoxDecorationC]);
356
  });
357 358 359 360 361 362 363 364 365 366 367 368 369

  // Regression test for https://github.com/flutter/flutter/issues/37136.
  test('provides useful assertion message when one of the children is null', () {
    bool assertionTriggered = false;
    try {
      MockMultiChildRenderObjectWidget(children: const <Widget>[null]);
    } catch (e) {
      expect(e.toString(), contains("MockMultiChildRenderObjectWidget's children must not contain any null values,"));
      assertionTriggered = true;
    }

    expect(assertionTriggered, isTrue);
  });
370
}