♻️ Unified the track tile widget

This commit is contained in:
2025-12-20 00:40:36 +08:00
parent a86e8b1cab
commit aaba0382cf
11 changed files with 353 additions and 186 deletions

View File

@@ -355,29 +355,35 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
),
const Gap(8),
// Title & Artist
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
currentMetadata?.title ??
Uri.parse(media.uri).pathSegments.last,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
currentMetadata?.artist ?? 'Unknown Artist',
style: Theme.of(context).textTheme.bodySmall,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
Flexible(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
currentMetadata?.title ??
Uri.parse(media.uri).pathSegments.last,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
currentMetadata?.artist ?? 'Unknown Artist',
style: Theme.of(
context,
).textTheme.bodySmall,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
],

View File

@@ -1,7 +1,7 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:groovybox/data/db.dart' as db;
import 'package:groovybox/ui/widgets/universal_image.dart';
import 'package:styled_widget/styled_widget.dart';
class TrackTile extends StatelessWidget {
final db.Track track;
@@ -41,29 +41,20 @@ class TrackTile extends StatelessWidget {
borderRadius: BorderRadius.circular(8),
),
child: ListTile(
contentPadding:
padding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
contentPadding: padding ?? const EdgeInsets.symmetric(horizontal: 16),
leading: Row(
mainAxisSize: MainAxisSize.min,
children: [
?leading,
AspectRatio(
aspectRatio: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(8),
image: track.artUri != null
? DecorationImage(
image: FileImage(File(track.artUri!)),
fit: BoxFit.cover,
)
: null,
),
child: track.artUri == null
? const Icon(Icons.music_note, color: Colors.white54)
: null,
),
child: UniversalImage(
uri: track.artUri,
fit: BoxFit.cover,
borderRadius: BorderRadius.circular(8),
fallbackIcon: Icons.music_note,
fallbackIconSize: 24,
).clipRRect(all: 8),
),
],
),

View File

@@ -0,0 +1,130 @@
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class UniversalImage extends StatelessWidget {
final String? uri;
final BoxFit? fit;
final double? width;
final double? height;
final Widget? fallback;
final IconData? fallbackIcon;
final double? fallbackIconSize;
final Color? fallbackIconColor;
final BorderRadius? borderRadius;
final bool useDecorationImage;
const UniversalImage({
super.key,
this.uri,
this.fit = BoxFit.cover,
this.width,
this.height,
this.fallback,
this.fallbackIcon = Icons.image,
this.fallbackIconSize = 48,
this.fallbackIconColor = Colors.white54,
this.borderRadius,
this.useDecorationImage = false,
});
bool _isNetworkUri(String uri) {
return uri.startsWith('http://') || uri.startsWith('https://');
}
Widget _buildFallback() {
if (fallback != null) {
return fallback!;
}
final icon = Icon(
fallbackIcon,
size: fallbackIconSize,
color: fallbackIconColor,
);
if (borderRadius != null) {
return Container(
width: width,
height: height,
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: borderRadius,
),
child: icon,
);
}
return Container(
width: width,
height: height,
color: Colors.grey[800],
child: icon,
);
}
Widget _buildNetworkImage() {
if (useDecorationImage) {
return CachedNetworkImage(
imageUrl: uri!,
fit: fit,
width: width,
height: height,
placeholder: (context, url) => Container(
width: width,
height: height,
color: Colors.grey[800],
child: const CircularProgressIndicator(),
),
errorWidget: (context, url, error) => _buildFallback(),
);
}
return CachedNetworkImage(
imageUrl: uri!,
fit: fit,
width: width,
height: height,
placeholder: (context, url) => Container(
width: width,
height: height,
color: Colors.grey[800],
child: const CircularProgressIndicator(),
),
errorWidget: (context, url, error) => _buildFallback(),
);
}
Widget _buildFileImage() {
if (useDecorationImage) {
return Image.file(
File(uri!),
fit: fit,
width: width,
height: height,
errorBuilder: (context, error, stackTrace) => _buildFallback(),
);
}
return Image.file(
File(uri!),
fit: fit,
width: width,
height: height,
errorBuilder: (context, error, stackTrace) => _buildFallback(),
);
}
@override
Widget build(BuildContext context) {
if (uri == null || uri!.isEmpty) {
return _buildFallback();
}
if (_isNetworkUri(uri!)) {
return _buildNetworkImage();
} else {
return _buildFileImage();
}
}
}