diff --git a/lib/screens/now_playing_screen.dart b/lib/screens/now_playing_screen.dart index 1d21c015..ba27c86e 100644 --- a/lib/screens/now_playing_screen.dart +++ b/lib/screens/now_playing_screen.dart @@ -1268,24 +1268,41 @@ class _NowPlayingScreenState extends ConsumerState { ), ), ), - Positioned.fromRect( - rect: Rect.lerp( - Rect.fromLTWH( - fullBleed ? 0 : (stage.maxWidth - artWidth) / 2, - fullBleed - ? -artworkTopInset - : (stage.maxHeight - artHeight) / 2, - fullBleed ? stage.maxWidth : artWidth, - fullBleed ? motionHeight : artHeight, + Consumer( + builder: (context, ref, child) => TweenAnimationBuilder( + tween: Tween( + end: !insetArtwork || ref.watch(playbackPlayingProvider) + ? 1 + : 0.73, ), - const Rect.fromLTWH( - 28, - 8, - compactCoverSize, - compactCoverSize, + duration: motion, + curve: Curves.easeInOutCubic, + builder: (context, scale, child) => Positioned.fromRect( + // Both cover layers travel to the actual resting size, + // including the smaller cover used while paused. + rect: Rect.lerp( + Rect.fromLTWH( + fullBleed + ? 0 + : (stage.maxWidth - artWidth * scale) / 2, + fullBleed + ? -artworkTopInset + : (stage.maxHeight - artHeight * scale) / 2, + fullBleed ? stage.maxWidth : artWidth * scale, + fullBleed ? motionHeight : artHeight * scale, + ), + const Rect.fromLTWH( + 28, + 8, + compactCoverSize, + compactCoverSize, + ), + progress, + )!, + child: child!, ), - progress, - )!, + child: child, + ), // Both layers share the header's progress, so reversing a // transition reuses the live cover instead of inserting a // duplicate while its previous instance is still fading. @@ -1318,50 +1335,26 @@ class _NowPlayingScreenState extends ConsumerState { Hero( tag: kNowPlayingArtworkHeroTag, child: expanded - ? Consumer( - builder: - ( - context, - ref, - child, - ) => AnimatedScale( - scale: - !insetArtwork || - ref.watch( - playbackPlayingProvider, - ) - ? 1 - : 0.73, - duration: motion, - curve: - Curves.easeInOutCubic, - child: child, + ? DecoratedBox( + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(12), + boxShadow: const [ + BoxShadow( + color: Color(0x40000000), + blurRadius: 28, + offset: Offset(0, 16), ), - child: DecoratedBox( - decoration: BoxDecoration( + ], + ), + child: _transitionArtwork( + _expandedArtworkKey, + ClipRRect( borderRadius: BorderRadius.circular( 12, ), - boxShadow: const [ - BoxShadow( - color: Color( - 0x40000000, - ), - blurRadius: 28, - offset: Offset(0, 16), - ), - ], - ), - child: _transitionArtwork( - _expandedArtworkKey, - ClipRRect( - borderRadius: - BorderRadius.circular( - 12, - ), - child: motionArtwork, - ), + child: motionArtwork, ), ), ) diff --git a/test/now_playing_lyrics_transition_test.dart b/test/now_playing_lyrics_transition_test.dart index b3407ee3..ce8bffa0 100644 --- a/test/now_playing_lyrics_transition_test.dart +++ b/test/now_playing_lyrics_transition_test.dart @@ -2099,6 +2099,49 @@ void main() { ); } + for (final playing in [false, true]) { + testWidgets( + 'cover returns from lyrics to its resting size (playing: $playing)', + (tester) async { + await pumpNowPlaying( + tester, + theme: MornyeTheme.build(Brightness.dark), + size: const Size(393, 852), + playback: PlaybackState( + playing: playing, + processingState: AudioProcessingState.ready, + updatePosition: const Duration(seconds: 2), + ), + ); + mediaItems.add(item('first')); + await tester.pumpAndSettle(); + final artwork = find.byType(MornyePlayerArtwork); + final restingBounds = tester.getRect(artwork); + final lyrics = find.byIcon(CupertinoIcons.quote_bubble); + final compact = find.byKey(const ValueKey('compact-player-artwork')); + await tester.tap(lyrics); + await tester.pumpAndSettle(); + final compactBounds = tester.getRect(compact); + await tester.tap(lyrics); + await tester.pump(); + expect(tester.getRect(artwork), compactBounds); + var previous = compactBounds; + for (var frame = 0; frame < 4; frame++) { + await tester.pump(const Duration(milliseconds: 80)); + final bounds = tester.getRect(artwork); + expect(bounds.width, greaterThan(previous.width)); + expect(bounds.width, lessThanOrEqualTo(restingBounds.width)); + expect(tester.getRect(compact), bounds); + previous = bounds; + } + await tester.pumpAndSettle(); + expect(tester.getRect(artwork), restingBounds); + expect(tester.takeException(), isNull); + await tester.pumpWidget(const SizedBox()); + }, + ); + } + testWidgets('header reverses in place and stays beside cover across panels', ( tester, ) async {