animated_cross_fade_test.dart 12.3 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7
import 'package:flutter/scheduler.dart';
8 9 10 11 12 13 14
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

void main() {
  testWidgets('AnimatedCrossFade test', (WidgetTester tester) async {
    await tester.pumpWidget(
15 16
      const Directionality(
        textDirection: TextDirection.ltr,
17 18 19
        child: Center(
          child: AnimatedCrossFade(
            firstChild: SizedBox(
20 21 22
              width: 100.0,
              height: 100.0,
            ),
23
            secondChild: SizedBox(
24 25 26
              width: 200.0,
              height: 200.0,
            ),
27
            duration: Duration(milliseconds: 200),
28
            crossFadeState: CrossFadeState.showFirst,
29
          ),
30 31
        ),
      ),
32 33 34 35 36 37 38 39
    );

    expect(find.byType(FadeTransition), findsNWidgets(2));
    RenderBox box = tester.renderObject(find.byType(AnimatedCrossFade));
    expect(box.size.width, equals(100.0));
    expect(box.size.height, equals(100.0));

    await tester.pumpWidget(
40 41
      const Directionality(
        textDirection: TextDirection.ltr,
42 43 44
        child: Center(
          child: AnimatedCrossFade(
            firstChild: SizedBox(
45 46 47
              width: 100.0,
              height: 100.0,
            ),
48
            secondChild: SizedBox(
49 50 51
              width: 200.0,
              height: 200.0,
            ),
52
            duration: Duration(milliseconds: 200),
53
            crossFadeState: CrossFadeState.showSecond,
54
          ),
55 56
        ),
      ),
57 58 59 60 61 62 63 64 65
    );

    await tester.pump(const Duration(milliseconds: 100));

    expect(find.byType(FadeTransition), findsNWidgets(2));
    box = tester.renderObject(find.byType(AnimatedCrossFade));
    expect(box.size.width, equals(150.0));
    expect(box.size.height, equals(150.0));
  });
66 67 68

  testWidgets('AnimatedCrossFade test showSecond', (WidgetTester tester) async {
    await tester.pumpWidget(
69 70
      const Directionality(
        textDirection: TextDirection.ltr,
71 72 73
        child: Center(
          child: AnimatedCrossFade(
            firstChild: SizedBox(
74 75 76
              width: 100.0,
              height: 100.0,
            ),
77
            secondChild: SizedBox(
78 79 80
              width: 200.0,
              height: 200.0,
            ),
81
            duration: Duration(milliseconds: 200),
82
            crossFadeState: CrossFadeState.showSecond,
83
          ),
84 85
        ),
      ),
86 87 88
    );

    expect(find.byType(FadeTransition), findsNWidgets(2));
89
    final RenderBox box = tester.renderObject(find.byType(AnimatedCrossFade));
90 91 92
    expect(box.size.width, equals(200.0));
    expect(box.size.height, equals(200.0));
  });
93

94
  testWidgets('AnimatedCrossFade alignment (VISUAL)', (WidgetTester tester) async {
95 96
    final Key firstKey = UniqueKey();
    final Key secondKey = UniqueKey();
97 98

    await tester.pumpWidget(
99
      Directionality(
100
        textDirection: TextDirection.ltr,
101 102
        child: Center(
          child: AnimatedCrossFade(
103
            alignment: Alignment.bottomRight,
104
            firstChild: SizedBox(
105 106 107 108
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
109
            secondChild: SizedBox(
110 111 112 113 114 115
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showFirst,
116
          ),
117 118
        ),
      ),
119 120 121
    );

    await tester.pumpWidget(
122
      Directionality(
123
        textDirection: TextDirection.ltr,
124 125
        child: Center(
          child: AnimatedCrossFade(
126
            alignment: Alignment.bottomRight,
127
            firstChild: SizedBox(
128 129 130 131
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
132
            secondChild: SizedBox(
133 134 135 136 137 138
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showSecond,
139
          ),
140 141 142 143 144 145 146 147 148 149 150 151 152
        ),
      ),
    );

    await tester.pump(const Duration(milliseconds: 100));

    final RenderBox box1 = tester.renderObject(find.byKey(firstKey));
    final RenderBox box2 = tester.renderObject(find.byKey(secondKey));
    expect(box1.localToGlobal(Offset.zero), const Offset(275.0, 175.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(275.0, 175.0));
  });

  testWidgets('AnimatedCrossFade alignment (LTR)', (WidgetTester tester) async {
153 154
    final Key firstKey = UniqueKey();
    final Key secondKey = UniqueKey();
155 156

    await tester.pumpWidget(
157
      Directionality(
158
        textDirection: TextDirection.ltr,
159 160
        child: Center(
          child: AnimatedCrossFade(
161
            alignment: AlignmentDirectional.bottomEnd,
162
            firstChild: SizedBox(
163 164 165 166
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
167
            secondChild: SizedBox(
168 169 170 171 172 173 174 175 176 177 178 179
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showFirst,
          ),
        ),
      ),
    );

    await tester.pumpWidget(
180
      Directionality(
181
        textDirection: TextDirection.ltr,
182 183
        child: Center(
          child: AnimatedCrossFade(
184
            alignment: AlignmentDirectional.bottomEnd,
185
            firstChild: SizedBox(
186 187 188 189
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
190
            secondChild: SizedBox(
191 192 193 194 195 196 197 198 199
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showSecond,
          ),
        ),
      ),
200 201 202 203 204 205 206 207 208 209
    );

    await tester.pump(const Duration(milliseconds: 100));

    final RenderBox box1 = tester.renderObject(find.byKey(firstKey));
    final RenderBox box2 = tester.renderObject(find.byKey(secondKey));
    expect(box1.localToGlobal(Offset.zero), const Offset(275.0, 175.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(275.0, 175.0));
  });

210
  testWidgets('AnimatedCrossFade alignment (RTL)', (WidgetTester tester) async {
211 212
    final Key firstKey = UniqueKey();
    final Key secondKey = UniqueKey();
213 214

    await tester.pumpWidget(
215
      Directionality(
216
        textDirection: TextDirection.rtl,
217 218
        child: Center(
          child: AnimatedCrossFade(
219
            alignment: AlignmentDirectional.bottomEnd,
220
            firstChild: SizedBox(
221 222 223 224
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
225
            secondChild: SizedBox(
226 227 228 229 230 231 232 233 234 235 236 237
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showFirst,
          ),
        ),
      ),
    );

    await tester.pumpWidget(
238
      Directionality(
239
        textDirection: TextDirection.rtl,
240 241
        child: Center(
          child: AnimatedCrossFade(
242
            alignment: AlignmentDirectional.bottomEnd,
243
            firstChild: SizedBox(
244 245 246 247
              key: firstKey,
              width: 100.0,
              height: 100.0,
            ),
248
            secondChild: SizedBox(
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
              key: secondKey,
              width: 200.0,
              height: 200.0,
            ),
            duration: const Duration(milliseconds: 200),
            crossFadeState: CrossFadeState.showSecond,
          ),
        ),
      ),
    );

    await tester.pump(const Duration(milliseconds: 100));

    final RenderBox box1 = tester.renderObject(find.byKey(firstKey));
    final RenderBox box2 = tester.renderObject(find.byKey(secondKey));
    expect(box1.localToGlobal(Offset.zero), const Offset(325.0, 175.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(325.0, 175.0));
  });

268
  Widget crossFadeWithWatcher({ bool towardsSecond = false }) {
269
    return Directionality(
270
      textDirection: TextDirection.ltr,
271
      child: AnimatedCrossFade(
272
        firstChild: const _TickerWatchingWidget(),
273
        secondChild: Container(),
274 275 276
        crossFadeState: towardsSecond ? CrossFadeState.showSecond : CrossFadeState.showFirst,
        duration: const Duration(milliseconds: 50),
      ),
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    );
  }

  testWidgets('AnimatedCrossFade preserves widget state', (WidgetTester tester) async {
    await tester.pumpWidget(crossFadeWithWatcher());

    _TickerWatchingWidgetState findState() => tester.state(find.byType(_TickerWatchingWidget));
    final _TickerWatchingWidgetState state = findState();

    await tester.pumpWidget(crossFadeWithWatcher(towardsSecond: true));
    for (int i = 0; i < 3; i += 1) {
      await tester.pump(const Duration(milliseconds: 25));
      expect(findState(), same(state));
    }
  });

  testWidgets('AnimatedCrossFade switches off TickerMode and semantics on faded out widget', (WidgetTester tester) async {
    ExcludeSemantics findSemantics() {
      return tester.widget(find.descendant(
        of: find.byKey(const ValueKey<CrossFadeState>(CrossFadeState.showFirst)),
        matching: find.byType(ExcludeSemantics),
      ));
    }

    await tester.pumpWidget(crossFadeWithWatcher());

    final _TickerWatchingWidgetState state = tester.state(find.byType(_TickerWatchingWidget));
    expect(state.ticker.muted, false);
    expect(findSemantics().excluding, false);

    await tester.pumpWidget(crossFadeWithWatcher(towardsSecond: true));
    for (int i = 0; i < 2; i += 1) {
      await tester.pump(const Duration(milliseconds: 25));
      // Animations are kept alive in the middle of cross-fade
      expect(state.ticker.muted, false);
      // Semantics are turned off immediately on the widget that's fading out
      expect(findSemantics().excluding, true);
    }

    // In the final state both animations and semantics should be off on the
    // widget that's faded out.
    await tester.pump(const Duration(milliseconds: 25));
    expect(state.ticker.muted, true);
    expect(findSemantics().excluding, true);
  });
322 323

  testWidgets('AnimatedCrossFade.layoutBuilder', (WidgetTester tester) async {
324 325 326
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
327 328 329
        child: AnimatedCrossFade(
          firstChild: Text('AAA', textDirection: TextDirection.ltr),
          secondChild: Text('BBB', textDirection: TextDirection.ltr),
330
          crossFadeState: CrossFadeState.showFirst,
331
          duration: Duration(milliseconds: 50),
332 333 334
        ),
      ),
    );
335 336
    expect(find.text('AAA'), findsOneWidget);
    expect(find.text('BBB'), findsOneWidget);
337
    await tester.pumpWidget(
338
      Directionality(
339
        textDirection: TextDirection.ltr,
340
        child: AnimatedCrossFade(
341 342 343 344 345 346 347 348
          firstChild: const Text('AAA', textDirection: TextDirection.ltr),
          secondChild: const Text('BBB', textDirection: TextDirection.ltr),
          crossFadeState: CrossFadeState.showFirst,
          duration: const Duration(milliseconds: 50),
          layoutBuilder: (Widget a, Key aKey, Widget b, Key bKey) => a,
        ),
      ),
    );
349 350
    expect(find.text('AAA'), findsOneWidget);
    expect(find.text('BBB'), findsNothing);
351
    await tester.pumpWidget(
352
      Directionality(
353
        textDirection: TextDirection.ltr,
354
        child: AnimatedCrossFade(
355 356 357 358 359 360 361 362
          firstChild: const Text('AAA', textDirection: TextDirection.ltr),
          secondChild: const Text('BBB', textDirection: TextDirection.ltr),
          crossFadeState: CrossFadeState.showSecond,
          duration: const Duration(milliseconds: 50),
          layoutBuilder: (Widget a, Key aKey, Widget b, Key bKey) => a,
        ),
      ),
    );
363 364 365
    expect(find.text('BBB'), findsOneWidget);
    expect(find.text('AAA'), findsNothing);
  });
366 367 368 369 370 371
}

class _TickerWatchingWidget extends StatefulWidget {
  const _TickerWatchingWidget();

  @override
372
  State<StatefulWidget> createState() => _TickerWatchingWidgetState();
373 374 375 376 377 378 379 380
}

class _TickerWatchingWidgetState extends State<_TickerWatchingWidget> with SingleTickerProviderStateMixin {
  Ticker ticker;

  @override
  void initState() {
    super.initState();
381
    ticker = createTicker((_) { })..start();
382 383 384
  }

  @override
385
  Widget build(BuildContext context) => Container();
386 387 388 389 390 391

  @override
  void dispose() {
    ticker.dispose();
    super.dispose();
  }
392
}