blob: c89742b04bc4bb419040a4688bed9635f20d6fc5 [file] [log] [blame]
// 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);
}