Skip to content

Commit 7c76dee

Browse files
authored
Make heroes fly on pushReplacement (flutter#30228)
1 parent d4c4f56 commit 7c76dee

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

packages/flutter/lib/src/widgets/heroes.dart

+9
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,15 @@ class HeroController extends NavigatorObserver {
621621
_maybeStartHeroTransition(route, previousRoute, HeroFlightDirection.pop, false);
622622
}
623623

624+
@override
625+
void didReplace({ Route<dynamic> newRoute, Route<dynamic> oldRoute }) {
626+
assert(navigator != null);
627+
if (newRoute?.isCurrent == true) {
628+
// Only run hero animations if the top-most route got replaced.
629+
_maybeStartHeroTransition(oldRoute, newRoute, HeroFlightDirection.push, false);
630+
}
631+
}
632+
624633
@override
625634
void didStartUserGesture(Route<dynamic> route, Route<dynamic> previousRoute) {
626635
assert(navigator != null);

packages/flutter/test/widgets/heroes_test.dart

+87
Original file line numberDiff line numberDiff line change
@@ -1692,4 +1692,91 @@ void main() {
16921692

16931693
expect(tester.takeException(), isAssertionError);
16941694
});
1695+
1696+
testWidgets('Heroes fly on pushReplacement', (WidgetTester tester) async {
1697+
// Regression test for https://github.com/flutter/flutter/issues/28041.
1698+
1699+
const String heroTag = 'foo';
1700+
final GlobalKey<NavigatorState> navigator = GlobalKey();
1701+
final Key smallContainer = UniqueKey();
1702+
final Key largeContainer = UniqueKey();
1703+
1704+
await tester.pumpWidget(
1705+
MaterialApp(
1706+
navigatorKey: navigator,
1707+
home: Center(
1708+
child: Card(
1709+
child: Hero(
1710+
tag: heroTag,
1711+
child: Container(
1712+
key: largeContainer,
1713+
color: Colors.red,
1714+
height: 200.0,
1715+
width: 200.0,
1716+
),
1717+
),
1718+
),
1719+
),
1720+
),
1721+
);
1722+
1723+
// The initial setup.
1724+
expect(find.byKey(largeContainer), isOnstage);
1725+
expect(find.byKey(largeContainer), isInCard);
1726+
expect(find.byKey(smallContainer, skipOffstage: false), findsNothing);
1727+
1728+
navigator.currentState.pushReplacement(
1729+
MaterialPageRoute<void>(
1730+
builder: (BuildContext context) {
1731+
return Center(
1732+
child: Card(
1733+
child: Hero(
1734+
tag: heroTag,
1735+
child: Container(
1736+
key: smallContainer,
1737+
color: Colors.red,
1738+
height: 100.0,
1739+
width: 100.0,
1740+
),
1741+
),
1742+
),
1743+
);
1744+
}
1745+
),
1746+
);
1747+
await tester.pump();
1748+
1749+
// The second route exists offstage.
1750+
expect(find.byKey(largeContainer), isOnstage);
1751+
expect(find.byKey(largeContainer), isInCard);
1752+
expect(find.byKey(smallContainer, skipOffstage: false), isOffstage);
1753+
expect(find.byKey(smallContainer, skipOffstage: false), isInCard);
1754+
1755+
await tester.pump();
1756+
1757+
// The hero started flying.
1758+
expect(find.byKey(largeContainer), findsNothing);
1759+
expect(find.byKey(smallContainer), isOnstage);
1760+
expect(find.byKey(smallContainer), isNotInCard);
1761+
1762+
await tester.pump(const Duration(milliseconds: 100));
1763+
1764+
// The hero is in-flight.
1765+
expect(find.byKey(largeContainer), findsNothing);
1766+
expect(find.byKey(smallContainer), isOnstage);
1767+
expect(find.byKey(smallContainer), isNotInCard);
1768+
final Size size = tester.getSize(find.byKey(smallContainer));
1769+
expect(size.height, greaterThan(100));
1770+
expect(size.width, greaterThan(100));
1771+
expect(size.height, lessThan(200));
1772+
expect(size.width, lessThan(200));
1773+
1774+
await tester.pumpAndSettle();
1775+
1776+
// The transition has ended.
1777+
expect(find.byKey(largeContainer), findsNothing);
1778+
expect(find.byKey(smallContainer), isOnstage);
1779+
expect(find.byKey(smallContainer), isInCard);
1780+
expect(tester.getSize(find.byKey(smallContainer)), const Size(100,100));
1781+
});
16951782
}

0 commit comments

Comments
 (0)