440 lines
15 KiB
Dart
440 lines
15 KiB
Dart
// 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 containerOne = UniqueKey();
|
|
final containerTwo = UniqueKey();
|
|
final 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 container1 = UniqueKey();
|
|
final container2 = UniqueKey();
|
|
final 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, ?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>[?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 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 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 statefulOne = UniqueKey();
|
|
final statefulTwo = UniqueKey();
|
|
final 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 containerOne = UniqueKey();
|
|
final containerTwo = UniqueKey();
|
|
final containerThree = UniqueKey();
|
|
|
|
late List<Widget> foundChildren;
|
|
Widget newLayoutBuilder(Widget? currentChild, List<Widget> previousChildren) {
|
|
foundChildren = <Widget>[?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 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 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();
|
|
}
|