blob: a5b1cdc52b27d2f2a907061a7e4d2dd74d54e836 [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 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Can size according to aspect ratio', (WidgetTester tester) async {
final Key outside = UniqueKey();
final Key inside = UniqueKey();
await tester.pumpWidget(
Center(
child: SizedBox(
width: 200.0,
child: FittedBox(
key: outside,
child: SizedBox(
key: inside,
width: 100.0,
height: 50.0,
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 200.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 100.0);
expect(insideBox.size.height, 50.0);
final Offset insidePoint = insideBox.localToGlobal(const Offset(100.0, 50.0));
final Offset outsidePoint = outsideBox.localToGlobal(const Offset(200.0, 100.0));
expect(outsidePoint, equals(const Offset(500.0, 350.0)));
expect(insidePoint, equals(outsidePoint));
});
testWidgets('Can contain child', (WidgetTester tester) async {
final Key outside = UniqueKey();
final Key inside = UniqueKey();
await tester.pumpWidget(
Center(
child: SizedBox(
width: 200.0,
height: 200.0,
child: FittedBox(
key: outside,
child: SizedBox(
key: inside,
width: 100.0,
height: 50.0,
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 200.0);
expect(outsideBox.size.height, 200.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 100.0);
expect(insideBox.size.height, 50.0);
final Offset insidePoint = insideBox.localToGlobal(const Offset(100.0, 0.0));
final Offset outsidePoint = outsideBox.localToGlobal(const Offset(200.0, 50.0));
expect(insidePoint, equals(outsidePoint));
});
testWidgets('Child can cover', (WidgetTester tester) async {
final Key outside = UniqueKey();
final Key inside = UniqueKey();
await tester.pumpWidget(
Center(
child: SizedBox(
width: 200.0,
height: 200.0,
child: FittedBox(
key: outside,
fit: BoxFit.cover,
child: SizedBox(
key: inside,
width: 100.0,
height: 50.0,
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 200.0);
expect(outsideBox.size.height, 200.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 100.0);
expect(insideBox.size.height, 50.0);
final Offset insidePoint = insideBox.localToGlobal(const Offset(50.0, 25.0));
final Offset outsidePoint = outsideBox.localToGlobal(const Offset(100.0, 100.0));
expect(insidePoint, equals(outsidePoint));
});
testWidgets('FittedBox with no child', (WidgetTester tester) async {
final Key key = UniqueKey();
await tester.pumpWidget(
Center(
child: FittedBox(
key: key,
fit: BoxFit.cover,
),
),
);
final RenderBox box = tester.firstRenderObject(find.byKey(key));
expect(box.size.width, 0.0);
expect(box.size.height, 0.0);
});
testWidgets('Child can be aligned multiple ways in a row', (WidgetTester tester) async {
final Key outside = UniqueKey();
final Key inside = UniqueKey();
{ // align RTL
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.rtl,
child: Center(
child: SizedBox(
width: 100.0,
height: 100.0,
child: FittedBox(
key: outside,
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.bottomEnd,
child: SizedBox(
key: inside,
width: 10.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 10.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(Offset.zero);
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(0.0, 90.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(10.0, 100.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
{ // change direction
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: SizedBox(
width: 100.0,
height: 100.0,
child: FittedBox(
key: outside,
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.bottomEnd,
child: SizedBox(
key: inside,
width: 10.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 10.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(Offset.zero);
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(90.0, 90.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(100.0, 100.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
{ // change alignment
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: SizedBox(
width: 100.0,
height: 100.0,
child: FittedBox(
key: outside,
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.center,
child: SizedBox(
key: inside,
width: 10.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 10.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(Offset.zero);
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(45.0, 45.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(10.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(55.0, 55.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
{ // change size
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: SizedBox(
width: 100.0,
height: 100.0,
child: FittedBox(
key: outside,
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.center,
child: SizedBox(
key: inside,
width: 30.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 30.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(Offset.zero);
final Offset outsideTopLeft = outsideBox.localToGlobal(const Offset(35.0, 45.0));
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(30.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(65.0, 55.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
{ // change fit
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: SizedBox(
width: 100.0,
height: 100.0,
child: FittedBox(
key: outside,
fit: BoxFit.fill,
alignment: AlignmentDirectional.center,
child: SizedBox(
key: inside,
width: 30.0,
height: 10.0,
),
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.width, 100.0);
expect(outsideBox.size.height, 100.0);
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(insideBox.size.width, 30.0);
expect(insideBox.size.height, 10.0);
final Offset insideTopLeft = insideBox.localToGlobal(Offset.zero);
final Offset outsideTopLeft = outsideBox.localToGlobal(Offset.zero);
final Offset insideBottomRight = insideBox.localToGlobal(const Offset(30.0, 10.0));
final Offset outsideBottomRight = outsideBox.localToGlobal(const Offset(100.0, 100.0));
expect(insideTopLeft, equals(outsideTopLeft));
expect(insideBottomRight, equals(outsideBottomRight));
}
});
testWidgets('FittedBox layers - contain', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: SizedBox(
width: 100.0,
height: 10.0,
child: FittedBox(
child: SizedBox(
width: 50.0,
height: 50.0,
child: RepaintBoundary(
child: Placeholder(),
),
),
),
),
),
);
expect(getLayers(), <Type>[TransformLayer, TransformLayer, OffsetLayer]);
});
testWidgets('FittedBox layers - cover - horizontal', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: SizedBox(
width: 100.0,
height: 10.0,
child: FittedBox(
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: 10.0,
height: 50.0,
child: RepaintBoundary(
child: Placeholder(),
),
),
),
),
),
);
expect(getLayers(), <Type>[TransformLayer, ClipRectLayer, TransformLayer, OffsetLayer]);
});
testWidgets('FittedBox layers - cover - vertical', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: SizedBox(
width: 10.0,
height: 100.0,
child: FittedBox(
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: 50.0,
height: 10.0,
child: RepaintBoundary(
child: Placeholder(),
),
),
),
),
),
);
expect(getLayers(), <Type>[TransformLayer, ClipRectLayer, TransformLayer, OffsetLayer]);
});
testWidgets('FittedBox layers - none - clip', (WidgetTester tester) async {
final List<double> values = <double>[10.0, 50.0, 100.0];
for (final double a in values) {
for (final double b in values) {
for (final double c in values) {
for (final double d in values) {
await tester.pumpWidget(
Center(
child: SizedBox(
width: a,
height: b,
child: FittedBox(
fit: BoxFit.none,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: c,
height: d,
child: const RepaintBoundary(
child: Placeholder(),
),
),
),
),
),
);
if (a < c || b < d) {
expect(getLayers(), <Type>[TransformLayer, ClipRectLayer, OffsetLayer]);
} else {
expect(getLayers(), <Type>[TransformLayer, OffsetLayer]);
}
}
}
}
}
});
testWidgets('Big child into small fitted box - hit testing', (WidgetTester tester) async {
final GlobalKey key1 = GlobalKey();
bool _pointerDown = false;
await tester.pumpWidget(
Center(
child: SizedBox(
width: 100.0,
height: 100.0,
child: FittedBox(
alignment: FractionalOffset.center,
child: SizedBox(
width: 1000.0,
height: 1000.0,
child: Listener(
onPointerDown: (PointerDownEvent event) {
_pointerDown = true;
},
child: Container(
key: key1,
color: const Color(0xFF000000),
),
),
),
),
),
),
);
expect(_pointerDown, isFalse);
await tester.tap(find.byKey(key1));
expect(_pointerDown, isTrue);
});
testWidgets('Can set and update clipBehavior', (WidgetTester tester) async {
await tester.pumpWidget(FittedBox(fit: BoxFit.none, child: Container()));
final RenderFittedBox renderObject = tester.allRenderObjects.whereType<RenderFittedBox>().first;
expect(renderObject.clipBehavior, equals(Clip.none));
await tester.pumpWidget(FittedBox(fit: BoxFit.none, clipBehavior: Clip.antiAlias, child: Container()));
expect(renderObject.clipBehavior, equals(Clip.antiAlias));
});
testWidgets('BoxFit.scaleDown matches size of child', (WidgetTester tester) async {
final Key outside = UniqueKey();
final Key inside = UniqueKey();
// Does not scale up when child is smaller than constraints
await tester.pumpWidget(
Center(
child: SizedBox(
width: 200.0,
child: FittedBox(
key: outside,
fit: BoxFit.scaleDown,
child: SizedBox(
key: inside,
width: 100.0,
height: 50.0,
),
),
),
),
);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
final RenderBox insideBox = tester.firstRenderObject(find.byKey(inside));
expect(outsideBox.size.width, 200.0);
expect(outsideBox.size.height, 50.0);
Offset outsidePoint = outsideBox.localToGlobal(Offset.zero);
Offset insidePoint = insideBox.localToGlobal(Offset.zero);
expect(insidePoint - outsidePoint, equals(const Offset(50.0, 0.0)));
// Scales down when child is bigger than constraints
await tester.pumpWidget(
Center(
child: SizedBox(
width: 200.0,
child: FittedBox(
key: outside,
fit: BoxFit.scaleDown,
child: SizedBox(
key: inside,
width: 400.0,
height: 200.0,
),
),
),
),
);
expect(outsideBox.size.width, 200.0);
expect(outsideBox.size.height, 100.0);
outsidePoint = outsideBox.localToGlobal(Offset.zero);
insidePoint = insideBox.localToGlobal(Offset.zero);
expect(insidePoint - outsidePoint, equals(Offset.zero));
});
testWidgets('Switching to and from BoxFit.scaleDown causes relayout', (WidgetTester tester) async {
final Key outside = UniqueKey();
final Widget scaleDownWidget = Center(
child: SizedBox(
width: 200.0,
child: FittedBox(
key: outside,
fit: BoxFit.scaleDown,
child: const SizedBox(
width: 100.0,
height: 50.0,
),
),
),
);
final Widget coverWidget = Center(
child: SizedBox(
width: 200.0,
child: FittedBox(
key: outside,
child: const SizedBox(
width: 100.0,
height: 50.0,
),
),
),
);
await tester.pumpWidget(scaleDownWidget);
final RenderBox outsideBox = tester.firstRenderObject(find.byKey(outside));
expect(outsideBox.size.height, 50.0);
await tester.pumpWidget(coverWidget);
expect(outsideBox.size.height, 100.0);
await tester.pumpWidget(scaleDownWidget);
expect(outsideBox.size.height, 50.0);
});
testWidgets('FittedBox without child does not throw', (WidgetTester tester) async {
await tester.pumpWidget(
const Center(
child: SizedBox(
width: 200.0,
height: 200.0,
child: FittedBox(),
),
),
);
expect(find.byType(FittedBox), findsOneWidget);
// Tapping it also should not throw.
await tester.tap(find.byType(FittedBox), warnIfMissed: false);
expect(tester.takeException(), isNull);
});
}
List<Type> getLayers() {
final List<Type> layers = <Type>[];
Layer? container = RendererBinding.instance!.renderView.debugLayer;
while (container is ContainerLayer) {
layers.add(container.runtimeType);
expect(container.firstChild, same(container.lastChild));
container = container.firstChild;
}
return layers;
}