Compare commits

..
Author SHA1 Message Date
Your NameandClaude Opus 4.5 3852e83a3c Fix artist hero animation grey image during transition
The artist image was turning grey during the hero animation because
ArtistDetailsScreen was deferring image loading until after the 350ms
transition completed. This caused the destination hero widget to show
an empty container instead of the image.

Fix: Pass initialImageUrl from source screens to ArtistDetailsScreen
so the hero has the same image content from the start of the animation.
The higher-res image and color extraction still happen after transition.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 08:21:58 +00:00
6 changed files with 29 additions and 8 deletions
+7 -1
View File
@@ -500,10 +500,16 @@ class _AlbumDetailsScreenState extends State<AlbumDetailsScreen> with SingleTick
void _navigateToArtist() {
// Navigate to the first artist if available
if (widget.album.artists != null && widget.album.artists!.isNotEmpty) {
final artist = widget.album.artists!.first;
final maProvider = context.read<MusicAssistantProvider>();
final imageUrl = maProvider.getImageUrl(artist, size: 256);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArtistDetailsScreen(artist: widget.album.artists!.first),
builder: (context) => ArtistDetailsScreen(
artist: artist,
initialImageUrl: imageUrl,
),
),
);
}
+7 -3
View File
@@ -15,11 +15,13 @@ import '../utils/page_transitions.dart';
class ArtistDetailsScreen extends StatefulWidget {
final Artist artist;
final String? heroTagSuffix;
final String? initialImageUrl;
const ArtistDetailsScreen({
super.key,
super.key,
required this.artist,
this.heroTagSuffix,
this.initialImageUrl,
});
@override
@@ -44,12 +46,14 @@ class _ArtistDetailsScreenState extends State<ArtistDetailsScreen> {
void initState() {
super.initState();
_isFavorite = widget.artist.favorite ?? false;
// Use initial image URL immediately for smooth hero animation
_artistImageUrl = widget.initialImageUrl;
_loadArtistAlbums();
_loadArtistDescription();
_refreshFavoriteStatus();
// CRITICAL FIX: Defer image loading and color extraction until after transition
// This prevents expensive operations during hero animation
// Defer higher-res image loading and color extraction until after transition
// But hero animation now works because we have initialImageUrl
WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(const Duration(milliseconds: 350), () {
if (mounted) {
+4
View File
@@ -90,6 +90,9 @@ class LibraryArtistsScreen extends StatelessWidget {
}) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
final maProvider = context.read<MusicAssistantProvider>();
// Get image URL for hero animation
final imageUrl = maProvider.getImageUrl(artist, size: 256);
// RepaintBoundary isolates repaints to individual tiles
return RepaintBoundary(
@@ -116,6 +119,7 @@ class LibraryArtistsScreen extends StatelessWidget {
child: ArtistDetailsScreen(
artist: artist,
heroTagSuffix: 'library',
initialImageUrl: imageUrl,
),
),
);
+4 -3
View File
@@ -312,7 +312,10 @@ class _NewLibraryScreenState extends State<NewLibraryScreen>
}) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
final maProvider = context.read<MusicAssistantProvider>();
final suffix = '_library';
// Get image URL for hero animation
final imageUrl = maProvider.getImageUrl(artist, size: 256);
return RepaintBoundary(
child: ListTile(
@@ -322,9 +325,6 @@ class _NewLibraryScreenState extends State<NewLibraryScreen>
radius: 24,
imageSize: 128,
heroTag: HeroTags.artistImage + (artist.uri ?? artist.itemId) + suffix,
onImageLoaded: (imageUrl) {
// Store for adaptive colors on tap
},
),
title: Hero(
tag: HeroTags.artistName + (artist.uri ?? artist.itemId) + suffix,
@@ -348,6 +348,7 @@ class _NewLibraryScreenState extends State<NewLibraryScreen>
child: ArtistDetailsScreen(
artist: artist,
heroTagSuffix: 'library',
initialImageUrl: imageUrl,
),
),
);
+6 -1
View File
@@ -404,6 +404,8 @@ class SearchScreenState extends State<SearchScreen> {
Widget _buildArtistTile(Artist artist) {
final colorScheme = Theme.of(context).colorScheme;
final maProvider = context.read<MusicAssistantProvider>();
final imageUrl = maProvider.getImageUrl(artist, size: 256);
return ListTile(
leading: ArtistAvatar(
@@ -428,7 +430,10 @@ class SearchScreenState extends State<SearchScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArtistDetailsScreen(artist: artist),
builder: (context) => ArtistDetailsScreen(
artist: artist,
initialImageUrl: imageUrl,
),
),
);
},
+1
View File
@@ -60,6 +60,7 @@ class _ArtistCardState extends State<ArtistCard> {
child: ArtistDetailsScreen(
artist: widget.artist,
heroTagSuffix: widget.heroTagSuffix,
initialImageUrl: imageUrl,
),
),
);