composited_transform_test.dart 11.9 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// 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';
6
import 'package:flutter_test/flutter_test.dart';
7 8

void main() {
9 10 11 12
  final LayerLink link = LayerLink();

  testWidgets('Change link during layout', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
13
    Widget build({ LayerLink? linkToUse }) {
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
      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,
38
                  child: SizedBox(key: key, height: 20.0, width: 20.0),
39 40 41 42 43 44 45 46 47 48
                ),
              ),
            ],
          );
          },
        ),
      );
    }

    await tester.pumpWidget(build());
49
    final RenderBox box = key.currentContext!.findRenderObject()! as RenderBox;
50 51 52 53 54 55 56
    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', () {
57
    final GlobalKey key = GlobalKey();
58

59
    Widget build({ required Alignment targetAlignment, required Alignment followerAlignment }) {
60
      return Directionality(
61
        textDirection: TextDirection.ltr,
62
        child: Stack(
63
          children: <Widget>[
64
            Positioned(
65 66
              left: 123.0,
              top: 456.0,
67
              child: CompositedTransformTarget(
68
                link: link,
69
                child: const SizedBox(height: 10.0, width: 10.0),
70
              ),
71
            ),
72
            Positioned(
73 74
              left: 787.0,
              top: 343.0,
75
              child: CompositedTransformFollower(
76
                link: link,
77 78
                targetAnchor: targetAlignment,
                followerAnchor: followerAlignment,
79
                child: SizedBox(key: key, height: 20.0, width: 20.0),
80
              ),
81
            ),
82 83
          ],
        ),
84 85 86 87 88
      );
    }

    testWidgets('topLeft', (WidgetTester tester) async {
      await tester.pumpWidget(build(targetAlignment: Alignment.topLeft, followerAlignment: Alignment.topLeft));
89
      final RenderBox box = key.currentContext!.findRenderObject()! as RenderBox;
90 91 92 93 94
      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));
95
      final RenderBox box = key.currentContext!.findRenderObject()! as RenderBox;
96 97 98 99 100
      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));
101
      final RenderBox box = key.currentContext!.findRenderObject()! as RenderBox;
102 103
      expect(box.localToGlobal(Offset.zero), const Offset(113.0, 466.0));
    });
104 105
  });

106
  group('Composited transforms - with rotations', () {
107 108
    final GlobalKey key1 = GlobalKey();
    final GlobalKey key2 = GlobalKey();
109

110
    Widget build({ required Alignment targetAlignment, required Alignment followerAlignment }) {
111
      return Directionality(
112
        textDirection: TextDirection.ltr,
113
        child: Stack(
114
          children: <Widget>[
115
            Positioned(
116 117
              top: 123.0,
              left: 456.0,
118
              child: Transform.rotate(
119
                angle: 1.0, // radians
120
                child: CompositedTransformTarget(
121
                  link: link,
122
                  child: SizedBox(key: key1, width: 80.0, height: 10.0),
123
                ),
124 125
              ),
            ),
126
            Positioned(
127 128
              top: 787.0,
              left: 343.0,
129
              child: Transform.rotate(
130
                angle: -0.3, // radians
131
                child: CompositedTransformFollower(
132
                  link: link,
133 134
                  targetAnchor: targetAlignment,
                  followerAnchor: followerAlignment,
135
                  child: SizedBox(key: key2, width: 40.0, height: 20.0),
136
                ),
137 138
              ),
            ),
139 140
          ],
        ),
141 142 143 144
      );
    }
    testWidgets('topLeft', (WidgetTester tester) async {
      await tester.pumpWidget(build(targetAlignment: Alignment.topLeft, followerAlignment: Alignment.topLeft));
145 146
      final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
      final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
147 148 149 150 151 152 153
      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));
154 155
      final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
      final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
156 157 158 159 160 161 162
      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));
163 164
      final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
      final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
165 166 167 168
      final Offset position1 = box1.localToGlobal(const Offset(80, 10));
      final Offset position2 = box2.localToGlobal(const Offset(40, 0));
      expect(position1, offsetMoreOrLessEquals(position2));
    });
169 170
  });

171
  group('Composited transforms - nested', () {
172 173
    final GlobalKey key1 = GlobalKey();
    final GlobalKey key2 = GlobalKey();
174

175
    Widget build({ required Alignment targetAlignment, required Alignment followerAlignment }) {
176
      return Directionality(
177
        textDirection: TextDirection.ltr,
178
        child: Stack(
179
          children: <Widget>[
180
            Positioned(
181 182
              top: 123.0,
              left: 456.0,
183
              child: Transform.rotate(
184
                angle: 1.0, // radians
185
                child: CompositedTransformTarget(
186
                  link: link,
187
                  child: SizedBox(key: key1, width: 80.0, height: 10.0),
188
                ),
189 190
              ),
            ),
191
            Positioned(
192 193
              top: 787.0,
              left: 343.0,
194
              child: Transform.rotate(
195
                angle: -0.3, // radians
196
                child: Padding(
197
                  padding: const EdgeInsets.all(20.0),
198 199 200 201 202
                  child: CompositedTransformFollower(
                    link: LayerLink(),
                    child: Transform(
                      transform: Matrix4.skew(0.9, 1.1),
                      child: Padding(
203
                        padding: const EdgeInsets.all(20.0),
204
                        child: CompositedTransformFollower(
205
                          link: link,
206 207
                          targetAnchor: targetAlignment,
                          followerAnchor: followerAlignment,
208
                          child: SizedBox(key: key2, width: 40.0, height: 20.0),
209
                        ),
210 211 212 213 214 215
                      ),
                    ),
                  ),
                ),
              ),
            ),
216 217
          ],
        ),
218 219 220 221
      );
    }
    testWidgets('topLeft', (WidgetTester tester) async {
      await tester.pumpWidget(build(targetAlignment: Alignment.topLeft, followerAlignment: Alignment.topLeft));
222 223
      final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
      final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
224 225 226 227 228 229 230
      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));
231 232
      final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
      final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
233 234 235 236 237 238 239
      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));
240 241
      final RenderBox box1 = key1.currentContext!.findRenderObject()! as RenderBox;
      final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
242 243 244 245
      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));
    });
246 247
  });

248
  group('Composited transforms - hit testing', () {
249 250 251
    final GlobalKey key1 = GlobalKey();
    final GlobalKey key2 = GlobalKey();
    final GlobalKey key3 = GlobalKey();
252 253 254

    bool tapped = false;

255
    Widget build({ required Alignment targetAlignment, required Alignment followerAlignment }) {
256
      return Directionality(
257
        textDirection: TextDirection.ltr,
258
        child: Stack(
259
          children: <Widget>[
260
            Positioned(
261 262
              left: 123.0,
              top: 456.0,
263
              child: CompositedTransformTarget(
264
                link: link,
265
                child: SizedBox(key: key1, height: 10.0, width: 10.0),
266
              ),
267
            ),
268
            CompositedTransformFollower(
269
              link: link,
270
              child: GestureDetector(
271 272
                key: key2,
                behavior: HitTestBehavior.opaque,
273
                onTap: () { tapped = true; },
274
                child: SizedBox(key: key3, height: 2.0, width: 2.0),
275
              ),
276
            ),
277 278
          ],
        ),
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
      );
    }

    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));
294
          final RenderBox box2 = key2.currentContext!.findRenderObject()! as RenderBox;
295 296
          expect(box2.size, const Size(2.0, 2.0));
          expect(tapped, isFalse);
297
          await tester.tap(find.byKey(key3), warnIfMissed: false); // the container itself is transparent to hits
298 299 300 301
          expect(tapped, isTrue);
        });
      }
    }
302 303
  });
}