blob: 934342c1bdf1349777d5079f00532aab2d1d6fb9 [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);
}