| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:ui'; |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; |
| |
| import 'multi_view_testing.dart'; |
| |
| void main() { |
| testWidgets('Providing a RenderObjectWidget directly to the RootWidget fails', ( |
| WidgetTester tester, |
| ) async { |
| // No render tree exists to attach the RenderObjectWidget to. |
| await tester.pumpWidget(wrapWithView: false, const ColoredBox(color: Colors.red)); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| startsWith( |
| 'The render object for ColoredBox cannot find ancestor render object to attach to.', |
| ), |
| ), |
| ); |
| }); |
| |
| testWidgets('Moving a RenderObjectWidget to the RootWidget via GlobalKey fails', ( |
| WidgetTester tester, |
| ) async { |
| final Widget globalKeyedWidget = ColoredBox(key: GlobalKey(), color: Colors.red); |
| |
| await tester.pumpWidget(wrapWithView: false, View(view: tester.view, child: globalKeyedWidget)); |
| expect(tester.takeException(), isNull); |
| |
| await tester.pumpWidget(wrapWithView: false, globalKeyedWidget); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| contains('cannot find ancestor render object to attach to.'), |
| ), |
| ); |
| }); |
| |
| testWidgets( |
| 'A View cannot be a child of a render object widget', |
| experimentalLeakTesting: |
| LeakTesting.settings.withIgnoredAll(), // leaking by design because of exception |
| (WidgetTester tester) async { |
| await tester.pumpWidget(Center(child: View(view: FakeView(tester.view), child: Container()))); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| contains('cannot maintain an independent render tree at its current location.'), |
| ), |
| ); |
| }, |
| ); |
| |
| testWidgets( |
| 'The child of a ViewAnchor cannot be a View', |
| experimentalLeakTesting: |
| LeakTesting.settings.withIgnoredAll(), // leaking by design because of exception |
| (WidgetTester tester) async { |
| await tester.pumpWidget( |
| ViewAnchor(child: View(view: FakeView(tester.view), child: Container())), |
| ); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| contains('cannot maintain an independent render tree at its current location.'), |
| ), |
| ); |
| }, |
| ); |
| |
| testWidgets('A View can not be moved via GlobalKey to be a child of a RenderObject', ( |
| WidgetTester tester, |
| ) async { |
| final Widget globalKeyedView = View( |
| key: GlobalKey(), |
| view: FakeView(tester.view), |
| child: const ColoredBox(color: Colors.red), |
| ); |
| |
| await tester.pumpWidget(wrapWithView: false, globalKeyedView); |
| expect(tester.takeException(), isNull); |
| |
| await tester.pumpWidget(wrapWithView: false, View(view: tester.view, child: globalKeyedView)); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| contains('cannot maintain an independent render tree at its current location.'), |
| ), |
| ); |
| }); |
| |
| testWidgets('The view property of a ViewAnchor cannot be a render object widget', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| ViewAnchor(view: const ColoredBox(color: Colors.red), child: Container()), |
| ); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| startsWith( |
| 'The render object for ColoredBox cannot find ancestor render object to attach to.', |
| ), |
| ), |
| ); |
| }); |
| |
| testWidgets( |
| 'A RenderObject cannot be moved into the view property of a ViewAnchor via GlobalKey', |
| (WidgetTester tester) async { |
| final Widget globalKeyedWidget = ColoredBox(key: GlobalKey(), color: Colors.red); |
| |
| await tester.pumpWidget(ViewAnchor(child: globalKeyedWidget)); |
| expect(tester.takeException(), isNull); |
| |
| await tester.pumpWidget(ViewAnchor(view: globalKeyedWidget, child: const SizedBox())); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| contains('cannot find ancestor render object to attach to.'), |
| ), |
| ); |
| }, |
| ); |
| |
| testWidgets('ViewAnchor cannot be used at the top of the widget tree (outside of View)', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget(wrapWithView: false, const ViewAnchor(child: SizedBox())); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| startsWith( |
| 'The render object for SizedBox cannot find ancestor render object to attach to.', |
| ), |
| ), |
| ); |
| }); |
| |
| testWidgets( |
| 'ViewAnchor cannot be moved to the top of the widget tree (outside of View) via GlobalKey', |
| (WidgetTester tester) async { |
| final Widget globalKeyedViewAnchor = ViewAnchor(key: GlobalKey(), child: const SizedBox()); |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| View(view: tester.view, child: globalKeyedViewAnchor), |
| ); |
| expect(tester.takeException(), isNull); |
| |
| await tester.pumpWidget(wrapWithView: false, globalKeyedViewAnchor); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| contains('cannot find ancestor render object to attach to.'), |
| ), |
| ); |
| }, |
| ); |
| |
| testWidgets('View can be used at the top of the widget tree', (WidgetTester tester) async { |
| await tester.pumpWidget(wrapWithView: false, View(view: tester.view, child: Container())); |
| |
| expect(tester.takeException(), isNull); |
| }); |
| |
| testWidgets('View can be moved to the top of the widget tree view GlobalKey', ( |
| WidgetTester tester, |
| ) async { |
| final Widget globalKeyView = View( |
| view: FakeView(tester.view), |
| child: const ColoredBox(color: Colors.red), |
| ); |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| View( |
| view: tester.view, |
| child: ViewAnchor( |
| view: globalKeyView, // This one has trouble when deactivating |
| child: const SizedBox(), |
| ), |
| ), |
| ); |
| expect(tester.takeException(), isNull); |
| expect(find.byType(SizedBox), findsOneWidget); |
| expect(find.byType(ColoredBox), findsOneWidget); |
| |
| await tester.pumpWidget(wrapWithView: false, globalKeyView); |
| expect(tester.takeException(), isNull); |
| expect(find.byType(SizedBox), findsNothing); |
| expect(find.byType(ColoredBox), findsOneWidget); |
| }); |
| |
| testWidgets('ViewCollection can be used at the top of the widget tree', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection(views: <Widget>[View(view: tester.view, child: Container())]), |
| ); |
| |
| expect(tester.takeException(), isNull); |
| }); |
| |
| testWidgets('ViewCollection cannot be used inside a View', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| ViewCollection(views: <Widget>[View(view: FakeView(tester.view), child: Container())]), |
| ); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| startsWith( |
| 'The Element for ViewCollection cannot be inserted into slot "null" of its ancestor.', |
| ), |
| ), |
| ); |
| }); |
| |
| testWidgets('ViewCollection can be used as ViewAnchor.view', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| ViewAnchor( |
| view: ViewCollection( |
| views: <Widget>[View(view: FakeView(tester.view), child: Container())], |
| ), |
| child: Container(), |
| ), |
| ); |
| |
| expect(tester.takeException(), isNull); |
| }); |
| |
| testWidgets('ViewCollection cannot have render object widgets as children', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| wrapWithView: false, |
| const ViewCollection(views: <Widget>[ColoredBox(color: Colors.red)]), |
| ); |
| |
| expect( |
| tester.takeException(), |
| isFlutterError.having( |
| (FlutterError error) => error.message, |
| 'message', |
| startsWith( |
| 'The render object for ColoredBox cannot find ancestor render object to attach to.', |
| ), |
| ), |
| ); |
| }); |
| |
| testWidgets('Views can be moved in and out of ViewCollections via GlobalKey', ( |
| WidgetTester tester, |
| ) async { |
| final Widget greenView = View( |
| key: GlobalKey(debugLabel: 'green'), |
| view: tester.view, |
| child: const ColoredBox(color: Colors.green), |
| ); |
| final Widget redView = View( |
| key: GlobalKey(debugLabel: 'red'), |
| view: FakeView(tester.view), |
| child: const ColoredBox(color: Colors.red), |
| ); |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| greenView, |
| ViewCollection(views: <Widget>[redView]), |
| ], |
| ), |
| ); |
| expect(tester.takeException(), isNull); |
| expect(find.byType(ColoredBox), findsNWidgets(2)); |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| redView, |
| ViewCollection(views: <Widget>[greenView]), |
| ], |
| ), |
| ); |
| expect(tester.takeException(), isNull); |
| expect(find.byType(ColoredBox), findsNWidgets(2)); |
| }); |
| |
| testWidgets('Can move stuff between views via global key: viewA -> viewB', ( |
| WidgetTester tester, |
| ) async { |
| final FlutterView greenView = tester.view; |
| final FlutterView redView = FakeView(tester.view); |
| final Widget globalKeyChild = SizedBox(key: GlobalKey()); |
| |
| Map<int, RenderObject> collectLeafRenderObjects() { |
| final Map<int, RenderObject> result = <int, RenderObject>{}; |
| for (final RenderView renderView in RendererBinding.instance.renderViews) { |
| void visit(RenderObject object) { |
| result[renderView.flutterView.viewId] = object; |
| object.visitChildren(visit); |
| } |
| |
| visit(renderView); |
| } |
| return result; |
| } |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View(view: greenView, child: ColoredBox(color: Colors.green, child: globalKeyChild)), |
| View(view: redView, child: const ColoredBox(color: Colors.red)), |
| ], |
| ), |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsNothing, |
| ); |
| final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!)); |
| |
| Map<int, RenderObject> leafRenderObject = collectLeafRenderObjects(); |
| expect(leafRenderObject[greenView.viewId], isA<RenderConstrainedBox>()); |
| expect(leafRenderObject[redView.viewId], isNot(isA<RenderConstrainedBox>())); |
| |
| // Move the child. |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View(view: greenView, child: const ColoredBox(color: Colors.green)), |
| View(view: redView, child: ColoredBox(color: Colors.red, child: globalKeyChild)), |
| ], |
| ), |
| ); |
| |
| expect( |
| find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)), |
| findsNothing, |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect(tester.renderObject(find.byKey(globalKeyChild.key!)), equals(boxWithGlobalKey)); |
| |
| leafRenderObject = collectLeafRenderObjects(); |
| expect(leafRenderObject[greenView.viewId], isNot(isA<RenderConstrainedBox>())); |
| expect(leafRenderObject[redView.viewId], isA<RenderConstrainedBox>()); |
| }); |
| |
| testWidgets('Can move stuff between views via global key: viewB -> viewA', ( |
| WidgetTester tester, |
| ) async { |
| final FlutterView greenView = tester.view; |
| final FlutterView redView = FakeView(tester.view); |
| final Widget globalKeyChild = SizedBox(key: GlobalKey()); |
| |
| Map<int, RenderObject> collectLeafRenderObjects() { |
| final Map<int, RenderObject> result = <int, RenderObject>{}; |
| for (final RenderView renderView in RendererBinding.instance.renderViews) { |
| void visit(RenderObject object) { |
| result[renderView.flutterView.viewId] = object; |
| object.visitChildren(visit); |
| } |
| |
| visit(renderView); |
| } |
| return result; |
| } |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View(view: greenView, child: const ColoredBox(color: Colors.green)), |
| View(view: redView, child: ColoredBox(color: Colors.red, child: globalKeyChild)), |
| ], |
| ), |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)), |
| findsNothing, |
| ); |
| final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!)); |
| |
| Map<int, RenderObject> leafRenderObject = collectLeafRenderObjects(); |
| expect(leafRenderObject[redView.viewId], isA<RenderConstrainedBox>()); |
| expect(leafRenderObject[greenView.viewId], isNot(isA<RenderConstrainedBox>())); |
| |
| // Move the child. |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View(view: greenView, child: ColoredBox(color: Colors.green, child: globalKeyChild)), |
| View(view: redView, child: const ColoredBox(color: Colors.red)), |
| ], |
| ), |
| ); |
| |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsNothing, |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect(tester.renderObject(find.byKey(globalKeyChild.key!)), equals(boxWithGlobalKey)); |
| |
| leafRenderObject = collectLeafRenderObjects(); |
| expect(leafRenderObject[redView.viewId], isNot(isA<RenderConstrainedBox>())); |
| expect(leafRenderObject[greenView.viewId], isA<RenderConstrainedBox>()); |
| }); |
| |
| testWidgets('Can move stuff out of a view that is going away, viewA -> ViewB', ( |
| WidgetTester tester, |
| ) async { |
| final FlutterView greenView = tester.view; |
| final Key greenKey = UniqueKey(); |
| final FlutterView redView = FakeView(tester.view); |
| final Key redKey = UniqueKey(); |
| final Widget globalKeyChild = SizedBox(key: GlobalKey()); |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View(key: greenKey, view: greenView, child: const ColoredBox(color: Colors.green)), |
| View( |
| key: redKey, |
| view: redView, |
| child: ColoredBox(color: Colors.red, child: globalKeyChild), |
| ), |
| ], |
| ), |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)), |
| findsNothing, |
| ); |
| final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!)); |
| |
| // Move the child and remove its view. |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View( |
| key: greenKey, |
| view: greenView, |
| child: ColoredBox(color: Colors.green, child: globalKeyChild), |
| ), |
| ], |
| ), |
| ); |
| |
| expect(findsColoredBox(Colors.red), findsNothing); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect(tester.renderObject(find.byKey(globalKeyChild.key!)), equals(boxWithGlobalKey)); |
| }); |
| |
| testWidgets('Can move stuff out of a view that is going away, viewB -> ViewA', ( |
| WidgetTester tester, |
| ) async { |
| final FlutterView greenView = tester.view; |
| final Key greenKey = UniqueKey(); |
| final FlutterView redView = FakeView(tester.view); |
| final Key redKey = UniqueKey(); |
| final Widget globalKeyChild = SizedBox(key: GlobalKey()); |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View( |
| key: greenKey, |
| view: greenView, |
| child: ColoredBox(color: Colors.green, child: globalKeyChild), |
| ), |
| View(key: redKey, view: redView, child: const ColoredBox(color: Colors.red)), |
| ], |
| ), |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.green), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsNothing, |
| ); |
| final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!)); |
| |
| // Move the child and remove its view. |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View( |
| key: redKey, |
| view: redView, |
| child: ColoredBox(color: Colors.red, child: globalKeyChild), |
| ), |
| ], |
| ), |
| ); |
| |
| expect(findsColoredBox(Colors.green), findsNothing); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect(tester.renderObject(find.byKey(globalKeyChild.key!)), equals(boxWithGlobalKey)); |
| }); |
| |
| testWidgets('Can move stuff out of a view that is moving itself, stuff ends up before view', ( |
| WidgetTester tester, |
| ) async { |
| final Key key1 = UniqueKey(); |
| final Key key2 = UniqueKey(); |
| final Key key3 = UniqueKey(); |
| final Key key4 = UniqueKey(); |
| |
| final GlobalKey viewKey = GlobalKey(); |
| final GlobalKey childKey = GlobalKey(); |
| |
| await tester.pumpWidget( |
| Column( |
| children: <Widget>[ |
| SizedBox(key: key1), |
| ViewAnchor( |
| key: key2, |
| view: View( |
| key: viewKey, |
| view: FakeView(tester.view), |
| child: SizedBox(child: ColoredBox(key: childKey, color: Colors.green)), |
| ), |
| child: const SizedBox(), |
| ), |
| ViewAnchor(key: key3, child: const SizedBox()), |
| SizedBox(key: key4), |
| ], |
| ), |
| ); |
| |
| await tester.pumpWidget( |
| Column( |
| children: <Widget>[ |
| SizedBox(key: key1, child: ColoredBox(key: childKey, color: Colors.green)), |
| ViewAnchor(key: key2, child: const SizedBox()), |
| ViewAnchor( |
| key: key3, |
| view: View(key: viewKey, view: FakeView(tester.view), child: const SizedBox()), |
| child: const SizedBox(), |
| ), |
| SizedBox(key: key4), |
| ], |
| ), |
| ); |
| |
| await tester.pumpWidget( |
| Column( |
| children: <Widget>[ |
| SizedBox(key: key1), |
| ViewAnchor( |
| key: key2, |
| view: View( |
| key: viewKey, |
| view: FakeView(tester.view), |
| child: SizedBox(child: ColoredBox(key: childKey, color: Colors.green)), |
| ), |
| child: const SizedBox(), |
| ), |
| ViewAnchor(key: key3, child: const SizedBox()), |
| SizedBox(key: key4), |
| ], |
| ), |
| ); |
| }); |
| |
| testWidgets('Can move stuff out of a view that is moving itself, stuff ends up after view', ( |
| WidgetTester tester, |
| ) async { |
| final Key key1 = UniqueKey(); |
| final Key key2 = UniqueKey(); |
| final Key key3 = UniqueKey(); |
| final Key key4 = UniqueKey(); |
| |
| final GlobalKey viewKey = GlobalKey(); |
| final GlobalKey childKey = GlobalKey(); |
| |
| await tester.pumpWidget( |
| Column( |
| children: <Widget>[ |
| SizedBox(key: key1), |
| ViewAnchor( |
| key: key2, |
| view: View( |
| key: viewKey, |
| view: FakeView(tester.view), |
| child: SizedBox(child: ColoredBox(key: childKey, color: Colors.green)), |
| ), |
| child: const SizedBox(), |
| ), |
| ViewAnchor(key: key3, child: const SizedBox()), |
| SizedBox(key: key4), |
| ], |
| ), |
| ); |
| |
| await tester.pumpWidget( |
| Column( |
| children: <Widget>[ |
| SizedBox(key: key1), |
| ViewAnchor(key: key2, child: const SizedBox()), |
| ViewAnchor( |
| key: key3, |
| view: View(key: viewKey, view: FakeView(tester.view), child: const SizedBox()), |
| child: const SizedBox(), |
| ), |
| SizedBox(key: key4, child: ColoredBox(key: childKey, color: Colors.green)), |
| ], |
| ), |
| ); |
| |
| await tester.pumpWidget( |
| Column( |
| children: <Widget>[ |
| SizedBox(key: key1), |
| ViewAnchor( |
| key: key2, |
| view: View( |
| key: viewKey, |
| view: FakeView(tester.view), |
| child: SizedBox(child: ColoredBox(key: childKey, color: Colors.green)), |
| ), |
| child: const SizedBox(), |
| ), |
| ViewAnchor(key: key3, child: const SizedBox()), |
| SizedBox(key: key4), |
| ], |
| ), |
| ); |
| }); |
| |
| testWidgets('Can globalkey move down the tree from a view that is going away', ( |
| WidgetTester tester, |
| ) async { |
| final FlutterView anchorView = FakeView(tester.view); |
| final Widget globalKeyChild = SizedBox(key: GlobalKey()); |
| |
| await tester.pumpWidget( |
| ColoredBox( |
| color: Colors.green, |
| child: ViewAnchor( |
| view: View( |
| view: anchorView, |
| child: ColoredBox(color: Colors.yellow, child: globalKeyChild), |
| ), |
| child: const ColoredBox(color: Colors.red), |
| ), |
| ), |
| ); |
| |
| expect(findsColoredBox(Colors.green), findsOneWidget); |
| expect(findsColoredBox(Colors.yellow), findsOneWidget); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.yellow), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect(findsColoredBox(Colors.red), findsOneWidget); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsNothing, |
| ); |
| expect(find.byType(SizedBox), findsOneWidget); |
| final RenderObject boxWithGlobalKey = tester.renderObject(find.byKey(globalKeyChild.key!)); |
| |
| await tester.pumpWidget( |
| ColoredBox( |
| color: Colors.green, |
| child: ViewAnchor(child: ColoredBox(color: Colors.red, child: globalKeyChild)), |
| ), |
| ); |
| expect(findsColoredBox(Colors.green), findsOneWidget); |
| expect(findsColoredBox(Colors.yellow), findsNothing); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.yellow), matching: find.byType(SizedBox)), |
| findsNothing, |
| ); |
| expect(findsColoredBox(Colors.red), findsOneWidget); |
| expect( |
| find.descendant(of: findsColoredBox(Colors.red), matching: find.byType(SizedBox)), |
| findsOneWidget, |
| ); |
| expect(find.byType(SizedBox), findsOneWidget); |
| expect(tester.renderObject(find.byKey(globalKeyChild.key!)), boxWithGlobalKey); |
| }); |
| |
| testWidgets('RenderObjects are disposed when a view goes away from a ViewAnchor', ( |
| WidgetTester tester, |
| ) async { |
| final FlutterView anchorView = FakeView(tester.view); |
| |
| await tester.pumpWidget( |
| ColoredBox( |
| color: Colors.green, |
| child: ViewAnchor( |
| view: View(view: anchorView, child: const ColoredBox(color: Colors.yellow)), |
| child: const ColoredBox(color: Colors.red), |
| ), |
| ), |
| ); |
| |
| final RenderObject box = tester.renderObject(findsColoredBox(Colors.yellow)); |
| |
| await tester.pumpWidget( |
| const ColoredBox( |
| color: Colors.green, |
| child: ViewAnchor(child: ColoredBox(color: Colors.red)), |
| ), |
| ); |
| |
| expect(box.debugDisposed, isTrue); |
| }); |
| |
| testWidgets('RenderObjects are disposed when a view goes away from a ViewCollection', ( |
| WidgetTester tester, |
| ) async { |
| final FlutterView redView = tester.view; |
| final FlutterView greenView = FakeView(tester.view); |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[ |
| View(view: redView, child: const ColoredBox(color: Colors.red)), |
| View(view: greenView, child: const ColoredBox(color: Colors.green)), |
| ], |
| ), |
| ); |
| |
| expect(findsColoredBox(Colors.green), findsOneWidget); |
| expect(findsColoredBox(Colors.red), findsOneWidget); |
| final RenderObject box = tester.renderObject(findsColoredBox(Colors.green)); |
| |
| await tester.pumpWidget( |
| wrapWithView: false, |
| ViewCollection( |
| views: <Widget>[View(view: redView, child: const ColoredBox(color: Colors.red))], |
| ), |
| ); |
| |
| expect(findsColoredBox(Colors.green), findsNothing); |
| expect(findsColoredBox(Colors.red), findsOneWidget); |
| expect(box.debugDisposed, isTrue); |
| }); |
| |
| testWidgets('View can be wrapped and unwrapped', (WidgetTester tester) async { |
| final Widget view = View(view: tester.view, child: const SizedBox()); |
| |
| await tester.pumpWidget(wrapWithView: false, view); |
| |
| final RenderObject renderView = tester.renderObject(find.byType(View)); |
| final RenderObject renderSizedBox = tester.renderObject(find.byType(SizedBox)); |
| |
| await tester.pumpWidget(wrapWithView: false, ViewCollection(views: <Widget>[view])); |
| |
| expect(tester.renderObject(find.byType(View)), same(renderView)); |
| expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox)); |
| |
| await tester.pumpWidget(wrapWithView: false, view); |
| |
| expect(tester.renderObject(find.byType(View)), same(renderView)); |
| expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox)); |
| }); |
| |
| testWidgets('ViewAnchor with View can be wrapped and unwrapped', (WidgetTester tester) async { |
| final Widget viewAnchor = ViewAnchor( |
| view: View(view: FakeView(tester.view), child: const SizedBox()), |
| child: const ColoredBox(color: Colors.green), |
| ); |
| |
| await tester.pumpWidget(viewAnchor); |
| |
| final List<RenderObject> renderViews = tester.renderObjectList(find.byType(View)).toList(); |
| final RenderObject renderSizedBox = tester.renderObject(find.byType(SizedBox)); |
| |
| await tester.pumpWidget(ColoredBox(color: Colors.yellow, child: viewAnchor)); |
| |
| expect(tester.renderObjectList(find.byType(View)), renderViews); |
| expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox)); |
| |
| await tester.pumpWidget(viewAnchor); |
| |
| expect(tester.renderObjectList(find.byType(View)), renderViews); |
| expect(tester.renderObject(find.byType(SizedBox)), same(renderSizedBox)); |
| }); |
| |
| testWidgets('Moving a View keeps its semantics tree stable', (WidgetTester tester) async { |
| final Widget view = View( |
| // No explicit key, we rely on the implicit key of the underlying RawView. |
| view: tester.view, |
| child: Semantics(textDirection: TextDirection.ltr, label: 'Hello', child: const SizedBox()), |
| ); |
| await tester.pumpWidget(wrapWithView: false, view); |
| |
| final RenderObject renderSemantics = tester.renderObject(find.bySemanticsLabel('Hello')); |
| final SemanticsNode semantics = tester.getSemantics(find.bySemanticsLabel('Hello')); |
| expect(semantics.id, 1); |
| expect(renderSemantics.debugSemantics, same(semantics)); |
| |
| await tester.pumpWidget(wrapWithView: false, ViewCollection(views: <Widget>[view])); |
| |
| final RenderObject renderSemanticsAfterMove = tester.renderObject( |
| find.bySemanticsLabel('Hello'), |
| ); |
| final SemanticsNode semanticsAfterMove = tester.getSemantics(find.bySemanticsLabel('Hello')); |
| expect(renderSemanticsAfterMove, same(renderSemantics)); |
| expect(semanticsAfterMove.id, 1); |
| expect(semanticsAfterMove, same(semantics)); |
| }); |
| } |
| |
| Finder findsColoredBox(Color color) { |
| return find.byWidgetPredicate((Widget widget) => widget is ColoredBox && widget.color == color); |
| } |