fix(flutter): CI analyze + center title in full player

- artist_detail_screen.dart missed an `import '../models/artist.dart'`
  for the ArtistRef seed parameter; analyze flagged undefined_class.
- radio.dart + player_provider.dart doc comments wrapped URL
  parameters in <...> which lint reads as HTML. Switched to backticks.

Full player title is now centered absolutely via a Stack: title with
horizontal padding equal to the actions cluster width sits at the
optical center, while LikeButton + TrackActionsButton are pinned to
the right edge with Positioned. Fixed-height SizedBox(32) keeps the
row stable when the title wraps to a single ellipsized line.
This commit is contained in:
2026-05-11 13:01:52 -04:00
parent 4dbb3190ff
commit 0134281b8c
4 changed files with 49 additions and 29 deletions
+5 -5
View File
@@ -2,11 +2,11 @@ import 'package:dio/dio.dart';
import '../../models/track.dart';
/// GET /api/radio?seed_track=<uuid>&limit=<int>. Returns the seed at
/// index 0 followed by up to `limit-1` weighted-shuffle picks scored
/// by the server's recommendation engine. The shape matches what
/// `playerActions.playTracks` expects, so a radio start is just one
/// fetch + one playTracks call.
/// `GET /api/radio?seed_track=<uuid>&limit=<int>`. Returns the seed
/// at index 0 followed by up to `limit-1` weighted-shuffle picks
/// scored by the server's recommendation engine. The shape matches
/// what `playerActions.playTracks` expects, so a radio start is just
/// one fetch + one playTracks call.
class RadioApi {
RadioApi(this._dio);
final Dio _dio;
@@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
import '../api/endpoints/likes.dart';
import '../likes/like_button.dart';
import '../models/album.dart';
import '../models/artist.dart';
import '../player/player_provider.dart';
import '../shared/widgets/server_image.dart';
import '../theme/theme_extension.dart';
@@ -219,30 +219,49 @@ class _TitleRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(children: [
Expanded(
child: Text(
media.title,
style: TextStyle(color: fs.parchment, fontSize: 22),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
// Stack: title centered absolutely in the row; like + kebab pinned
// to the right edge. Padding on the title equals the actions' width
// so it stays optically centered without colliding with them.
return SizedBox(
height: 32,
child: Stack(
alignment: Alignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 64),
child: Text(
media.title,
style: TextStyle(color: fs.parchment, fontSize: 22),
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
),
Positioned(
right: 0,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
LikeButton(kind: LikeKind.track, id: media.id, size: 22),
TrackActionsButton(
track: TrackRef(
id: media.id,
title: media.title,
albumId: (media.extras?['album_id'] as String?) ?? '',
albumTitle: media.album ?? '',
artistId: (media.extras?['artist_id'] as String?) ?? '',
artistName: media.artist ?? '',
durationSec: media.duration?.inSeconds ?? 0,
streamUrl: '',
),
hideQueueActions: true,
),
],
),
),
],
),
LikeButton(kind: LikeKind.track, id: media.id, size: 22),
TrackActionsButton(
track: TrackRef(
id: media.id,
title: media.title,
albumId: (media.extras?['album_id'] as String?) ?? '',
albumTitle: media.album ?? '',
artistId: (media.extras?['artist_id'] as String?) ?? '',
artistName: media.artist ?? '',
durationSec: media.duration?.inSeconds ?? 0,
streamUrl: '',
),
hideQueueActions: true,
),
]);
);
}
}
@@ -104,7 +104,7 @@ class PlayerActions {
await _ref.read(audioHandlerProvider).setVolume(v);
}
/// Fetches /api/radio?seed_track=<id> and starts playing the
/// Fetches `/api/radio?seed_track=<id>` and starts playing the
/// returned track list (seed at index 0 + recommended picks).
Future<void> startRadio(String trackId) async {
final dio = await _ref.read(dioProvider.future);