From 7e429f8373387ea51c3301660bcbcd4d00b96587 Mon Sep 17 00:00:00 2001 From: Padma Dev Date: Tue, 11 Aug 2026 17:32:20 +0700 Subject: [PATCH] feat(playlists): add play actions (#506) --- lib/screens/library_tracks_folder_screen.dart | 45 ++++++++++++------- lib/screens/playlist_screen.dart | 29 ++++++++++++ 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/lib/screens/library_tracks_folder_screen.dart b/lib/screens/library_tracks_folder_screen.dart index 140d9a33..ff46a27b 100644 --- a/lib/screens/library_tracks_folder_screen.dart +++ b/lib/screens/library_tracks_folder_screen.dart @@ -17,6 +17,7 @@ import 'package:spotiflac_android/utils/file_access.dart'; import 'package:spotiflac_android/providers/download_queue_provider.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; import 'package:spotiflac_android/providers/library_collections_provider.dart'; +import 'package:spotiflac_android/providers/music_player_provider.dart'; import 'package:spotiflac_android/providers/playback_provider.dart'; import 'package:spotiflac_android/providers/local_library_provider.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; @@ -595,11 +596,9 @@ class _LibraryTracksFolderScreenState ? Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - _buildHeaderActionPlaceholder(), - const SizedBox(width: 12), - _buildDownloadAllCenterButton(entries), - const SizedBox(width: 12), - _buildHeaderActionPlaceholder(), + Flexible(child: _buildPlayAllCenterButton(entries)), + const SizedBox(width: 8), + Flexible(child: _buildDownloadAllCenterButton(entries)), ], ) : null, @@ -678,24 +677,38 @@ class _LibraryTracksFolderScreenState ); } - Widget _buildHeaderActionPlaceholder() => - const SizedBox(width: 48, height: 48); + Widget _buildPlayAllCenterButton(List entries) { + final tracks = entries.map((e) => e.track).toList(growable: false); + return HeaderFilledButton( + icon: Icons.play_arrow_rounded, + label: context.l10n.tooltipPlay, + onPressed: tracks.isEmpty ? null : () => _playAll(tracks), + ); + } Widget _buildDownloadAllCenterButton(List entries) { final tracks = entries.map((e) => e.track).toList(growable: false); - return FilledButton.icon( + return HeaderFilledButton( + icon: Icons.download_rounded, + label: context.l10n.downloadAllCount(tracks.length), onPressed: tracks.isEmpty ? null : () => _confirmDownloadAll(tracks), - icon: const Icon(Icons.download_rounded, size: 18), - label: Text(context.l10n.downloadAllCount(tracks.length)), - style: FilledButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: Colors.black87, - minimumSize: const Size(0, 48), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), - ), ); } + Future _playAll(List tracks) async { + if (tracks.isEmpty) return; + + try { + await ref.read(musicPlayerControllerProvider).setShuffle(false); + await ref.read(playbackProvider.notifier).playTrackList(tracks); + } catch (e) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.snackbarCannotOpenFile('$e'))), + ); + } + } + void _confirmDownloadAll(List tracks) { confirmDownloadAllDialog( context, diff --git a/lib/screens/playlist_screen.dart b/lib/screens/playlist_screen.dart index a49097ab..bd10c21a 100644 --- a/lib/screens/playlist_screen.dart +++ b/lib/screens/playlist_screen.dart @@ -11,6 +11,8 @@ import 'package:spotiflac_android/models/track.dart'; import 'package:spotiflac_android/providers/download_queue_provider.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; import 'package:spotiflac_android/providers/library_collections_provider.dart'; +import 'package:spotiflac_android/providers/music_player_provider.dart'; +import 'package:spotiflac_android/providers/playback_provider.dart'; import 'package:spotiflac_android/utils/image_cache_utils.dart'; import 'package:spotiflac_android/utils/cover_art_utils.dart'; import 'package:spotiflac_android/utils/adaptive_layout.dart'; @@ -338,6 +340,8 @@ class _PlaylistScreenState extends ConsumerState children: [ _buildLoveAllButton(), const SizedBox(width: 12), + Flexible(child: _buildPlayDownloadedButton(context)), + const SizedBox(width: 8), Flexible(child: _buildDownloadAllCenterButton(context)), const SizedBox(width: 12), _buildAddToPlaylistButton(context), @@ -489,6 +493,16 @@ class _PlaylistScreenState extends ConsumerState ); } + Widget _buildPlayDownloadedButton(BuildContext context) { + return HeaderFilledButton( + icon: Icons.play_arrow_rounded, + label: context.l10n.tooltipPlay, + onPressed: _tracks.isEmpty + ? null + : () => _playDownloadedPlaylist(context), + ); + } + Widget _buildDownloadAllCenterButton(BuildContext context) { return HeaderFilledButton( icon: Icons.download_rounded, @@ -523,6 +537,21 @@ class _PlaylistScreenState extends ConsumerState Future _loveAll(List tracks) => loveAllTracks(context, ref, tracks); + Future _playDownloadedPlaylist(BuildContext context) async { + final tracks = List.from(_tracks, growable: false); + if (tracks.isEmpty) return; + + try { + await ref.read(musicPlayerControllerProvider).setShuffle(false); + await ref.read(playbackProvider.notifier).playTrackList(tracks); + } catch (e) { + if (!mounted || !context.mounted) return; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.snackbarCannotOpenFile('$e'))), + ); + } + } + void _downloadAll(BuildContext context) { _downloadTracks(context, _tracks); }