1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:ui';
6
7import 'package:flutter/material.dart';
8import 'package:flutter/rendering.dart';
9import 'package:flutter_test/flutter_test.dart';
10import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
11
12import 'multi_view_testing.dart';
13
14void main() {
15 testWidgets('Providing a RenderObjectWidget directly to the RootWidget fails', (
16 WidgetTester tester,
17 ) async {
18 // No render tree exists to attach the RenderObjectWidget to.
19 await tester.pumpWidget(wrapWithView: false, const ColoredBox(color: Colors.red));
20
21 expect(
22 tester.takeException(),
23 isFlutterError.having(
24 (FlutterError error) => error.message,
25 'message',
26 startsWith(
27 'The render object for ColoredBox cannot find ancestor render object to attach to.',
28 ),
29 ),
30 );
31 });
32
33 testWidgets('Moving a RenderObjectWidget to the RootWidget via GlobalKey fails', (
34 WidgetTester tester,
35 ) async {
36 final Widget globalKeyedWidget = ColoredBox(key: GlobalKey(), color: Colors.red);
37
38 await tester.pumpWidget(wrapWithView: false, View(view: tester.view, child: globalKeyedWidget));
39 expect(tester.takeException(), isNull);
40
41 await tester.pumpWidget(wrapWithView: false, globalKeyedWidget);
42
43 expect(
44 tester.takeException(),
45 isFlutterError.having(
46 (FlutterError error) => error.message,
47 'message',
48 contains('cannot find ancestor render object to attach to.'),
49 ),
50 );
51 });
52
53 testWidgets(
54 'A View cannot be a child of a render object widget',
55 experimentalLeakTesting:
56 LeakTesting.settings.withIgnoredAll(), // leaking by design because of exception
57 (WidgetTester tester) async {
58 await tester.pumpWidget(Center(child: View(view: FakeView(tester.view), child: Container())));
59
60 expect(
61 tester.takeException(),
62 isFlutterError.having(
63 (FlutterError error) => error.message,
64 'message',
65 contains('cannot maintain an independent render tree at its current location.'),
66 ),
67 );
68 },
69 );
70
71 testWidgets(
72 'The child of a ViewAnchor cannot be a View',
73 experimentalLeakTesting:
74 LeakTesting.settings.withIgnoredAll(), // leaking by design because of exception
75 (WidgetTester tester) async {
76 await tester.pumpWidget(
77 ViewAnchor(child: View(view: FakeView(tester.view), child: Container())),
78 );
79
80 expect(
81 tester.takeException(),
82 isFlutterError.having(
83 (FlutterError error) => error.message,
84 'message',
85 contains('cannot maintain an independent render tree at its current location.'),
86 ),
87 );
88 },
89 );
90
91 testWidgets('A View can not be moved via GlobalKey to be a child of a RenderObject', (
92 WidgetTester tester,
93 ) async {
94 final Widget globalKeyedView = View(
95 key: GlobalKey(),
96 view: FakeView(tester.view),
97 child: const ColoredBox(color: Colors.red),
98 );
99
100 await tester.pumpWidget(wrapWithView: false, globalKeyedView);
101 expect(tester.takeException(), isNull);
102
103 await tester.pumpWidget(wrapWithView: false, View(view: tester.view, child: globalKeyedView));
104
105 expect(
106 tester.takeException(),
107 isFlutterError.having(
108 (FlutterError error) => error.message,
109 'message',
110 contains('cannot maintain an independent render tree at its current location.'),
111 ),
112 );
113 });
114
115 testWidgets('The view property of a ViewAnchor cannot be a render object widget', (
116 WidgetTester tester,
117 ) async {
118 await tester.pumpWidget(
119 ViewAnchor(view: const ColoredBox(color: Colors.red), child: Container()),
120 );
121
122 expect(
123 tester.takeException(),
124 isFlutterError.having(
125 (FlutterError error) => error.message,
126 'message',
127 startsWith(
128 'The render object for ColoredBox cannot find ancestor render object to attach to.',
129 ),
130 ),
131 );
132 });
133
134 testWidgets(
135 'A RenderObject cannot be moved into the view property of a ViewAnchor via GlobalKey',
136 (WidgetTester tester) async {
137 final Widget globalKeyedWidget = ColoredBox(key: GlobalKey(), color: Colors.red);
138
139 await tester.pumpWidget(ViewAnchor(child: globalKeyedWidget));
140 expect(tester.takeException(), isNull);
141
142 await tester.pumpWidget(ViewAnchor(view: globalKeyedWidget, child: const SizedBox()));
143
144 expect(
145 tester.takeException(),
146 isFlutterError.having(
147 (FlutterError error) => error.message,
148 'message',
149 contains('cannot find ancestor render object to attach to.'),
150 ),
151 );
152 },
153 );
154
155 testWidgets('ViewAnchor cannot be used at the top of the widget tree (outside of View)', (
156 WidgetTester tester,
157 ) async {
158 await tester.pumpWidget(wrapWithView: false, const ViewAnchor(child: SizedBox()));
159
160 expect(
161 tester.takeException(),
162 isFlutterError.having(
163 (FlutterError error) => error.message,
164 'message',
165 startsWith(
166 'The render object for SizedBox cannot find ancestor render object to attach to.',
167 ),
168 ),
169 );
170 });
171
172 testWidgets(
173 'ViewAnchor cannot be moved to the top of the widget tree (outside of View) via GlobalKey',
174 (WidgetTester tester) async {
175 final Widget globalKeyedViewAnchor = ViewAnchor(key: GlobalKey(), child: const SizedBox());
176
177 await tester.pumpWidget(
178 wrapWithView: false,
179 View(view: tester.view, child: globalKeyedViewAnchor),
180 );
181 expect(tester.takeException(), isNull);
182
183 await tester.pumpWidget(wrapWithView: false, globalKeyedViewAnchor);
184
185 expect(
186 tester.takeException(),
187 isFlutterError.having(
188 (FlutterError error) => error.message,
189 'message',
190 contains('cannot find ancestor render object to attach to.'),
191 ),
192 );
193 },
194 );
195
196 testWidgets('View can be used at the top of the widget tree', (WidgetTester tester) async {
197 await tester.pumpWidget(wrapWithView: false, View(view: tester.view, child: Container()));
198
199 expect(tester.takeException(), isNull);
200 });
201
202 testWidgets('View can be moved to the top of the widget tree view GlobalKey', (
203 WidgetTester tester,
204 ) async {
205 final Widget globalKeyView = View(
206 view: FakeView(tester.view),
207 child: const ColoredBox(color: Colors.red),
208 );
209
210 await tester.pumpWidget(
211 wrapWithView: false,
212 View(
213 view: tester.view,
214 child: ViewAnchor(
215 view: globalKeyView, // This one has trouble when deactivating
216 child: const SizedBox(),
217 ),
218 ),
219 );
220 expect(tester.takeException(), isNull);
221 expect(find.byType(SizedBox), findsOneWidget);
222 expect(find.byType(ColoredBox), findsOneWidget);
223
224 await tester.pumpWidget(wrapWithView: false, globalKeyView);
225 expect(tester.takeException(), isNull);
226 expect(find.byType(SizedBox), findsNothing);
227 expect(find.byType(ColoredBox), findsOneWidget);
228 });
229
230 testWidgets('ViewCollection can be used at the top of the widget tree', (
231 WidgetTester tester,
232 ) async {
233 await tester.pumpWidget(
234 wrapWithView: false,
235 ViewCollection(views: <Widget>[View(view: tester.view, child: Container())]),
236 );
237
238 expect(tester.takeException(), isNull);
239 });
240
241 testWidgets('ViewCollection cannot be used inside a View', (WidgetTester tester) async {
242 await tester.pumpWidget(
243 ViewCollection(views: <Widget>[View(view: FakeView(tester.view), child: Container())]),
244 );
245
246 expect(
247 tester.takeException(),
248 isFlutterError.having(
249 (FlutterError error) => error.message,
250 'message',
251 startsWith(
252 'The Element for ViewCollection cannot be inserted into slot "null" of its ancestor.',
253 ),
254 ),
255 );
256 });
257
258 testWidgets('ViewCollection can be used as ViewAnchor.view', (WidgetTester tester) async {
259 await tester.pumpWidget(
260 ViewAnchor(
261 view: ViewCollection(
262 views: <Widget>[View(view: FakeView(tester.view), child: Container())],
263 ),
264 child: Container(),
265 ),
266 );
267
268 expect(tester.takeException(), isNull);
269 });
270
271 testWidgets('ViewCollection cannot have render object widgets as children', (
272 WidgetTester tester,
273 ) async {
274 await tester.pumpWidget(
275 wrapWithView: false,
276 const ViewCollection(views: <Widget>[ColoredBox(color: Colors.red)]),
277 );
278
279 expect(
280 tester.takeException(),
281 isFlutterError.having(
282 (FlutterError error) => error.message,
283 'message',
284 startsWith(
285 'The render object for ColoredBox cannot find ancestor render object to attach to.',
286 ),
287 ),
288 );
289 });
290
291 testWidgets('Views can be moved in and out of ViewCollections via GlobalKey', (
292 WidgetTester tester,
293 ) async {
294 final Widget greenView = View(
295 key: GlobalKey(debugLabel: 'green'),
296 view: tester.view,
297 child: const ColoredBox(color: Colors.green),
298 );
299 final Widget redView = View(
300 key: GlobalKey(debugLabel: 'red'),
301 view: FakeView(tester.view),
302 child: const ColoredBox(color: Colors.red),
303 );
304
305 await tester.pumpWidget(
306 wrapWithView: false,
307 ViewCollection(
308 views: <Widget>[
309 greenView,
310 ViewCollection(views: <Widget>[redView]),
311 ],
312 ),
313 );
314 expect(tester.takeException(), isNull);
315 expect(find.byType(ColoredBox), findsNWidgets(2));
316
317 await tester.pumpWidget(
318 wrapWithView: false,
319 ViewCollection(
320 views: <Widget>[
321 redView,
322 ViewCollection(views: <Widget>[greenView]),
323 ],
324 ),
325 );
326 expect(tester.takeException(), isNull);
327 expect(find.byType(ColoredBox), findsNWidgets(2));
328 });
329
330 testWidgets('Can move stuff between views via global key: viewA -> viewB', (
331 WidgetTester tester,
332 ) async {
333 final FlutterView greenView = tester.view;
334 final FlutterView redView = FakeView(tester.view);
335 final Widget globalKeyChild = SizedBox(key: GlobalKey());
336
337 Map<int, RenderObject> collectLeafRenderObjects() {
338 final Map<int, RenderObject> result = <int, RenderObject>{};
339 for (final RenderView renderView in RendererBinding.instance.renderViews) {
340 void visit(RenderObject object) {
341 result[renderView.flutterView.viewId] = object;
342 object.visitChildren(visit);
343 }
344
345 visit(renderView);
346 }
347 return result;
348 }
349
350 await tester.pumpWidget(
351 wrapWithView: false,
352 ViewCollection(
353 views: <Widget>[
354 View(view: greenView, child: ColoredBox(color: Colors.green, child: globalKeyChild)),
355 View(view: redView, child: const ColoredBox(color: Colors.red)),
356 ],
357 ),
358 );
359 expect(
360 find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)),
361 findsOneWidget,
362 );
363 expect(
364 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
365 findsNothing,
366 );
367 final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
368
369 Map<int, RenderObject> leafRenderObject = collectLeafRenderObjects();
370 expect(leafRenderObject[greenView.viewId], isA<RenderConstrainedBox>());
371 expect(leafRenderObject[redView.viewId], isNot(isA<RenderConstrainedBox>()));
372
373 // Move the child.
374 await tester.pumpWidget(
375 wrapWithView: false,
376 ViewCollection(
377 views: <Widget>[
378 View(view: greenView, child: const ColoredBox(color: Colors.green)),
379 View(view: redView, child: ColoredBox(color: Colors.red, child: globalKeyChild)),
380 ],
381 ),
382 );
383
384 expect(
385 find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)),
386 findsNothing,
387 );
388 expect(
389 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
390 findsOneWidget,
391 );
392 expect(tester.renderObject(find.byKey(globalKeyChild.key!)), equals(boxWithGlobalKey));
393
394 leafRenderObject = collectLeafRenderObjects();
395 expect(leafRenderObject[greenView.viewId], isNot(isA<RenderConstrainedBox>()));
396 expect(leafRenderObject[redView.viewId], isA<RenderConstrainedBox>());
397 });
398
399 testWidgets('Can move stuff between views via global key: viewB -> viewA', (
400 WidgetTester tester,
401 ) async {
402 final FlutterView greenView = tester.view;
403 final FlutterView redView = FakeView(tester.view);
404 final Widget globalKeyChild = SizedBox(key: GlobalKey());
405
406 Map<int, RenderObject> collectLeafRenderObjects() {
407 final Map<int, RenderObject> result = <int, RenderObject>{};
408 for (final RenderView renderView in RendererBinding.instance.renderViews) {
409 void visit(RenderObject object) {
410 result[renderView.flutterView.viewId] = object;
411 object.visitChildren(visit);
412 }
413
414 visit(renderView);
415 }
416 return result;
417 }
418
419 await tester.pumpWidget(
420 wrapWithView: false,
421 ViewCollection(
422 views: <Widget>[
423 View(view: greenView, child: const ColoredBox(color: Colors.green)),
424 View(view: redView, child: ColoredBox(color: Colors.red, child: globalKeyChild)),
425 ],
426 ),
427 );
428 expect(
429 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
430 findsOneWidget,
431 );
432 expect(
433 find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)),
434 findsNothing,
435 );
436 final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
437
438 Map<int, RenderObject> leafRenderObject = collectLeafRenderObjects();
439 expect(leafRenderObject[redView.viewId], isA<RenderConstrainedBox>());
440 expect(leafRenderObject[greenView.viewId], isNot(isA<RenderConstrainedBox>()));
441
442 // Move the child.
443 await tester.pumpWidget(
444 wrapWithView: false,
445 ViewCollection(
446 views: <Widget>[
447 View(view: greenView, child: ColoredBox(color: Colors.green, child: globalKeyChild)),
448 View(view: redView, child: const ColoredBox(color: Colors.red)),
449 ],
450 ),
451 );
452
453 expect(
454 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
455 findsNothing,
456 );
457 expect(
458 find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)),
459 findsOneWidget,
460 );
461 expect(tester.renderObject(find.byKey(globalKeyChild.key!)), equals(boxWithGlobalKey));
462
463 leafRenderObject = collectLeafRenderObjects();
464 expect(leafRenderObject[redView.viewId], isNot(isA<RenderConstrainedBox>()));
465 expect(leafRenderObject[greenView.viewId], isA<RenderConstrainedBox>());
466 });
467
468 testWidgets('Can move stuff out of a view that is going away, viewA -> ViewB', (
469 WidgetTester tester,
470 ) async {
471 final FlutterView greenView = tester.view;
472 final Key greenKey = UniqueKey();
473 final FlutterView redView = FakeView(tester.view);
474 final Key redKey = UniqueKey();
475 final Widget globalKeyChild = SizedBox(key: GlobalKey());
476
477 await tester.pumpWidget(
478 wrapWithView: false,
479 ViewCollection(
480 views: <Widget>[
481 View(key: greenKey, view: greenView, child: const ColoredBox(color: Colors.green)),
482 View(
483 key: redKey,
484 view: redView,
485 child: ColoredBox(color: Colors.red, child: globalKeyChild),
486 ),
487 ],
488 ),
489 );
490 expect(
491 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
492 findsOneWidget,
493 );
494 expect(
495 find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)),
496 findsNothing,
497 );
498 final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
499
500 // Move the child and remove its view.
501 await tester.pumpWidget(
502 wrapWithView: false,
503 ViewCollection(
504 views: <Widget>[
505 View(
506 key: greenKey,
507 view: greenView,
508 child: ColoredBox(color: Colors.green, child: globalKeyChild),
509 ),
510 ],
511 ),
512 );
513
514 expect(findsColoredBox(Colors.red), findsNothing);
515 expect(
516 find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)),
517 findsOneWidget,
518 );
519 expect(tester.renderObject(find.byKey(globalKeyChild.key!)), equals(boxWithGlobalKey));
520 });
521
522 testWidgets('Can move stuff out of a view that is going away, viewB -> ViewA', (
523 WidgetTester tester,
524 ) async {
525 final FlutterView greenView = tester.view;
526 final Key greenKey = UniqueKey();
527 final FlutterView redView = FakeView(tester.view);
528 final Key redKey = UniqueKey();
529 final Widget globalKeyChild = SizedBox(key: GlobalKey());
530
531 await tester.pumpWidget(
532 wrapWithView: false,
533 ViewCollection(
534 views: <Widget>[
535 View(
536 key: greenKey,
537 view: greenView,
538 child: ColoredBox(color: Colors.green, child: globalKeyChild),
539 ),
540 View(key: redKey, view: redView, child: const ColoredBox(color: Colors.red)),
541 ],
542 ),
543 );
544 expect(
545 find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)),
546 findsOneWidget,
547 );
548 expect(
549 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
550 findsNothing,
551 );
552 final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
553
554 // Move the child and remove its view.
555 await tester.pumpWidget(
556 wrapWithView: false,
557 ViewCollection(
558 views: <Widget>[
559 View(
560 key: redKey,
561 view: redView,
562 child: ColoredBox(color: Colors.red, child: globalKeyChild),
563 ),
564 ],
565 ),
566 );
567
568 expect(findsColoredBox(Colors.green), findsNothing);
569 expect(
570 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
571 findsOneWidget,
572 );
573 expect(tester.renderObject(find.byKey(globalKeyChild.key!)), equals(boxWithGlobalKey));
574 });
575
576 testWidgets('Can move stuff out of a view that is moving itself, stuff ends up before view', (
577 WidgetTester tester,
578 ) async {
579 final Key key1 = UniqueKey();
580 final Key key2 = UniqueKey();
581 final Key key3 = UniqueKey();
582 final Key key4 = UniqueKey();
583
584 final GlobalKey viewKey = GlobalKey();
585 final GlobalKey childKey = GlobalKey();
586
587 await tester.pumpWidget(
588 Column(
589 children: <Widget>[
590 SizedBox(key: key1),
591 ViewAnchor(
592 key: key2,
593 view: View(
594 key: viewKey,
595 view: FakeView(tester.view),
596 child: SizedBox(child: ColoredBox(key: childKey, color: Colors.green)),
597 ),
598 child: const SizedBox(),
599 ),
600 ViewAnchor(key: key3, child: const SizedBox()),
601 SizedBox(key: key4),
602 ],
603 ),
604 );
605
606 await tester.pumpWidget(
607 Column(
608 children: <Widget>[
609 SizedBox(key: key1, child: ColoredBox(key: childKey, color: Colors.green)),
610 ViewAnchor(key: key2, child: const SizedBox()),
611 ViewAnchor(
612 key: key3,
613 view: View(key: viewKey, view: FakeView(tester.view), child: const SizedBox()),
614 child: const SizedBox(),
615 ),
616 SizedBox(key: key4),
617 ],
618 ),
619 );
620
621 await tester.pumpWidget(
622 Column(
623 children: <Widget>[
624 SizedBox(key: key1),
625 ViewAnchor(
626 key: key2,
627 view: View(
628 key: viewKey,
629 view: FakeView(tester.view),
630 child: SizedBox(child: ColoredBox(key: childKey, color: Colors.green)),
631 ),
632 child: const SizedBox(),
633 ),
634 ViewAnchor(key: key3, child: const SizedBox()),
635 SizedBox(key: key4),
636 ],
637 ),
638 );
639 });
640
641 testWidgets('Can move stuff out of a view that is moving itself, stuff ends up after view', (
642 WidgetTester tester,
643 ) async {
644 final Key key1 = UniqueKey();
645 final Key key2 = UniqueKey();
646 final Key key3 = UniqueKey();
647 final Key key4 = UniqueKey();
648
649 final GlobalKey viewKey = GlobalKey();
650 final GlobalKey childKey = GlobalKey();
651
652 await tester.pumpWidget(
653 Column(
654 children: <Widget>[
655 SizedBox(key: key1),
656 ViewAnchor(
657 key: key2,
658 view: View(
659 key: viewKey,
660 view: FakeView(tester.view),
661 child: SizedBox(child: ColoredBox(key: childKey, color: Colors.green)),
662 ),
663 child: const SizedBox(),
664 ),
665 ViewAnchor(key: key3, child: const SizedBox()),
666 SizedBox(key: key4),
667 ],
668 ),
669 );
670
671 await tester.pumpWidget(
672 Column(
673 children: <Widget>[
674 SizedBox(key: key1),
675 ViewAnchor(key: key2, child: const SizedBox()),
676 ViewAnchor(
677 key: key3,
678 view: View(key: viewKey, view: FakeView(tester.view), child: const SizedBox()),
679 child: const SizedBox(),
680 ),
681 SizedBox(key: key4, child: ColoredBox(key: childKey, color: Colors.green)),
682 ],
683 ),
684 );
685
686 await tester.pumpWidget(
687 Column(
688 children: <Widget>[
689 SizedBox(key: key1),
690 ViewAnchor(
691 key: key2,
692 view: View(
693 key: viewKey,
694 view: FakeView(tester.view),
695 child: SizedBox(child: ColoredBox(key: childKey, color: Colors.green)),
696 ),
697 child: const SizedBox(),
698 ),
699 ViewAnchor(key: key3, child: const SizedBox()),
700 SizedBox(key: key4),
701 ],
702 ),
703 );
704 });
705
706 testWidgets('Can globalkey move down the tree from a view that is going away', (
707 WidgetTester tester,
708 ) async {
709 final FlutterView anchorView = FakeView(tester.view);
710 final Widget globalKeyChild = SizedBox(key: GlobalKey());
711
712 await tester.pumpWidget(
713 ColoredBox(
714 color: Colors.green,
715 child: ViewAnchor(
716 view: View(
717 view: anchorView,
718 child: ColoredBox(color: Colors.yellow, child: globalKeyChild),
719 ),
720 child: const ColoredBox(color: Colors.red),
721 ),
722 ),
723 );
724
725 expect(findsColoredBox(Colors.green), findsOneWidget);
726 expect(findsColoredBox(Colors.yellow), findsOneWidget);
727 expect(
728 find.descendant(of: findsColoredBox(Colors.yellow), matching: find.byType(SizedBox)),
729 findsOneWidget,
730 );
731 expect(findsColoredBox(Colors.red), findsOneWidget);
732 expect(
733 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
734 findsNothing,
735 );
736 expect(find.byType(SizedBox), findsOneWidget);
737 final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!));
738
739 await tester.pumpWidget(
740 ColoredBox(
741 color: Colors.green,
742 child: ViewAnchor(child: ColoredBox(color: Colors.red, child: globalKeyChild)),
743 ),
744 );
745 expect(findsColoredBox(Colors.green), findsOneWidget);
746 expect(findsColoredBox(Colors.yellow), findsNothing);
747 expect(
748 find.descendant(of: findsColoredBox(Colors.yellow), matching: find.byType(SizedBox)),
749 findsNothing,
750 );
751 expect(findsColoredBox(Colors.red), findsOneWidget);
752 expect(
753 find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)),
754 findsOneWidget,
755 );
756 expect(find.byType(SizedBox), findsOneWidget);
757 expect(tester.renderObject(find.byKey(globalKeyChild.key!)), boxWithGlobalKey);
758 });
759
760 testWidgets('RenderObjects are disposed when a view goes away from a ViewAnchor', (
761 WidgetTester tester,
762 ) async {
763 final FlutterView anchorView = FakeView(tester.view);
764
765 await tester.pumpWidget(
766 ColoredBox(
767 color: Colors.green,
768 child: ViewAnchor(
769 view: View(view: anchorView, child: const ColoredBox(color: Colors.yellow)),
770 child: const ColoredBox(color: Colors.red),
771 ),
772 ),
773 );
774
775 final RenderObject box = tester.renderObject(findsColoredBox(Colors.yellow));
776
777 await tester.pumpWidget(
778 const ColoredBox(
779 color: Colors.green,
780 child: ViewAnchor(child: ColoredBox(color: Colors.red)),
781 ),
782 );
783
784 expect(box.debugDisposed, isTrue);
785 });
786
787 testWidgets('RenderObjects are disposed when a view goes away from a ViewCollection', (
788 WidgetTester tester,
789 ) async {
790 final FlutterView redView = tester.view;
791 final FlutterView greenView = FakeView(tester.view);
792
793 await tester.pumpWidget(
794 wrapWithView: false,
795 ViewCollection(
796 views: <Widget>[
797 View(view: redView, child: const ColoredBox(color: Colors.red)),
798 View(view: greenView, child: const ColoredBox(color: Colors.green)),
799 ],
800 ),
801 );
802
803 expect(findsColoredBox(Colors.green), findsOneWidget);
804 expect(findsColoredBox(Colors.red), findsOneWidget);
805 final RenderObject box = tester.renderObject(findsColoredBox(Colors.green));
806
807 await tester.pumpWidget(
808 wrapWithView: false,
809 ViewCollection(
810 views: <Widget>[View(view: redView, child: const ColoredBox(color: Colors.red))],
811 ),
812 );
813
814 expect(findsColoredBox(Colors.green), findsNothing);
815 expect(findsColoredBox(Colors.red), findsOneWidget);
816 expect(box.debugDisposed, isTrue);
817 });
818
819 testWidgets('View can be wrapped and unwrapped', (WidgetTester tester) async {
820 final Widget view = View(view: tester.view, child: const SizedBox());
821
822 await tester.pumpWidget(wrapWithView: false, view);
823
824 final RenderObject renderView = tester.renderObject(find.byType(View));
825 final RenderObject renderSizedBox = tester.renderObject(find.byType(SizedBox));
826
827 await tester.pumpWidget(wrapWithView: false, ViewCollection(views: <Widget>[view]));
828
829 expect(tester.renderObject(find.byType(View)), same(renderView));
830 expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox));
831
832 await tester.pumpWidget(wrapWithView: false, view);
833
834 expect(tester.renderObject(find.byType(View)), same(renderView));
835 expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox));
836 });
837
838 testWidgets('ViewAnchor with View can be wrapped and unwrapped', (WidgetTester tester) async {
839 final Widget viewAnchor = ViewAnchor(
840 view: View(view: FakeView(tester.view), child: const SizedBox()),
841 child: const ColoredBox(color: Colors.green),
842 );
843
844 await tester.pumpWidget(viewAnchor);
845
846 final List<RenderObject> renderViews = tester.renderObjectList(find.byType(View)).toList();
847 final RenderObject renderSizedBox = tester.renderObject(find.byType(SizedBox));
848
849 await tester.pumpWidget(ColoredBox(color: Colors.yellow, child: viewAnchor));
850
851 expect(tester.renderObjectList(find.byType(View)), renderViews);
852 expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox));
853
854 await tester.pumpWidget(viewAnchor);
855
856 expect(tester.renderObjectList(find.byType(View)), renderViews);
857 expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox));
858 });
859
860 testWidgets('Moving a View keeps its semantics tree stable', (WidgetTester tester) async {
861 final Widget view = View(
862 // No explicit key, we rely on the implicit key of the underlying RawView.
863 view: tester.view,
864 child: Semantics(textDirection: TextDirection.ltr, label: 'Hello', child: const SizedBox()),
865 );
866 await tester.pumpWidget(wrapWithView: false, view);
867
868 final RenderObject renderSemantics = tester.renderObject(find.bySemanticsLabel('Hello'));
869 final SemanticsNode semantics = tester.getSemantics(find.bySemanticsLabel('Hello'));
870 expect(semantics.id, 1);
871 expect(renderSemantics.debugSemantics, same(semantics));
872
873 await tester.pumpWidget(wrapWithView: false, ViewCollection(views: <Widget>[view]));
874
875 final RenderObject renderSemanticsAfterMove = tester.renderObject(
876 find.bySemanticsLabel('Hello'),
877 );
878 final SemanticsNode semanticsAfterMove = tester.getSemantics(find.bySemanticsLabel('Hello'));
879 expect(renderSemanticsAfterMove, same(renderSemantics));
880 expect(semanticsAfterMove.id, 1);
881 expect(semanticsAfterMove, same(semantics));
882 });
883}
884
885Finder findsColoredBox(Color color) {
886 return find.byWidgetPredicate((Widget widget) => widget is ColoredBox && widget.color == color);
887}
888

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com