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'; import '../../models/track.dart';
/// GET /api/radio?seed_track=<uuid>&limit=<int>. Returns the seed at /// `GET /api/radio?seed_track=<uuid>&limit=<int>`. Returns the seed
/// index 0 followed by up to `limit-1` weighted-shuffle picks scored /// at index 0 followed by up to `limit-1` weighted-shuffle picks
/// by the server's recommendation engine. The shape matches what /// scored by the server's recommendation engine. The shape matches
/// `playerActions.playTracks` expects, so a radio start is just one /// what `playerActions.playTracks` expects, so a radio start is just
/// fetch + one playTracks call. /// one fetch + one playTracks call.
class RadioApi { class RadioApi {
RadioApi(this._dio); RadioApi(this._dio);
final Dio _dio; final Dio _dio;
@@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
import '../api/endpoints/likes.dart'; import '../api/endpoints/likes.dart';
import '../likes/like_button.dart'; import '../likes/like_button.dart';
import '../models/album.dart'; import '../models/album.dart';
import '../models/artist.dart';
import '../player/player_provider.dart'; import '../player/player_provider.dart';
import '../shared/widgets/server_image.dart'; import '../shared/widgets/server_image.dart';
import '../theme/theme_extension.dart'; import '../theme/theme_extension.dart';
@@ -219,30 +219,49 @@ class _TitleRow extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row(children: [ // Stack: title centered absolutely in the row; like + kebab pinned
Expanded( // to the right edge. Padding on the title equals the actions' width
child: Text( // so it stays optically centered without colliding with them.
media.title, return SizedBox(
style: TextStyle(color: fs.parchment, fontSize: 22), height: 32,
maxLines: 1, child: Stack(
overflow: TextOverflow.ellipsis, 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); 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). /// returned track list (seed at index 0 + recommended picks).
Future<void> startRadio(String trackId) async { Future<void> startRadio(String trackId) async {
final dio = await _ref.read(dioProvider.future); final dio = await _ref.read(dioProvider.future);