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:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user