feat(metadata): album-style header with centered square cover

The full-bleed cover made the Hero flight from square list thumbnails
morph into a fullscreen rect and back. The header now blurs the cover
as a backdrop and shows a centered rounded square (album header layout),
so the flight stays square end to end.
This commit is contained in:
zarzet
2026-07-14 09:09:53 +07:00
parent a11cff8b28
commit 9a205987ee
2 changed files with 38 additions and 4 deletions
+37 -4
View File
@@ -82,7 +82,7 @@ extension _TrackMetadataCards on _TrackMetadataScreenState {
bool showContent,
) {
final cacheWidth = coverCacheWidthForViewport(context);
final coverChild = _hasPath(_embeddedCoverPreviewPath)
Widget coverImage() => _hasPath(_embeddedCoverPreviewPath)
? Image.file(
File(_embeddedCoverPreviewPath!),
fit: BoxFit.cover,
@@ -117,13 +117,20 @@ extension _TrackMetadataCards on _TrackMetadataScreenState {
),
);
// Centered square cover over a blurred backdrop (same layout as the album
// header) so the Hero flight from a list thumbnail keeps its square shape.
final coverSize = (MediaQuery.sizeOf(context).width * 0.5)
.clamp(150.0, 210.0)
.toDouble();
return Stack(
fit: StackFit.expand,
children: [
Hero(
tag: _coverHeroTag,
child: Material(color: Colors.transparent, child: coverChild),
ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 32, sigmaY: 32),
child: coverImage(),
),
Container(color: Colors.black.withValues(alpha: 0.35)),
Positioned(
left: 0,
right: 0,
@@ -153,6 +160,32 @@ extension _TrackMetadataCards on _TrackMetadataScreenState {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Hero(
tag: _coverHeroTag,
child: Container(
width: coverSize,
height: coverSize,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.45),
blurRadius: 24,
offset: const Offset(0, 8),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: SizedBox(
width: coverSize,
height: coverSize,
child: coverImage(),
),
),
),
),
const SizedBox(height: 20),
Text(
trackName,
style: const TextStyle(
+1
View File
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'dart:ui' show ImageFilter;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';