mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-22 19:07:16 +02:00
chore: cleanup unused code and dead imports
This commit is contained in:
@@ -13,7 +13,6 @@ import 'package:spotiflac_android/providers/recent_access_provider.dart';
|
||||
import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
import 'package:spotiflac_android/widgets/download_service_picker.dart';
|
||||
|
||||
/// Simple in-memory cache for album tracks
|
||||
class _AlbumCache {
|
||||
static final Map<String, _CacheEntry> _cache = {};
|
||||
static const Duration _ttl = Duration(minutes: 10);
|
||||
@@ -39,7 +38,6 @@ class _CacheEntry {
|
||||
_CacheEntry(this.tracks, this.expiresAt);
|
||||
}
|
||||
|
||||
/// Album detail screen with Material Expressive 3 design
|
||||
class AlbumScreen extends ConsumerStatefulWidget {
|
||||
final String albumId;
|
||||
final String albumName;
|
||||
@@ -99,7 +97,6 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
// Show title in AppBar when scrolled past the header (320 - kToolbarHeight + info card top)
|
||||
final shouldShow = _scrollController.offset > 280;
|
||||
if (shouldShow != _showTitleInAppBar) {
|
||||
setState(() => _showTitleInAppBar = shouldShow);
|
||||
@@ -121,7 +118,6 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
// Ignore palette extraction errors
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,12 +128,8 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
|
||||
if (widget.albumId.startsWith('deezer:')) {
|
||||
final deezerAlbumId = widget.albumId.replaceFirst('deezer:', '');
|
||||
// ignore: avoid_print
|
||||
print('[AlbumScreen] Fetching from Deezer: $deezerAlbumId');
|
||||
metadata = await PlatformBridge.getDeezerMetadata('album', deezerAlbumId);
|
||||
} else {
|
||||
// ignore: avoid_print
|
||||
print('[AlbumScreen] Fetching from Spotify with fallback: ${widget.albumId}');
|
||||
final url = 'https://open.spotify.com/album/${widget.albumId}';
|
||||
metadata = await PlatformBridge.getSpotifyMetadataWithFallback(url);
|
||||
}
|
||||
@@ -219,7 +211,7 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
expandedHeight: 320,
|
||||
pinned: true,
|
||||
stretch: true,
|
||||
backgroundColor: colorScheme.surface, // Use theme color for collapsed state
|
||||
backgroundColor: colorScheme.surface,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
title: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
@@ -261,7 +253,6 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// Cover image centered - fade out when collapsing
|
||||
AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
opacity: showContent ? 1.0 : 0.0,
|
||||
@@ -449,7 +440,6 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Build error widget with special handling for rate limit (429)
|
||||
Widget _buildErrorWidget(String error, ColorScheme colorScheme) {
|
||||
final isRateLimit = error.contains('429') ||
|
||||
error.toLowerCase().contains('rate limit') ||
|
||||
@@ -512,7 +502,6 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Separate Consumer widget for each track - only rebuilds when this specific track's status changes
|
||||
class _AlbumTrackItem extends ConsumerWidget {
|
||||
final Track track;
|
||||
final VoidCallback onDownload;
|
||||
|
||||
@@ -97,7 +97,6 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
int? _monthlyListeners;
|
||||
String? _error;
|
||||
|
||||
// Sticky title state
|
||||
bool _showTitleInAppBar = false;
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@@ -310,7 +309,6 @@ return Scaffold(
|
||||
);
|
||||
}
|
||||
|
||||
/// Build Spotify-style header with full-width image and artist name overlay
|
||||
Widget _buildHeader(BuildContext context, ColorScheme colorScheme) {
|
||||
String? imageUrl = _headerImageUrl;
|
||||
if (imageUrl == null || imageUrl.isEmpty) {
|
||||
@@ -479,7 +477,6 @@ if (hasValidImage)
|
||||
);
|
||||
}
|
||||
|
||||
/// Build a single popular track item with dynamic download status
|
||||
Widget _buildPopularTrackItem(int rank, Track track, ColorScheme colorScheme) {
|
||||
final queueItem = ref.watch(
|
||||
downloadQueueLookupProvider.select((lookup) => lookup.byTrackId[track.id]),
|
||||
@@ -608,7 +605,6 @@ if (hasValidImage)
|
||||
_downloadTrack(track);
|
||||
}
|
||||
|
||||
/// Build download button with status indicator for popular tracks
|
||||
Widget _buildPopularDownloadButton({
|
||||
required Track track,
|
||||
required ColorScheme colorScheme,
|
||||
|
||||
@@ -77,7 +77,6 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen> {
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
// Ignore palette extraction errors
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,9 +507,7 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen> {
|
||||
Widget _buildTrackList(BuildContext context, ColorScheme colorScheme, List<DownloadHistoryItem> tracks) {
|
||||
final discMap = _groupTracksByDisc(tracks);
|
||||
|
||||
// Single disc - use simple list
|
||||
if (discMap.length <= 1) {
|
||||
// Single disc - use simple list
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
@@ -525,7 +522,6 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
// Multiple discs - build list with separators
|
||||
final discNumbers = discMap.keys.toList()..sort();
|
||||
final List<Widget> children = [];
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when trackState changes - used to sync search bar with state
|
||||
void _onTrackStateChanged(TrackState? previous, TrackState next) {
|
||||
if (previous != null &&
|
||||
!next.hasContent &&
|
||||
@@ -96,7 +95,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
|
||||
if (searchProvider == null || searchProvider.isEmpty) return false;
|
||||
|
||||
// Check if the extension is enabled and has search capability
|
||||
final extension = extState.extensions.where((e) => e.id == searchProvider && e.enabled).firstOrNull;
|
||||
return extension != null;
|
||||
}
|
||||
@@ -130,10 +128,7 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute live search with concurrency protection
|
||||
/// Prevents race conditions in extensions by ensuring only one search runs at a time
|
||||
Future<void> _executeLiveSearch(String query) async {
|
||||
// If a search is already in progress, queue this one
|
||||
if (_isLiveSearchInProgress) {
|
||||
_pendingLiveSearchQuery = query;
|
||||
return;
|
||||
@@ -151,13 +146,10 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
final pending = _pendingLiveSearchQuery;
|
||||
_pendingLiveSearchQuery = null;
|
||||
|
||||
// Execute pending query if it's different from what we just searched
|
||||
// and still matches current text field content
|
||||
if (pending != null &&
|
||||
pending != query &&
|
||||
mounted &&
|
||||
_urlController.text.trim() == pending) {
|
||||
// Small delay to let extension's state settle
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
if (mounted && _urlController.text.trim() == pending) {
|
||||
_executeLiveSearch(pending);
|
||||
@@ -224,7 +216,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
ref.read(settingsProvider.notifier).setHasSearchedBefore();
|
||||
}
|
||||
|
||||
/// Navigate to detail screen based on fetched content type
|
||||
void _navigateToDetailIfNeeded() {
|
||||
final trackState = ref.read(trackProvider);
|
||||
|
||||
@@ -356,7 +347,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
// ignore: use_build_context_synchronously
|
||||
final l10n = context.l10n;
|
||||
|
||||
// Show quality picker if enabled in settings
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
DownloadServicePicker.show(
|
||||
this.context,
|
||||
@@ -676,7 +666,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
);
|
||||
}
|
||||
|
||||
/// Build recent access history section (shown when search focused)
|
||||
Widget _buildRecentAccess(List<RecentAccessItem> items, ColorScheme colorScheme) {
|
||||
final historyItems = ref.read(downloadHistoryProvider).items;
|
||||
|
||||
@@ -690,9 +679,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
albumGroups.putIfAbsent(albumKey, () => []).add(h);
|
||||
}
|
||||
|
||||
// Convert to RecentAccessItem based on track count:
|
||||
// - 1 track: show as individual Track
|
||||
// - 2+ tracks: show as Album
|
||||
final downloadItems = <RecentAccessItem>[];
|
||||
for (final entry in albumGroups.entries) {
|
||||
final tracks = entry.value;
|
||||
@@ -703,7 +689,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
: mostRecent.artistName;
|
||||
|
||||
if (tracks.length == 1) {
|
||||
// Single track - show as Track
|
||||
downloadItems.add(RecentAccessItem(
|
||||
id: mostRecent.spotifyId ?? mostRecent.id,
|
||||
name: mostRecent.trackName,
|
||||
@@ -714,7 +699,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
providerId: 'download',
|
||||
));
|
||||
} else {
|
||||
// Multiple tracks - show as Album
|
||||
downloadItems.add(RecentAccessItem(
|
||||
id: '${mostRecent.albumName}|$artistForKey',
|
||||
name: mostRecent.albumName,
|
||||
@@ -727,10 +711,8 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by most recent and take top 10
|
||||
downloadItems.sort((a, b) => b.accessedAt.compareTo(a.accessedAt));
|
||||
|
||||
// Filter out hidden downloads (use ref.watch for reactivity)
|
||||
|
||||
final hiddenIds = ref.watch(recentAccessProvider.select((s) => s.hiddenDownloadIds));
|
||||
final visibleDownloads = downloadItems
|
||||
.where((item) => !hiddenIds.contains(item.id))
|
||||
@@ -768,11 +750,9 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
if (uniqueItems.isNotEmpty)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// Hide ALL download items (not just visible ones)
|
||||
for (final item in downloadItems) {
|
||||
ref.read(recentAccessProvider.notifier).hideDownloadFromRecents(item.id);
|
||||
}
|
||||
// Clear non-download recent history
|
||||
ref.read(recentAccessProvider.notifier).clearHistory();
|
||||
},
|
||||
child: Text(
|
||||
@@ -784,7 +764,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (uniqueItems.isEmpty && hasHiddenDownloads)
|
||||
// Show "Show All" button when recents is empty but there are hidden downloads
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||
@@ -897,10 +876,8 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
icon: Icon(Icons.close, size: 20, color: colorScheme.onSurfaceVariant),
|
||||
onPressed: () {
|
||||
if (item.providerId == 'download') {
|
||||
// For download items, hide from recents without deleting the file
|
||||
ref.read(recentAccessProvider.notifier).hideDownloadFromRecents(item.id);
|
||||
} else {
|
||||
// For other items, remove from recent history
|
||||
ref.read(recentAccessProvider.notifier).removeItem(item);
|
||||
}
|
||||
},
|
||||
@@ -936,7 +913,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
));
|
||||
}
|
||||
case RecentAccessType.album:
|
||||
// Handle downloaded albums - navigate to DownloadedAlbumScreen
|
||||
if (item.providerId == 'download') {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => DownloadedAlbumScreen(
|
||||
@@ -1000,7 +976,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
);
|
||||
}
|
||||
|
||||
/// Build error widget with special handling for rate limit (429)
|
||||
Widget _buildErrorWidget(String error, ColorScheme colorScheme) {
|
||||
final isRateLimit = error.contains('429') ||
|
||||
error.toLowerCase().contains('rate limit') ||
|
||||
@@ -1427,7 +1402,6 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
));
|
||||
}
|
||||
|
||||
/// Get search hint based on selected provider
|
||||
String _getSearchHint() {
|
||||
final settings = ref.read(settingsProvider);
|
||||
final searchProvider = settings.searchProvider;
|
||||
@@ -1474,11 +1448,8 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
),
|
||||
prefixIcon: _SearchProviderDropdown(
|
||||
onProviderChanged: () {
|
||||
// Reset search state when provider changes
|
||||
_lastSearchQuery = null;
|
||||
// Force rebuild to update hint text
|
||||
setState(() {});
|
||||
// Re-trigger search if there's text
|
||||
final text = _urlController.text.trim();
|
||||
if (text.isNotEmpty && text.length >= _minLiveSearchChars) {
|
||||
_performSearch(text);
|
||||
@@ -1514,9 +1485,7 @@ class _HomeTabState extends ConsumerState<HomeTab> with AutomaticKeepAliveClient
|
||||
);
|
||||
}
|
||||
|
||||
/// Handle Enter key press - search or fetch URL
|
||||
void _onSearchSubmitted() {
|
||||
// Cancel any pending live search since user explicitly pressed enter
|
||||
_liveSearchDebounce?.cancel();
|
||||
_pendingLiveSearchQuery = null;
|
||||
|
||||
@@ -1549,13 +1518,11 @@ class _SearchProviderDropdown extends ConsumerWidget {
|
||||
final extState = ref.watch(extensionProvider);
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// Get current provider info
|
||||
final currentProvider = settings.searchProvider;
|
||||
final searchProviders = extState.extensions
|
||||
.where((ext) => ext.enabled && ext.hasCustomSearch)
|
||||
.toList();
|
||||
|
||||
// Find current provider extension
|
||||
Extension? currentExt;
|
||||
if (currentProvider != null && currentProvider.isNotEmpty) {
|
||||
currentExt = searchProviders.where((e) => e.id == currentProvider).firstOrNull;
|
||||
@@ -1567,12 +1534,10 @@ class _SearchProviderDropdown extends ConsumerWidget {
|
||||
if (currentExt != null) {
|
||||
iconPath = currentExt.iconPath;
|
||||
if (currentExt.searchBehavior?.icon != null) {
|
||||
// Use search behavior icon if available
|
||||
displayIcon = _getIconFromName(currentExt.searchBehavior!.icon!);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't show dropdown if no custom search providers available
|
||||
if (searchProviders.isEmpty) {
|
||||
return const Icon(Icons.search);
|
||||
}
|
||||
@@ -1608,15 +1573,13 @@ class _SearchProviderDropdown extends ConsumerWidget {
|
||||
offset: const Offset(0, 40),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
onSelected: (String providerId) {
|
||||
// Empty string means default (Deezer/Spotify)
|
||||
final provider = providerId.isEmpty ? null : providerId;
|
||||
ref.read(settingsProvider.notifier).setSearchProvider(provider);
|
||||
onProviderChanged?.call();
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
// Default option (Deezer/Spotify based on metadata source)
|
||||
PopupMenuItem<String>(
|
||||
value: '', // Empty string = default provider
|
||||
value: '',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
@@ -1716,7 +1679,6 @@ class _SearchProviderDropdown extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Separate Consumer widget for each track item - only rebuilds when this specific track's status changes
|
||||
class _TrackItemWithStatus extends ConsumerWidget {
|
||||
final Track track;
|
||||
final int index;
|
||||
@@ -2028,7 +1990,6 @@ class _CollectionItemWidget extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Screen for viewing extension album with track fetching
|
||||
class ExtensionAlbumScreen extends ConsumerStatefulWidget {
|
||||
final String extensionId;
|
||||
final String albumId;
|
||||
@@ -2299,7 +2260,6 @@ class _ExtensionPlaylistScreenState extends ConsumerState<ExtensionPlaylistScree
|
||||
}
|
||||
}
|
||||
|
||||
/// Screen for viewing extension artist with album fetching
|
||||
class ExtensionArtistScreen extends ConsumerStatefulWidget {
|
||||
final String extensionId;
|
||||
final String artistId;
|
||||
|
||||
@@ -120,7 +120,6 @@ class _MainShellState extends ConsumerState<MainShell> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle back press with double-tap to exit
|
||||
void _handleBackPress() {
|
||||
final trackState = ref.read(trackProvider);
|
||||
|
||||
@@ -174,9 +173,6 @@ class _MainShellState extends ConsumerState<MainShell> {
|
||||
|
||||
final isKeyboardVisible = MediaQuery.of(context).viewInsets.bottom > 0;
|
||||
|
||||
// Determine if we can pop (for predictive back animation)
|
||||
// canPop is true when we're at root with no content - enables predictive back gesture
|
||||
// IMPORTANT: Never allow pop when keyboard is visible to prevent accidental navigation
|
||||
final canPop = _currentIndex == 0 &&
|
||||
!trackState.hasSearchText &&
|
||||
!trackState.hasContent &&
|
||||
@@ -250,8 +246,6 @@ class _MainShellState extends ConsumerState<MainShell> {
|
||||
canPop: canPop,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
if (didPop) {
|
||||
// System handled the pop - this means predictive back completed
|
||||
// We need to handle double-tap to exit here
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import 'package:spotiflac_android/providers/download_queue_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/widgets/download_service_picker.dart';
|
||||
|
||||
/// Playlist detail screen with Material Expressive 3 design
|
||||
class PlaylistScreen extends ConsumerStatefulWidget {
|
||||
final String playlistName;
|
||||
final String? coverUrl;
|
||||
@@ -69,7 +68,6 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
// Ignore palette extraction errors
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/screens/track_metadata_screen.dart';
|
||||
import 'package:spotiflac_android/screens/downloaded_album_screen.dart';
|
||||
|
||||
/// Grouped album data for history display
|
||||
class _GroupedAlbum {
|
||||
final String albumName;
|
||||
final String artistName;
|
||||
@@ -108,7 +107,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Enter selection mode with initial item
|
||||
void _enterSelectionMode(String itemId) {
|
||||
HapticFeedback.mediumImpact();
|
||||
setState(() {
|
||||
@@ -125,7 +123,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Toggle item selection
|
||||
void _toggleSelection(String itemId) {
|
||||
setState(() {
|
||||
if (_selectedIds.contains(itemId)) {
|
||||
@@ -146,7 +143,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Delete selected items
|
||||
Future<void> _deleteSelected() async {
|
||||
final count = _selectedIds.length;
|
||||
final confirmed = await showDialog<bool>(
|
||||
@@ -307,9 +303,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Filter history items based on current filter mode
|
||||
/// Album = track yang albumnya punya >1 track di history
|
||||
/// Single = track yang albumnya cuma 1 track di history
|
||||
List<DownloadHistoryItem> _filterHistoryItems(
|
||||
List<DownloadHistoryItem> items,
|
||||
String filterMode,
|
||||
@@ -725,7 +718,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Build content for each filter tab
|
||||
Widget _buildFilterContent({
|
||||
required BuildContext context,
|
||||
required ColorScheme colorScheme,
|
||||
@@ -931,7 +923,6 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Build album grid item for grouped albums view
|
||||
Widget _buildAlbumGridItem(
|
||||
BuildContext context,
|
||||
_GroupedAlbum album,
|
||||
@@ -1745,7 +1736,6 @@ child: CachedNetworkImage(
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter chip widget for history filtering
|
||||
class _FilterChip extends StatelessWidget {
|
||||
final String label;
|
||||
final int count;
|
||||
|
||||
@@ -15,7 +15,7 @@ class AboutPage extends StatelessWidget {
|
||||
final topPadding = MediaQuery.of(context).padding.top;
|
||||
|
||||
return PopScope(
|
||||
canPop: true, // Always allow back gesture
|
||||
canPop: true,
|
||||
child: Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
@@ -253,9 +253,9 @@ class _AppHeaderCard extends StatelessWidget {
|
||||
color: colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Image.asset(
|
||||
child: Image.asset(
|
||||
'assets/images/logo-transparant.png',
|
||||
color: colorScheme.onPrimary, // Tint with onPrimary color
|
||||
color: colorScheme.onPrimary,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (_, _, _) => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
|
||||
@@ -17,7 +17,7 @@ class AppearanceSettingsPage extends ConsumerWidget {
|
||||
final topPadding = MediaQuery.of(context).padding.top;
|
||||
|
||||
return PopScope(
|
||||
canPop: true, // Always allow back gesture
|
||||
canPop: true,
|
||||
child: Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
@@ -161,7 +161,7 @@ class _ThemePreviewCard extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme
|
||||
.surfaceContainerHighest, // Background similar to reference
|
||||
.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
@@ -203,7 +203,7 @@ class _ThemePreviewCard extends StatelessWidget {
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.1),
|
||||
blurRadius: 12, // Reduced from 20 for performance
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -22,7 +22,7 @@ class DownloadSettingsPage extends ConsumerWidget {
|
||||
final isBuiltInService = _builtInServices.contains(settings.defaultService);
|
||||
|
||||
return PopScope(
|
||||
canPop: true, // Always allow back gesture
|
||||
canPop: true,
|
||||
child: Scaffold(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
|
||||
@@ -581,7 +581,7 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
|
||||
switch (step) {
|
||||
case 0: return _storagePermissionGranted;
|
||||
case 1: return _selectedDirectory != null;
|
||||
case 2: return false; // Spotify step never shows checkmark (optional)
|
||||
case 2: return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -122,7 +122,7 @@ class _StoreTabState extends ConsumerState<StoreTab> {
|
||||
),
|
||||
onChanged: (value) {
|
||||
ref.read(storeProvider.notifier).setSearchQuery(value);
|
||||
setState(() {}); // Update suffix icon
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -13,8 +13,6 @@ import 'package:spotiflac_android/providers/download_queue_provider.dart';
|
||||
import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
|
||||
/// Screen to display detailed metadata for a downloaded track
|
||||
/// Designed with Material Expressive 3 style
|
||||
class TrackMetadataScreen extends ConsumerStatefulWidget {
|
||||
final DownloadHistoryItem item;
|
||||
|
||||
@@ -101,7 +99,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
// Ignore palette extraction errors
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +260,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
// Background with dominant color
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
decoration: BoxDecoration(
|
||||
@@ -280,7 +276,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
),
|
||||
),
|
||||
|
||||
// Cover image centered - fade out when collapsing
|
||||
AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
opacity: showContent ? 1.0 : 0.0,
|
||||
@@ -683,7 +678,6 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// Show 320kbps for MP3, bit depth/sample rate for FLAC
|
||||
if (fileExtension == 'MP3')
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
@@ -1057,8 +1051,8 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
ref.read(downloadHistoryProvider.notifier).removeFromHistory(item.id);
|
||||
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context); // Close dialog
|
||||
Navigator.pop(context); // Go back to history
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: Text(context.l10n.dialogDelete, style: TextStyle(color: colorScheme.error)),
|
||||
|
||||
Reference in New Issue
Block a user