animated_switcher_test.dart 16.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/widgets.dart';
8 9 10
import 'package:flutter_test/flutter_test.dart';

void main() {
11
  testWidgets('AnimatedSwitcher fades in a new child.', (WidgetTester tester) async {
12 13 14
    final UniqueKey containerOne = UniqueKey();
    final UniqueKey containerTwo = UniqueKey();
    final UniqueKey containerThree = UniqueKey();
15
    await tester.pumpWidget(
16
      AnimatedSwitcher(
17
        duration: const Duration(milliseconds: 100),
18
        child: Container(key: containerOne, color: const Color(0x00000000)),
19 20 21 22 23
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

24
    expect(find.byType(FadeTransition), findsOneWidget);
25 26 27 28
    FadeTransition transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(1.0));

    await tester.pumpWidget(
29
      AnimatedSwitcher(
30
        duration: const Duration(milliseconds: 100),
31
        child: Container(key: containerTwo, color: const Color(0xff000000)),
32 33 34 35 36 37
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

    await tester.pump(const Duration(milliseconds: 50));
38
    expect(find.byType(FadeTransition), findsNWidgets(2));
39 40 41 42
    transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(0.5));

    await tester.pumpWidget(
43
      AnimatedSwitcher(
44
        duration: const Duration(milliseconds: 100),
45
        child: Container(key: containerThree, color: const Color(0xffff0000)),
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

    await tester.pump(const Duration(milliseconds: 10));
    transition = tester.widget(find.byType(FadeTransition).at(0));
    expect(transition.opacity.value, closeTo(0.4, 0.01));
    transition = tester.widget(find.byType(FadeTransition).at(1));
    expect(transition.opacity.value, closeTo(0.4, 0.01));
    transition = tester.widget(find.byType(FadeTransition).at(2));
    expect(transition.opacity.value, closeTo(0.1, 0.01));
    await tester.pumpAndSettle();
  });

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
  testWidgets('AnimatedSwitcher can handle back-to-back changes.', (WidgetTester tester) async {
    final UniqueKey container1 = UniqueKey();
    final UniqueKey container2 = UniqueKey();
    final UniqueKey container3 = UniqueKey();
    await tester.pumpWidget(
      AnimatedSwitcher(
        duration: const Duration(milliseconds: 100),
        child: Container(key: container1),
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );
    expect(find.byKey(container1), findsOneWidget);
    expect(find.byKey(container2), findsNothing);
    expect(find.byKey(container3), findsNothing);

    await tester.pumpWidget(
      AnimatedSwitcher(
        duration: const Duration(milliseconds: 100),
        child: Container(key: container2),
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );
    expect(find.byKey(container1), findsOneWidget);
    expect(find.byKey(container2), findsOneWidget);
    expect(find.byKey(container3), findsNothing);

    await tester.pumpWidget(
      AnimatedSwitcher(
        duration: const Duration(milliseconds: 100),
        child: Container(key: container3),
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );
    expect(find.byKey(container1), findsOneWidget);
    expect(find.byKey(container2), findsNothing);
    expect(find.byKey(container3), findsOneWidget);
  });

102
  testWidgets("AnimatedSwitcher doesn't transition in a new child of the same type.", (WidgetTester tester) async {
103
    await tester.pumpWidget(
104
      AnimatedSwitcher(
105
        duration: const Duration(milliseconds: 100),
106
        child: Container(color: const Color(0x00000000)),
107
        switchInCurve: Curves.linear,
108
        switchOutCurve: Curves.linear,
109 110
      ),
    );
111

112
    expect(find.byType(FadeTransition), findsOneWidget);
113 114 115 116
    FadeTransition transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(1.0));

    await tester.pumpWidget(
117
      AnimatedSwitcher(
118
        duration: const Duration(milliseconds: 100),
119
        child: Container(color: const Color(0xff000000)),
120
        switchInCurve: Curves.linear,
121 122 123 124 125
        switchOutCurve: Curves.linear,
      ),
    );

    await tester.pump(const Duration(milliseconds: 50));
126 127
    expect(find.byType(FadeTransition), findsOneWidget);
    transition = tester.firstWidget(find.byType(FadeTransition));
128 129 130 131
    expect(transition.opacity.value, equals(1.0));
    await tester.pumpAndSettle();
  });

132
  testWidgets('AnimatedSwitcher handles null children.', (WidgetTester tester) async {
133
    await tester.pumpWidget(
134
      const AnimatedSwitcher(
135
        duration: Duration(milliseconds: 100),
136 137 138 139 140 141 142 143 144
        child: null,
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

    expect(find.byType(FadeTransition), findsNothing);

    await tester.pumpWidget(
145
      AnimatedSwitcher(
146
        duration: const Duration(milliseconds: 100),
147
        child: Container(color: const Color(0xff000000)),
148 149
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
150 151
      ),
    );
152 153 154 155 156 157 158

    await tester.pump(const Duration(milliseconds: 50));
    FadeTransition transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(0.5));
    await tester.pumpAndSettle();

    await tester.pumpWidget(
159
      AnimatedSwitcher(
160
        duration: const Duration(milliseconds: 100),
161
        child: Container(color: const Color(0x00000000)),
162 163 164 165 166
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

167
    expect(find.byType(FadeTransition), findsOneWidget);
168 169 170 171
    transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(1.0));

    await tester.pumpWidget(
172
      const AnimatedSwitcher(
173
        duration: Duration(milliseconds: 100),
174 175 176 177 178 179
        child: null,
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

180 181 182
    await tester.pump(const Duration(milliseconds: 50));
    transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(0.5));
183 184

    await tester.pumpWidget(
185
      const AnimatedSwitcher(
186
        duration: Duration(milliseconds: 100),
187 188 189 190 191 192 193 194 195 196
        child: null,
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

    await tester.pump(const Duration(milliseconds: 50));
    transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(0.0));

197 198 199
    await tester.pumpAndSettle();
  });

200
  testWidgets("AnimatedSwitcher doesn't start any animations after dispose.", (WidgetTester tester) async {
201
    await tester.pumpWidget(AnimatedSwitcher(
202
      duration: const Duration(milliseconds: 100),
203
      child: Container(color: const Color(0xff000000)),
204 205 206 207 208
      switchInCurve: Curves.linear,
    ));
    await tester.pump(const Duration(milliseconds: 50));

    // Change the widget tree in the middle of the animation.
209
    await tester.pumpWidget(Container(color: const Color(0xffff0000)));
210 211
    expect(await tester.pumpAndSettle(const Duration(milliseconds: 100)), equals(1));
  });
212

213
  testWidgets('AnimatedSwitcher uses custom layout.', (WidgetTester tester) async {
214
    Widget newLayoutBuilder(Widget currentChild, List<Widget> previousChildren) {
215
      return Column(
216
        children: previousChildren + <Widget>[currentChild],
217 218 219 220
      );
    }

    await tester.pumpWidget(
221
      AnimatedSwitcher(
222
        duration: const Duration(milliseconds: 100),
223
        child: Container(color: const Color(0x00000000)),
224 225 226 227 228 229 230 231
        switchInCurve: Curves.linear,
        layoutBuilder: newLayoutBuilder,
      ),
    );

    expect(find.byType(Column), findsOneWidget);
  });

232
  testWidgets('AnimatedSwitcher uses custom transitions.', (WidgetTester tester) async {
233 234 235 236 237 238 239
    final List<Widget> foundChildren = <Widget>[];
    Widget newLayoutBuilder(Widget currentChild, List<Widget> previousChildren) {
      foundChildren.clear();
      if (currentChild != null) {
        foundChildren.add(currentChild);
      }
      foundChildren.addAll(previousChildren);
240
      return Column(
241
        children: foundChildren,
242 243 244 245
      );
    }

    Widget newTransitionBuilder(Widget child, Animation<double> animation) {
246
      return SizeTransition(
247 248 249 250 251 252
        sizeFactor: animation,
        child: child,
      );
    }

    await tester.pumpWidget(
253
      Directionality(
254
        textDirection: TextDirection.rtl,
255
        child: AnimatedSwitcher(
256
          duration: const Duration(milliseconds: 100),
257
          child: Container(color: const Color(0x00000000)),
258 259 260 261 262 263 264 265
          switchInCurve: Curves.linear,
          layoutBuilder: newLayoutBuilder,
          transitionBuilder: newTransitionBuilder,
        ),
      ),
    );

    expect(find.byType(Column), findsOneWidget);
266
    for (final Widget child in foundChildren) {
Dan Field's avatar
Dan Field committed
267
      expect(child, isA<KeyedSubtree>());
268 269 270
    }

    await tester.pumpWidget(
271
      Directionality(
272
        textDirection: TextDirection.rtl,
273
        child: AnimatedSwitcher(
274 275 276 277 278 279 280 281 282 283
          duration: const Duration(milliseconds: 100),
          child: null,
          switchInCurve: Curves.linear,
          layoutBuilder: newLayoutBuilder,
          transitionBuilder: newTransitionBuilder,
        ),
      ),
    );
    await tester.pump(const Duration(milliseconds: 50));

284
    for (final Widget child in foundChildren) {
Dan Field's avatar
Dan Field committed
285
      expect(child, isA<KeyedSubtree>());
286
      expect(
287
        find.descendant(of: find.byWidget(child), matching: find.byType(SizeTransition)),
288 289 290 291
        findsOneWidget,
      );
    }
  });
292 293

  testWidgets("AnimatedSwitcher doesn't reset state of the children in transitions.", (WidgetTester tester) async {
294 295 296
    final UniqueKey statefulOne = UniqueKey();
    final UniqueKey statefulTwo = UniqueKey();
    final UniqueKey statefulThree = UniqueKey();
297 298 299 300

    StatefulTestState.generation = 0;

    await tester.pumpWidget(
301
      AnimatedSwitcher(
302
        duration: const Duration(milliseconds: 100),
303
        child: StatefulTest(key: statefulOne),
304 305 306 307 308 309 310 311 312 313 314
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

    expect(find.byType(FadeTransition), findsOneWidget);
    FadeTransition transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(1.0));
    expect(StatefulTestState.generation, equals(1));

    await tester.pumpWidget(
315
      AnimatedSwitcher(
316
        duration: const Duration(milliseconds: 100),
317
        child: StatefulTest(key: statefulTwo),
318 319 320 321 322 323 324 325 326 327 328 329
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

    await tester.pump(const Duration(milliseconds: 50));
    expect(find.byType(FadeTransition), findsNWidgets(2));
    transition = tester.firstWidget(find.byType(FadeTransition));
    expect(transition.opacity.value, equals(0.5));
    expect(StatefulTestState.generation, equals(2));

    await tester.pumpWidget(
330
      AnimatedSwitcher(
331
        duration: const Duration(milliseconds: 100),
332
        child: StatefulTest(key: statefulThree),
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

    await tester.pump(const Duration(milliseconds: 10));
    expect(StatefulTestState.generation, equals(3));
    transition = tester.widget(find.byType(FadeTransition).at(0));
    expect(transition.opacity.value, closeTo(0.4, 0.01));
    transition = tester.widget(find.byType(FadeTransition).at(1));
    expect(transition.opacity.value, closeTo(0.4, 0.01));
    transition = tester.widget(find.byType(FadeTransition).at(2));
    expect(transition.opacity.value, closeTo(0.1, 0.01));
    await tester.pumpAndSettle();
    expect(StatefulTestState.generation, equals(3));
  });

  testWidgets('AnimatedSwitcher updates widgets without animating if they are isomorphic.', (WidgetTester tester) async {
351
    Future<void> pumpChild(Widget child) async {
352
      return tester.pumpWidget(
353
        Directionality(
354
          textDirection: TextDirection.rtl,
355
          child: AnimatedSwitcher(
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
            duration: const Duration(milliseconds: 100),
            child: child,
            switchInCurve: Curves.linear,
            switchOutCurve: Curves.linear,
          ),
        ),
      );
    }

    await pumpChild(const Text('1'));
    await tester.pump(const Duration(milliseconds: 10));
    FadeTransition transition = tester.widget(find.byType(FadeTransition).first);
    expect(transition.opacity.value, equals(1.0));
    expect(find.text('1'), findsOneWidget);
    expect(find.text('2'), findsNothing);
    await pumpChild(const Text('2'));
    transition = tester.widget(find.byType(FadeTransition).first);
    await tester.pump(const Duration(milliseconds: 20));
    expect(transition.opacity.value, equals(1.0));
    expect(find.text('1'), findsNothing);
    expect(find.text('2'), findsOneWidget);
  });

  testWidgets('AnimatedSwitcher updates previous child transitions if the transitionBuilder changes.', (WidgetTester tester) async {
380 381 382
    final UniqueKey containerOne = UniqueKey();
    final UniqueKey containerTwo = UniqueKey();
    final UniqueKey containerThree = UniqueKey();
383 384 385 386 387 388 389 390

    final List<Widget> foundChildren = <Widget>[];
    Widget newLayoutBuilder(Widget currentChild, List<Widget> previousChildren) {
      foundChildren.clear();
      if (currentChild != null) {
        foundChildren.add(currentChild);
      }
      foundChildren.addAll(previousChildren);
391
      return Column(
392 393 394 395 396 397
        children: foundChildren,
      );
    }

    // Insert three unique children so that we have some previous children.
    await tester.pumpWidget(
398
      AnimatedSwitcher(
399
        duration: const Duration(milliseconds: 100),
400
        child: Container(key: containerOne, color: const Color(0xFFFF0000)),
401 402 403 404 405 406 407 408 409
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
        layoutBuilder: newLayoutBuilder,
      ),
    );

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

    await tester.pumpWidget(
410
      AnimatedSwitcher(
411
        duration: const Duration(milliseconds: 100),
412
        child: Container(key: containerTwo, color: const Color(0xFF00FF00)),
413 414 415 416 417 418 419 420 421
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
        layoutBuilder: newLayoutBuilder,
      ),
    );

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

    await tester.pumpWidget(
422
      AnimatedSwitcher(
423
        duration: const Duration(milliseconds: 100),
424
        child: Container(key: containerThree, color: const Color(0xFF0000FF)),
425 426 427 428 429 430 431 432 433
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
        layoutBuilder: newLayoutBuilder,
      ),
    );

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

    expect(foundChildren.length, equals(3));
434
    for (final Widget child in foundChildren) {
Dan Field's avatar
Dan Field committed
435
      expect(child, isA<KeyedSubtree>());
436 437 438 439 440 441 442
      expect(
        find.descendant(of: find.byWidget(child), matching: find.byType(FadeTransition)),
        findsOneWidget,
      );
    }

    Widget newTransitionBuilder(Widget child, Animation<double> animation) {
443
      return ScaleTransition(
444 445 446 447 448 449 450 451
        scale: animation,
        child: child,
      );
    }

    // Now set a new transition builder and make sure all the previous
    // transitions are replaced.
    await tester.pumpWidget(
452
      AnimatedSwitcher(
453
        duration: const Duration(milliseconds: 100),
454
        child: Container(color: const Color(0x00000000)),
455 456 457 458 459 460 461 462 463
        switchInCurve: Curves.linear,
        layoutBuilder: newLayoutBuilder,
        transitionBuilder: newTransitionBuilder,
      ),
    );

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

    expect(foundChildren.length, equals(3));
464
    for (final Widget child in foundChildren) {
Dan Field's avatar
Dan Field committed
465
      expect(child, isA<KeyedSubtree>());
466 467 468 469 470 471 472 473 474 475 476 477
      expect(
        find.descendant(of: find.byWidget(child), matching: find.byType(ScaleTransition)),
        findsOneWidget,
      );
    }
  });
}

class StatefulTest extends StatefulWidget {
  const StatefulTest({Key key}) : super(key: key);

  @override
478
  StatefulTestState createState() => StatefulTestState();
479 480 481 482 483 484 485 486 487 488 489 490 491
}

class StatefulTestState extends State<StatefulTest> {
  StatefulTestState();
  static int generation = 0;

  @override
  void initState() {
    super.initState();
    generation++;
  }

  @override
492
  Widget build(BuildContext context) => Container();
493
}