fix(ui): align metadata and wrap track badges

This commit is contained in:
zarzet
2026-08-18 20:50:31 +07:00
parent 2cb5993d6e
commit 6d97151ee7
6 changed files with 132 additions and 50 deletions
+13 -20
View File
@@ -320,32 +320,25 @@ class _TrackItemWithStatus extends ConsumerWidget {
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Row(
children: [
Flexible(
child: ClickableArtistName(
artistName: track.artistName,
artistId: track.artistId,
coverUrl: track.coverUrl,
extensionId: extensionId,
style: Theme.of(context).textTheme.bodySmall
?.copyWith(
color: colorScheme.onSurfaceVariant,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
TrackMetadataBadgesLine(
primary: ClickableArtistName(
artistName: track.artistName,
artistId: track.artistId,
coverUrl: track.coverUrl,
extensionId: extensionId,
style: Theme.of(context).textTheme.bodySmall
?.copyWith(color: colorScheme.onSurfaceVariant),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
badges: [
...buildQualityBadges(
audioQuality: track.audioQuality,
audioModes: track.audioModes,
colorScheme: colorScheme,
explicit: track.isExplicit,
),
if (isInLocalLibrary) ...[
const SizedBox(width: 6),
const InLibraryBadge(),
],
if (isInLocalLibrary) const InLibraryBadge(),
],
),
],
+2 -1
View File
@@ -522,7 +522,8 @@ extension _TrackMetadataCards on _TrackMetadataScreenState {
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
SizedBox(
width: 100,
+38 -5
View File
@@ -144,8 +144,44 @@ class DolbyLogoPainter extends CustomPainter {
color != oldDelegate.color;
}
/// Convenience builder: returns a list of quality badge widgets for a track.
/// Pass the result into a Row using spread operator.
/// Keeps artist text and optional track badges responsive on narrow rows.
///
/// A [Row] with a flexible artist still overflows when the badges themselves
/// need more room than remains. [Wrap] moves whole badges to another run while
/// keeping the artist constrained and ellipsized when it alone is too long.
class TrackMetadataBadgesLine extends StatelessWidget {
const TrackMetadataBadgesLine({
super.key,
required this.primary,
this.badges = const [],
});
final Widget primary;
final List<Widget> badges;
@override
Widget build(BuildContext context) {
if (badges.isEmpty) return primary;
return LayoutBuilder(
builder: (context, constraints) => Wrap(
spacing: 6,
runSpacing: 4,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
ConstrainedBox(
constraints: BoxConstraints(maxWidth: constraints.maxWidth),
child: primary,
),
...badges,
],
),
);
}
}
/// Returns the quality-related badge widgets for a track. Spacing and wrapping
/// belong to [TrackMetadataBadgesLine] so the group stays responsive.
List<Widget> buildQualityBadges({
required String? audioQuality,
required String? audioModes,
@@ -154,17 +190,14 @@ List<Widget> buildQualityBadges({
}) {
final badges = <Widget>[];
if (explicit) {
badges.add(const SizedBox(width: 6));
badges.add(ExplicitBadge(colorScheme: colorScheme));
}
if (audioQuality != null && audioQuality.isNotEmpty) {
badges.add(const SizedBox(width: 6));
badges.add(
AudioQualityBadge(label: audioQuality, colorScheme: colorScheme),
);
}
if (audioModes != null && audioModes.contains('DOLBY_ATMOS')) {
badges.add(const SizedBox(width: 4));
badges.add(DolbyAtmosBadge(colorScheme: colorScheme));
}
return badges;
+19 -24
View File
@@ -76,36 +76,31 @@ class TrackListTile extends ConsumerWidget {
isSelected: isSelected,
leading: leading,
title: track.name,
subtitle: Row(
children: [
Flexible(
child: clickableArtist && !isSelectionMode
? ClickableArtistName(
artistName: track.artistName,
artistId: track.artistId,
coverUrl: track.coverUrl,
extensionId: track.source,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: colorScheme.onSurfaceVariant),
)
: Text(
track.artistName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
),
subtitle: TrackMetadataBadgesLine(
primary: clickableArtist && !isSelectionMode
? ClickableArtistName(
artistName: track.artistName,
artistId: track.artistId,
coverUrl: track.coverUrl,
extensionId: track.source,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: colorScheme.onSurfaceVariant),
)
: Text(
track.artistName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
badges: [
...buildQualityBadges(
audioQuality: track.audioQuality,
audioModes: track.audioModes,
colorScheme: colorScheme,
explicit: track.isExplicit,
),
if (isInLocalLibrary || isInHistory) ...[
const SizedBox(width: 6),
const InLibraryBadge(),
],
if (isInLocalLibrary || isInHistory) const InLibraryBadge(),
],
),
trailing: Row(