diff --git a/packages/flutter/lib/src/material/page_transitions_theme.dart b/packages/flutter/lib/src/material/page_transitions_theme.dart index cbcfa2cf264..26c3f97f1e8 100644 --- a/packages/flutter/lib/src/material/page_transitions_theme.dart +++ b/packages/flutter/lib/src/material/page_transitions_theme.dart @@ -48,7 +48,7 @@ class _FadeUpwardsPageTransition extends StatelessWidget { } // This transition is intended to match the default for Android P. -class _OpenUpwardsPageTransition extends StatelessWidget { +class _OpenUpwardsPageTransition extends StatefulWidget { const _OpenUpwardsPageTransition({ required this.animation, required this.secondaryAnimation, @@ -81,37 +81,77 @@ class _OpenUpwardsPageTransition extends StatelessWidget { final Animation secondaryAnimation; final Widget child; + @override + State<_OpenUpwardsPageTransition> createState() => _OpenUpwardsPageTransitionState(); +} + +class _OpenUpwardsPageTransitionState extends State<_OpenUpwardsPageTransition> { + late CurvedAnimation _primaryAnimation; + late CurvedAnimation _secondaryTranslationCurvedAnimation; + + @override + void initState() { + super.initState(); + _setAnimations(); + } + + @override + void didUpdateWidget(covariant _OpenUpwardsPageTransition oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.animation != widget.animation || + oldWidget.secondaryAnimation != widget.secondaryAnimation + ) { + _disposeAnimations(); + _setAnimations(); + } + } + + void _setAnimations() { + _primaryAnimation = CurvedAnimation( + parent: widget.animation, + curve: _OpenUpwardsPageTransition._transitionCurve, + reverseCurve: _OpenUpwardsPageTransition._transitionCurve.flipped, + ); + _secondaryTranslationCurvedAnimation = CurvedAnimation( + parent: widget.secondaryAnimation, + curve: _OpenUpwardsPageTransition._transitionCurve, + reverseCurve: _OpenUpwardsPageTransition._transitionCurve.flipped, + ); + } + + void _disposeAnimations() { + _primaryAnimation.dispose(); + _secondaryTranslationCurvedAnimation.dispose(); + } + + @override + void dispose() { + _disposeAnimations(); + super.dispose(); + } + + @override Widget build(BuildContext context) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { final Size size = constraints.biggest; - final CurvedAnimation primaryAnimation = CurvedAnimation( - parent: animation, - curve: _transitionCurve, - reverseCurve: _transitionCurve.flipped, - ); - // Gradually expose the new page from bottom to top. final Animation clipAnimation = Tween( begin: 0.0, end: size.height, - ).animate(primaryAnimation); + ).animate(_primaryAnimation); - final Animation opacityAnimation = _scrimOpacityTween.animate(primaryAnimation); - final Animation primaryTranslationAnimation = _primaryTranslationTween.animate(primaryAnimation); + final Animation opacityAnimation = _OpenUpwardsPageTransition._scrimOpacityTween.animate(_primaryAnimation); + final Animation primaryTranslationAnimation = _OpenUpwardsPageTransition._primaryTranslationTween.animate(_primaryAnimation); - final Animation secondaryTranslationAnimation = _secondaryTranslationTween.animate( - CurvedAnimation( - parent: secondaryAnimation, - curve: _transitionCurve, - reverseCurve: _transitionCurve.flipped, - ), + final Animation secondaryTranslationAnimation = _OpenUpwardsPageTransition._secondaryTranslationTween.animate( + _secondaryTranslationCurvedAnimation, ); return AnimatedBuilder( - animation: animation, + animation: widget.animation, builder: (BuildContext context, Widget? child) { return Container( color: Colors.black.withOpacity(opacityAnimation.value), @@ -129,10 +169,10 @@ class _OpenUpwardsPageTransition extends StatelessWidget { ); }, child: AnimatedBuilder( - animation: secondaryAnimation, + animation: widget.secondaryAnimation, child: FractionalTranslation( translation: primaryTranslationAnimation.value, - child: child, + child: widget.child, ), builder: (BuildContext context, Widget? child) { return FractionalTranslation( diff --git a/packages/flutter/test/material/page_transitions_theme_test.dart b/packages/flutter/test/material/page_transitions_theme_test.dart index 275c3107742..fcf58aba2db 100644 --- a/packages/flutter/test/material/page_transitions_theme_test.dart +++ b/packages/flutter/test/material/page_transitions_theme_test.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); @@ -97,7 +98,10 @@ void main() { expect(findZoomPageTransition(), findsOneWidget); }, variant: TargetPlatformVariant.only(TargetPlatform.android)); - testWidgets('PageTransitionsTheme override builds a _OpenUpwardsPageTransition', (WidgetTester tester) async { + testWidgets('PageTransitionsTheme override builds a _OpenUpwardsPageTransition', + // TODO(polina-c): remove when fixed https://github.com/flutter/flutter/issues/145600 [leak-tracking-opt-in] + experimentalLeakTesting: LeakTesting.settings.withTracked(classes: const ['CurvedAnimation']), + (WidgetTester tester) async { final Map routes = { '/': (BuildContext context) => Material( child: TextButton(