blob: 4e18b415368f74796d94ed98df5442f9d5a35e6c [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/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
final LayerLink link = LayerLink();
testWidgets('Change link during layout', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
Widget build({ LayerLink? linkToUse }) {
return Directionality(
textDirection: TextDirection.ltr,
// The LayoutBuilder forces the CompositedTransformTarget widget to
// access its own size when [RenderObject.debugActiveLayout] is
// non-null.
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Stack(
children: <Widget>[
Positioned(
left: 123.0,
top: 456.0,
child: CompositedTransformTarget(
link: linkToUse ?? link,
child: const SizedBox(height: 10.0, width: 10.0),
),
),
Positioned(
left: 787.0,
top: 343.0,
child: CompositedTransformFollower(
link: linkToUse ?? link,
targetAnchor: Alignment.center,
followerAnchor: Alignment.center,
child: SizedBox(key: key, height: 20.0, width: 20.0),
),
),
],
);
},
),
);
}
await tester.pumpWidget(build());
final RenderBox box = key.currentContext!.findRenderObject()! as RenderBox;
expect(box.localToGlobal(Offset.zero), const Offset(118.0, 451.0));
await tester.pumpWidget(build(linkToUse: LayerLink()));
expect(box.localToGlobal(Offset.zero), const Offset(118.0, 451.0));
});
group('Composited transforms - only offsets', () {
final GlobalKey key = GlobalKey();
Widget build({ required Alignment targetAlignment, required Alignment followerAlignment }) {
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: <Widget>[
Positioned(
left: 123.0,
top: 456.0,
child: CompositedTransformTarget(
link: link,
child: const SizedBox(height: 10.0, width: 10.0),
),
),
Positioned(
left: 787.0,
top: 343.0,
child: CompositedTransformFollower(
link: link,
targetAnchor: targetAlignment,
followerAnchor: followerAlignment,
child: SizedBox(key: key, height: 20.0, width: 20.0),
),
),
],
),
);
}
testWidgets('topLeft', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.topLeft, followerAlignment: Alignment.topLeft));
final RenderBox box = key.currentContext!.findRenderObject()! as RenderBox;
expect(box.localToGlobal(Offset.zero), const Offset(123.0, 456.0));
});
testWidgets('center', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.center, followerAlignment: Alignment.center));
final RenderBox box = key.currentContext!.findRenderObject()! as RenderBox;
expect(box.localToGlobal(Offset.zero), const Offset(118.0, 451.0));
});
testWidgets('bottomRight - topRight', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.bottomRight, followerAlignment: Alignment.topRight));
final RenderBox box = key.currentContext!.findRenderObject()! as RenderBox;
expect(box.localToGlobal(Offset.zero), const Offset(113.0, 466.0));
});
});
group('Composited transforms - with rotations', () {
final GlobalKey key1 = GlobalKey();
final GlobalKey key2 = GlobalKey();
Widget build({ required Alignment targetAlignment, required Alignment followerAlignment }) {
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: <Widget>[
Positioned(
top: 123.0,
left: 456.0,
child: Transform.rotate(
angle: 1.0, // radians
child: CompositedTransformTarget(
link: link,
child: SizedBox(key: key1, width: 80.0, height: 10.0),
),
),
),
Positioned(
top: 787.0,
left: 343.0,
child: Transform.rotate(
angle: -0.3, // radians
child: CompositedTransformFollower(
link: link,
targetAnchor: targetAlignment,
followerAnchor: followerAlignment,
child: SizedBox(key: key2, width: 40.0, height: 20.0),
),
),
),
],
),
);
}
testWidgets('topLeft', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.topLeft, followerAlignment: Alignment.topLeft));
final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
final Offset position1 = box1.localToGlobal(Offset.zero);
final Offset position2 = box2.localToGlobal(Offset.zero);
expect(position1, offsetMoreOrLessEquals(position2));
});
testWidgets('center', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.center, followerAlignment: Alignment.center));
final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
final Offset position1 = box1.localToGlobal(const Offset(40, 5));
final Offset position2 = box2.localToGlobal(const Offset(20, 10));
expect(position1, offsetMoreOrLessEquals(position2));
});
testWidgets('bottomRight - topRight', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.bottomRight, followerAlignment: Alignment.topRight));
final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
final Offset position1 = box1.localToGlobal(const Offset(80, 10));
final Offset position2 = box2.localToGlobal(const Offset(40, 0));
expect(position1, offsetMoreOrLessEquals(position2));
});
});
group('Composited transforms - nested', () {
final GlobalKey key1 = GlobalKey();
final GlobalKey key2 = GlobalKey();
Widget build({ required Alignment targetAlignment, required Alignment followerAlignment }) {
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: <Widget>[
Positioned(
top: 123.0,
left: 456.0,
child: Transform.rotate(
angle: 1.0, // radians
child: CompositedTransformTarget(
link: link,
child: SizedBox(key: key1, width: 80.0, height: 10.0),
),
),
),
Positioned(
top: 787.0,
left: 343.0,
child: Transform.rotate(
angle: -0.3, // radians
child: Padding(
padding: const EdgeInsets.all(20.0),
child: CompositedTransformFollower(
link: LayerLink(),
child: Transform(
transform: Matrix4.skew(0.9, 1.1),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: CompositedTransformFollower(
link: link,
targetAnchor: targetAlignment,
followerAnchor: followerAlignment,
child: SizedBox(key: key2, width: 40.0, height: 20.0),
),
),
),
),
),
),
),
],
),
);
}
testWidgets('topLeft', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.topLeft, followerAlignment: Alignment.topLeft));
final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
final Offset position1 = box1.localToGlobal(Offset.zero);
final Offset position2 = box2.localToGlobal(Offset.zero);
expect(position1, offsetMoreOrLessEquals(position2));
});
testWidgets('center', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.center, followerAlignment: Alignment.center));
final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
final Offset position1 = box1.localToGlobal(Alignment.center.alongSize(const Size(80, 10)));
final Offset position2 = box2.localToGlobal(Alignment.center.alongSize(const Size(40, 20)));
expect(position1, offsetMoreOrLessEquals(position2));
});
testWidgets('bottomRight - topRight', (WidgetTester tester) async {
await tester.pumpWidget(build(targetAlignment: Alignment.bottomRight, followerAlignment: Alignment.topRight));
final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
final Offset position1 = box1.localToGlobal(Alignment.bottomRight.alongSize(const Size(80, 10)));
final Offset position2 = box2.localToGlobal(Alignment.topRight.alongSize(const Size(40, 20)));
expect(position1, offsetMoreOrLessEquals(position2));
});
});
group('Composited transforms - hit testing', () {
final GlobalKey key1 = GlobalKey();
final GlobalKey key2 = GlobalKey();
final GlobalKey key3 = GlobalKey();
bool tapped = false;
Widget build({ required Alignment targetAlignment, required Alignment followerAlignment }) {
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: <Widget>[
Positioned(
left: 123.0,
top: 456.0,
child: CompositedTransformTarget(
link: link,
child: SizedBox(key: key1, height: 10.0, width: 10.0),
),
),
CompositedTransformFollower(
link: link,
child: GestureDetector(
key: key2,
behavior: HitTestBehavior.opaque,
onTap: () { tapped = true; },
child: SizedBox(key: key3, height: 2.0, width: 2.0),
),
),
],
),
);
}
const List<Alignment> alignments = <Alignment>[
Alignment.topLeft, Alignment.topRight,
Alignment.center,
Alignment.bottomLeft, Alignment.bottomRight,
];
setUp(() { tapped = false; });
for (final Alignment targetAlignment in alignments) {
for (final Alignment followerAlignment in alignments) {
testWidgets('$targetAlignment - $followerAlignment', (WidgetTester tester) async{
await tester.pumpWidget(build(targetAlignment: targetAlignment, followerAlignment: followerAlignment));
final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
expect(box2.size, const Size(2.0, 2.0));
expect(tapped, isFalse);
await tester.tap(find.byKey(key3), warnIfMissed: false); // the container itself is transparent to hits
expect(tapped, isTrue);
});
}
}
});
}