blob: 0d914cb73ed9c449eb4e808b31bef2e939b29612 [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() {
testWidgets('AnimatedSwitcher fades in a new child.', (WidgetTester tester) async {
final UniqueKey containerOne = UniqueKey();
final UniqueKey containerTwo = UniqueKey();
final UniqueKey containerThree = UniqueKey();
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: Container(key: containerOne, color: const Color(0x00000000)),
),
);
expect(find.byType(FadeTransition), findsOneWidget);
FadeTransition transition = tester.firstWidget(find.byType(FadeTransition));
expect(transition.opacity.value, equals(1.0));
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: Container(key: containerTwo, color: const Color(0xff000000)),
),
);
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));
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: Container(key: containerThree, color: const Color(0xffff0000)),
),
);
await tester.pump(const Duration(milliseconds: 10));
transition = tester.widget(find.byType(FadeTransition).at(0));
expect(transition.opacity.value, moreOrLessEquals(0.4, epsilon: 0.01));
transition = tester.widget(find.byType(FadeTransition).at(1));
expect(transition.opacity.value, moreOrLessEquals(0.4, epsilon: 0.01));
transition = tester.widget(find.byType(FadeTransition).at(2));
expect(transition.opacity.value, moreOrLessEquals(0.1, epsilon: 0.01));
await tester.pumpAndSettle();
});
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),
),
);
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),
),
);
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),
),
);
expect(find.byKey(container1), findsOneWidget);
expect(find.byKey(container2), findsNothing);
expect(find.byKey(container3), findsOneWidget);
});
testWidgets("AnimatedSwitcher doesn't transition in a new child of the same type.", (WidgetTester tester) async {
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: Container(color: const Color(0x00000000)),
),
);
expect(find.byType(FadeTransition), findsOneWidget);
FadeTransition transition = tester.firstWidget(find.byType(FadeTransition));
expect(transition.opacity.value, equals(1.0));
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: Container(color: const Color(0xff000000)),
),
);
await tester.pump(const Duration(milliseconds: 50));
expect(find.byType(FadeTransition), findsOneWidget);
transition = tester.firstWidget(find.byType(FadeTransition));
expect(transition.opacity.value, equals(1.0));
await tester.pumpAndSettle();
});
testWidgets('AnimatedSwitcher handles null children.', (WidgetTester tester) async {
await tester.pumpWidget(
const AnimatedSwitcher(
duration: Duration(milliseconds: 100),
),
);
expect(find.byType(FadeTransition), findsNothing);
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: Container(color: const Color(0xff000000)),
),
);
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(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: Container(color: const Color(0x00000000)),
),
);
expect(find.byType(FadeTransition), findsOneWidget);
transition = tester.firstWidget(find.byType(FadeTransition));
expect(transition.opacity.value, equals(1.0));
await tester.pumpWidget(
const AnimatedSwitcher(
duration: Duration(milliseconds: 100),
),
);
await tester.pump(const Duration(milliseconds: 50));
transition = tester.firstWidget(find.byType(FadeTransition));
expect(transition.opacity.value, equals(0.5));
await tester.pumpWidget(
const AnimatedSwitcher(
duration: Duration(milliseconds: 100),
),
);
await tester.pump(const Duration(milliseconds: 50));
transition = tester.firstWidget(find.byType(FadeTransition));
expect(transition.opacity.value, equals(0.0));
await tester.pumpAndSettle();
});
testWidgets("AnimatedSwitcher doesn't start any animations after dispose.", (WidgetTester tester) async {
await tester.pumpWidget(AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: Container(color: const Color(0xff000000)),
));
await tester.pump(const Duration(milliseconds: 50));
// Change the widget tree in the middle of the animation.
await tester.pumpWidget(Container(color: const Color(0xffff0000)));
expect(await tester.pumpAndSettle(), equals(1));
});
testWidgets('AnimatedSwitcher uses custom layout.', (WidgetTester tester) async {
Widget newLayoutBuilder(Widget? currentChild, List<Widget> previousChildren) {
return Column(
children: <Widget>[
...previousChildren,
if (currentChild != null) currentChild,
],
);
}
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
layoutBuilder: newLayoutBuilder,
child: Container(color: const Color(0x00000000)),
),
);
expect(find.byType(Column), findsOneWidget);
});
testWidgets('AnimatedSwitcher uses custom transitions.', (WidgetTester tester) async {
late List<Widget> foundChildren;
Widget newLayoutBuilder(Widget? currentChild, List<Widget> previousChildren) {
foundChildren = <Widget>[
if (currentChild != null) currentChild,
...previousChildren,
];
return Column(children: foundChildren);
}
Widget newTransitionBuilder(Widget child, Animation<double> animation) {
return SizeTransition(
sizeFactor: animation,
child: child,
);
}
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.rtl,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
layoutBuilder: newLayoutBuilder,
transitionBuilder: newTransitionBuilder,
child: Container(color: const Color(0x00000000)),
),
),
);
expect(find.byType(Column), findsOneWidget);
for (final Widget child in foundChildren) {
expect(child, isA<KeyedSubtree>());
}
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.rtl,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
layoutBuilder: newLayoutBuilder,
transitionBuilder: newTransitionBuilder,
),
),
);
await tester.pump(const Duration(milliseconds: 50));
for (final Widget child in foundChildren) {
expect(child, isA<KeyedSubtree>());
expect(
find.descendant(of: find.byWidget(child), matching: find.byType(SizeTransition)),
findsOneWidget,
);
}
});
testWidgets("AnimatedSwitcher doesn't reset state of the children in transitions.", (WidgetTester tester) async {
final UniqueKey statefulOne = UniqueKey();
final UniqueKey statefulTwo = UniqueKey();
final UniqueKey statefulThree = UniqueKey();
StatefulTestState.generation = 0;
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: StatefulTest(key: statefulOne),
),
);
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(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: StatefulTest(key: statefulTwo),
),
);
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(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: StatefulTest(key: statefulThree),
),
);
await tester.pump(const Duration(milliseconds: 10));
expect(StatefulTestState.generation, equals(3));
transition = tester.widget(find.byType(FadeTransition).at(0));
expect(transition.opacity.value, moreOrLessEquals(0.4, epsilon: 0.01));
transition = tester.widget(find.byType(FadeTransition).at(1));
expect(transition.opacity.value, moreOrLessEquals(0.4, epsilon: 0.01));
transition = tester.widget(find.byType(FadeTransition).at(2));
expect(transition.opacity.value, moreOrLessEquals(0.1, epsilon: 0.01));
await tester.pumpAndSettle();
expect(StatefulTestState.generation, equals(3));
});
testWidgets('AnimatedSwitcher updates widgets without animating if they are isomorphic.', (WidgetTester tester) async {
Future<void> pumpChild(Widget child) async {
return tester.pumpWidget(
Directionality(
textDirection: TextDirection.rtl,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: child,
),
),
);
}
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 {
final UniqueKey containerOne = UniqueKey();
final UniqueKey containerTwo = UniqueKey();
final UniqueKey containerThree = UniqueKey();
late List<Widget> foundChildren;
Widget newLayoutBuilder(Widget? currentChild, List<Widget> previousChildren) {
foundChildren = <Widget>[
if (currentChild != null) currentChild,
...previousChildren,
];
return Column(children: foundChildren);
}
// Insert three unique children so that we have some previous children.
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
layoutBuilder: newLayoutBuilder,
child: Container(key: containerOne, color: const Color(0xFFFF0000)),
),
);
await tester.pump(const Duration(milliseconds: 10));
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
layoutBuilder: newLayoutBuilder,
child: Container(key: containerTwo, color: const Color(0xFF00FF00)),
),
);
await tester.pump(const Duration(milliseconds: 10));
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
layoutBuilder: newLayoutBuilder,
child: Container(key: containerThree, color: const Color(0xFF0000FF)),
),
);
await tester.pump(const Duration(milliseconds: 10));
expect(foundChildren.length, equals(3));
for (final Widget child in foundChildren) {
expect(child, isA<KeyedSubtree>());
expect(
find.descendant(of: find.byWidget(child), matching: find.byType(FadeTransition)),
findsOneWidget,
);
}
Widget newTransitionBuilder(Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation,
child: child,
);
}
// Now set a new transition builder and make sure all the previous
// transitions are replaced.
await tester.pumpWidget(
AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
layoutBuilder: newLayoutBuilder,
transitionBuilder: newTransitionBuilder,
child: Container(color: const Color(0x00000000)),
),
);
await tester.pump(const Duration(milliseconds: 10));
expect(foundChildren.length, equals(3));
for (final Widget child in foundChildren) {
expect(child, isA<KeyedSubtree>());
expect(
find.descendant(of: find.byWidget(child), matching: find.byType(ScaleTransition)),
findsOneWidget,
);
}
});
testWidgets('AnimatedSwitcher does not duplicate animations if the same child is entered twice.', (WidgetTester tester) async {
Future<void> pumpChild(Widget child) async {
return tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 1000),
child: child,
),
),
);
}
await pumpChild(const Text('1', key: Key('1')));
await pumpChild(const Text('2', key: Key('2')));
await pumpChild(const Text('1', key: Key('1')));
await tester.pump(const Duration(milliseconds: 1000));
expect(find.text('1'), findsOneWidget);
});
}
class StatefulTest extends StatefulWidget {
const StatefulTest({super.key});
@override
StatefulTestState createState() => StatefulTestState();
}
class StatefulTestState extends State<StatefulTest> {
StatefulTestState();
static int generation = 0;
@override
void initState() {
super.initState();
generation++;
}
@override
Widget build(BuildContext context) => Container();
}