From 0134281b8c9c2f195fa5ab13626e4dffdb780854 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 11 May 2026 13:01:52 -0400 Subject: [PATCH] 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. --- flutter_client/lib/api/endpoints/radio.dart | 10 +-- .../lib/library/artist_detail_screen.dart | 1 + .../lib/player/now_playing_screen.dart | 65 ++++++++++++------- .../lib/player/player_provider.dart | 2 +- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/flutter_client/lib/api/endpoints/radio.dart b/flutter_client/lib/api/endpoints/radio.dart index b39774bb..b8e4ca5a 100644 --- a/flutter_client/lib/api/endpoints/radio.dart +++ b/flutter_client/lib/api/endpoints/radio.dart @@ -2,11 +2,11 @@ import 'package:dio/dio.dart'; import '../../models/track.dart'; -/// GET /api/radio?seed_track=&limit=. 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=&limit=`. 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; diff --git a/flutter_client/lib/library/artist_detail_screen.dart b/flutter_client/lib/library/artist_detail_screen.dart index c2123039..e13d276d 100644 --- a/flutter_client/lib/library/artist_detail_screen.dart +++ b/flutter_client/lib/library/artist_detail_screen.dart @@ -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'; diff --git a/flutter_client/lib/player/now_playing_screen.dart b/flutter_client/lib/player/now_playing_screen.dart index a2cdf704..ff518406 100644 --- a/flutter_client/lib/player/now_playing_screen.dart +++ b/flutter_client/lib/player/now_playing_screen.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, - ), - ]); + ); } } diff --git a/flutter_client/lib/player/player_provider.dart b/flutter_client/lib/player/player_provider.dart index 1b8d2283..baca1af3 100644 --- a/flutter_client/lib/player/player_provider.dart +++ b/flutter_client/lib/player/player_provider.dart @@ -104,7 +104,7 @@ class PlayerActions { await _ref.read(audioHandlerProvider).setVolume(v); } - /// Fetches /api/radio?seed_track= and starts playing the + /// Fetches `/api/radio?seed_track=` and starts playing the /// returned track list (seed at index 0 + recommended picks). Future startRadio(String trackId) async { final dio = await _ref.read(dioProvider.future);