diff --git a/packages/flutter/lib/src/widgets/animated_switcher.dart b/packages/flutter/lib/src/widgets/animated_switcher.dart index e2d6828a6b..7dce6d9320 100644 --- a/packages/flutter/lib/src/widgets/animated_switcher.dart +++ b/packages/flutter/lib/src/widgets/animated_switcher.dart @@ -216,7 +216,6 @@ class AnimatedSwitcher extends StatefulWidget { /// This is an [AnimatedSwitcherTransitionBuilder] function. static Widget defaultTransitionBuilder(Widget child, Animation animation) { return FadeTransition( - key: ValueKey(child.key), opacity: animation, child: child, ); @@ -338,7 +337,10 @@ class _AnimatedSwitcherState extends State with TickerProvider }) { final _ChildEntry entry = _ChildEntry( widgetChild: child, - transition: KeyedSubtree.wrap(builder(child, animation), _childNumber), + transition: KeyedSubtree( + key: ValueKey(_childNumber), + child: builder(child, animation), + ), animation: animation, controller: controller, ); @@ -389,6 +391,6 @@ class _AnimatedSwitcherState extends State with TickerProvider @override Widget build(BuildContext context) { _rebuildOutgoingWidgetsIfNeeded(); - return widget.layoutBuilder(_currentEntry?.transition, _outgoingWidgets!.where((Widget outgoing) => outgoing.key != _currentEntry?.transition.key).toSet().toList()); + return widget.layoutBuilder(_currentEntry?.transition, _outgoingWidgets!); } } diff --git a/packages/flutter/test/widgets/animated_switcher_test.dart b/packages/flutter/test/widgets/animated_switcher_test.dart index 0d914cb73e..4dcacebb75 100644 --- a/packages/flutter/test/widgets/animated_switcher_test.dart +++ b/packages/flutter/test/widgets/animated_switcher_test.dart @@ -416,23 +416,44 @@ void main() { } }); - testWidgets('AnimatedSwitcher does not duplicate animations if the same child is entered twice.', (WidgetTester tester) async { - Future pumpChild(Widget child) async { + testWidgets('AnimatedSwitcher can handle multiple children with the same key.', (WidgetTester tester) async { + final UniqueKey containerA = UniqueKey(); + final UniqueKey containerB = UniqueKey(); + + // Pump an AnimatedSwitcher with a child container with the given key. + Future pump(Key key) async { return tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 1000), - child: child, - ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 1000), + child: Container(key: key), ), ); } - await pumpChild(const Text('1', key: Key('1'))); - await pumpChild(const Text('2', key: Key('2'))); - await pumpChild(const Text('1', key: Key('1'))); + + // Pump four widgets with the two keys A and B in alternating order. + await pump(containerA); await tester.pump(const Duration(milliseconds: 1000)); - expect(find.text('1'), findsOneWidget); + await pump(containerB); + await tester.pump(const Duration(milliseconds: 500)); + await pump(containerA); + await tester.pump(const Duration(milliseconds: 250)); + await pump(containerB); + await tester.pump(const Duration(milliseconds: 125)); + + // All four widgets should still be animating in (the one pumped last) or + // out (the other ones), and thus have an associated FadeTransition each. + expect(find.byType(FadeTransition), findsNWidgets(4)); + final Iterable transitions = tester.widgetList( + find.byType(FadeTransition), + ); + + // The exponentially decaying timing used in pumping the widgets should have + // lined up all of the FadeTransitions' values to be the same. + for (final FadeTransition transition in transitions) { + expect(transition.opacity.value, moreOrLessEquals(0.125, epsilon: 0.001)); + } + + await tester.pumpAndSettle(); }); }