animated_switcher_test.dart 16.3 KB
Newer Older
1 2 3 4
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/widgets.dart';
6 7 8
import 'package:flutter_test/flutter_test.dart';

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

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

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

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

    await tester.pumpWidget(
41
      AnimatedSwitcher(
42
        duration: const Duration(milliseconds: 100),
43
        child: Container(key: containerThree, color: const Color(0xffff0000)),
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
        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();
  });

59 60 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
  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);
  });

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

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

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

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

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

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

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

    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(
157
      AnimatedSwitcher(
158
        duration: const Duration(milliseconds: 100),
159
        child: Container(color: const Color(0x00000000)),
160 161 162 163 164
        switchInCurve: Curves.linear,
        switchOutCurve: Curves.linear,
      ),
    );

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

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

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

    await tester.pumpWidget(
183
      const AnimatedSwitcher(
184
        duration: Duration(milliseconds: 100),
185 186 187 188 189 190 191 192 193 194
        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));

195 196 197
    await tester.pumpAndSettle();
  });

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

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

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

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

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

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

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

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

    expect(find.byType(Column), findsOneWidget);
264
    for (Widget child in foundChildren) {
265
      expect(child, isInstanceOf<KeyedSubtree>());
266 267 268
    }

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

    for (Widget child in foundChildren) {
283
      expect(child, isInstanceOf<KeyedSubtree>());
284
      expect(
285
        find.descendant(of: find.byWidget(child), matching: find.byType(SizeTransition)),
286 287 288 289
        findsOneWidget,
      );
    }
  });
290 291

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

    StatefulTestState.generation = 0;

    await tester.pumpWidget(
299
      AnimatedSwitcher(
300
        duration: const Duration(milliseconds: 100),
301
        child: StatefulTest(key: statefulOne),
302 303 304 305 306 307 308 309 310 311 312
        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(
313
      AnimatedSwitcher(
314
        duration: const Duration(milliseconds: 100),
315
        child: StatefulTest(key: statefulTwo),
316 317 318 319 320 321 322 323 324 325 326 327
        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(
328
      AnimatedSwitcher(
329
        duration: const Duration(milliseconds: 100),
330
        child: StatefulTest(key: statefulThree),
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
        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 {
349
    Future<void> pumpChild(Widget child) async {
350
      return tester.pumpWidget(
351
        Directionality(
352
          textDirection: TextDirection.rtl,
353
          child: AnimatedSwitcher(
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
            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 {
378 379 380
    final UniqueKey containerOne = UniqueKey();
    final UniqueKey containerTwo = UniqueKey();
    final UniqueKey containerThree = UniqueKey();
381 382 383 384 385 386 387 388

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

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

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

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

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

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

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

    expect(foundChildren.length, equals(3));
    for (Widget child in foundChildren) {
433
      expect(child, isInstanceOf<KeyedSubtree>());
434 435 436 437 438 439 440
      expect(
        find.descendant(of: find.byWidget(child), matching: find.byType(FadeTransition)),
        findsOneWidget,
      );
    }

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

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

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

    expect(foundChildren.length, equals(3));
    for (Widget child in foundChildren) {
463
      expect(child, isInstanceOf<KeyedSubtree>());
464 465 466 467 468 469 470 471 472 473 474 475
      expect(
        find.descendant(of: find.byWidget(child), matching: find.byType(ScaleTransition)),
        findsOneWidget,
      );
    }
  });
}

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

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

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

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

  @override
490
  Widget build(BuildContext context) => Container();
491
}