diff --git a/packages/flutter/lib/src/material/page_transitions_theme.dart b/packages/flutter/lib/src/material/page_transitions_theme.dart index bcc201c469cc9..c2ff2098acbe7 100644 --- a/packages/flutter/lib/src/material/page_transitions_theme.dart +++ b/packages/flutter/lib/src/material/page_transitions_theme.dart @@ -291,6 +291,7 @@ class _ZoomEnterTransitionState extends State<_ZoomEnterTransition> with _ZoomTr bool get useSnapshot => !kIsWeb && widget.allowSnapshotting; late _ZoomEnterTransitionPainter delegate; + MediaQueryData? mediaQueryData; static final Animatable _fadeInTransition = Tween( begin: 0.0, @@ -355,6 +356,18 @@ class _ZoomEnterTransitionState extends State<_ZoomEnterTransition> with _ZoomTr super.didUpdateWidget(oldWidget); } + @override + void didChangeDependencies() { + // If the screen size changes during the transition, perhaps due to + // a keyboard dismissal, then ensure that contents are re-rasterized once. + final MediaQueryData? data = MediaQuery.maybeOf(context); + if (mediaQueryDataChanged(mediaQueryData, data)) { + controller.clear(); + } + mediaQueryData = data; + super.didChangeDependencies(); + } + @override void dispose() { widget.animation.removeListener(onAnimationValueChange); @@ -394,6 +407,7 @@ class _ZoomExitTransition extends StatefulWidget { class _ZoomExitTransitionState extends State<_ZoomExitTransition> with _ZoomTransitionBase { late _ZoomExitTransitionPainter delegate; + MediaQueryData? mediaQueryData; // See SnapshotWidget doc comment, this is disabled on web because the HTML backend doesn't // support this functionality and the canvaskit backend uses a single thread for UI and raster @@ -458,6 +472,18 @@ class _ZoomExitTransitionState extends State<_ZoomExitTransition> with _ZoomTran super.didUpdateWidget(oldWidget); } + @override + void didChangeDependencies() { + // If the screen size changes during the transition, perhaps due to + // a keyboard dismissal, then ensure that contents are re-rasterized once. + final MediaQueryData? data = MediaQuery.maybeOf(context); + if (mediaQueryDataChanged(mediaQueryData, data)) { + controller.clear(); + } + mediaQueryData = data; + super.didChangeDependencies(); + } + @override void dispose() { widget.animation.removeListener(onAnimationValueChange); @@ -804,6 +830,13 @@ mixin _ZoomTransitionBase { break; } } + + // Whether any of the properties that would impact the page transition + // changed. + bool mediaQueryDataChanged(MediaQueryData? oldData, MediaQueryData? newData) { + return oldData?.size != newData?.size || + oldData?.viewInsets != newData?.viewInsets; + } } class _ZoomEnterTransitionPainter extends SnapshotPainter { diff --git a/packages/flutter/test/material/page_test.dart b/packages/flutter/test/material/page_test.dart index 7c6dc815b60a1..b97bfd87c7308 100644 --- a/packages/flutter/test/material/page_test.dart +++ b/packages/flutter/test/material/page_test.dart @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +@Tags(['reduced-test-set']) import 'package:flutter/cupertino.dart' show CupertinoPageRoute; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -234,6 +236,47 @@ void main() { expect(find.text('Page 2'), findsNothing); }, variant: TargetPlatformVariant.only(TargetPlatform.android)); + testWidgets('test page transition (_ZoomPageTransition) with rasterization re-rasterizes when window size changes', (WidgetTester tester) async { + // Shrink the window size. + late Size oldSize; + try { + oldSize = tester.binding.window.physicalSize; + tester.binding.window.physicalSizeTestValue = const Size(1000, 1000); + + final Key key = GlobalKey(); + await tester.pumpWidget( + RepaintBoundary( + key: key, + child: MaterialApp( + onGenerateRoute: (RouteSettings settings) { + return MaterialPageRoute( + builder: (BuildContext context) { + return const Material(child: SizedBox.shrink()); + }, + ); + }, + ), + ), + ); + + tester.state(find.byType(Navigator)).pushNamed('/next'); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 50)); + + await expectLater(find.byKey(key), matchesGoldenFile('zoom_page_transition.small.png')); + + // Increase the window size. + tester.binding.window.physicalSizeTestValue = const Size(1000, 2000); + + await tester.pump(); + await tester.pump(const Duration(milliseconds: 50)); + + await expectLater(find.byKey(key), matchesGoldenFile('zoom_page_transition.big.png')); + } finally { + tester.binding.window.physicalSizeTestValue = oldSize; + } + }, variant: TargetPlatformVariant.only(TargetPlatform.android), skip: kIsWeb); // [intended] rasterization is not used on the web. + testWidgets('test fullscreen dialog transition', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp(