From 3a63918e1fdf174612460f5d6785281c4f4c88f6 Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Wed, 30 Sep 2026 21:44:16 +0700 Subject: [PATCH] refactor(flutter): remove unreachable state and widget APIs --- lib/constants/app_info.dart | 1 - lib/models/track.dart | 4 -- .../library_collections_provider.dart | 11 ---- lib/providers/music_player_provider.dart | 2 - lib/providers/recent_access_provider.dart | 24 -------- lib/providers/repo_provider.dart | 17 ------ lib/providers/theme_provider.dart | 5 -- lib/providers/track_provider.dart | 17 ------ lib/services/backup_service.dart | 8 --- lib/utils/file_access.dart | 7 +-- lib/utils/progress_stream_poller.dart | 2 - lib/utils/synced_lyrics_scroll.dart | 10 ---- lib/widgets/animation_utils.dart | 16 +++-- lib/widgets/app_bottom_sheet.dart | 33 ----------- lib/widgets/cached_cover_image.dart | 6 -- lib/widgets/mornye_chrome.dart | 13 ----- lib/widgets/priority_settings_scaffold.dart | 4 +- lib/widgets/track_card.dart | 13 +---- test/design_system_test.dart | 58 ------------------- test/models_and_utils_test.dart | 2 - test/synced_lyrics_scroll_test.dart | 15 ----- 21 files changed, 12 insertions(+), 256 deletions(-) diff --git a/lib/constants/app_info.dart b/lib/constants/app_info.dart index 6a2d8131..283bb1e1 100644 --- a/lib/constants/app_info.dart +++ b/lib/constants/app_info.dart @@ -3,7 +3,6 @@ import 'package:flutter/foundation.dart'; class AppInfo { static const String version = '5.0.6'; static const String buildNumber = '155'; - static const String fullVersion = '$version+$buildNumber'; static const String gitCommit = String.fromEnvironment('GIT_COMMIT'); static String get shortGitCommit => diff --git a/lib/models/track.dart b/lib/models/track.dart index ef983bc6..d600962e 100644 --- a/lib/models/track.dart +++ b/lib/models/track.dart @@ -224,15 +224,11 @@ class Track { ); } - bool get isFromExtension => source != null && source!.isNotEmpty; - bool get isDolbyAtmos => audioModes != null && audioModes!.contains('DOLBY_ATMOS'); bool get isExplicit => explicit == true; - bool get hasAudioQuality => audioQuality != null && audioQuality!.isNotEmpty; - bool get hasPreview => previewUrl != null && previewUrl!.isNotEmpty; } diff --git a/lib/providers/library_collections_provider.dart b/lib/providers/library_collections_provider.dart index d9278f60..e0daceb8 100644 --- a/lib/providers/library_collections_provider.dart +++ b/lib/providers/library_collections_provider.dart @@ -167,11 +167,6 @@ class UserPlaylistCollection { ); } - bool containsTrack(Track track) { - final key = trackCollectionKey(track); - return _trackKeys.contains(key); - } - bool containsTrackKey(String trackKey) { return _trackKeys.contains(trackKey); } @@ -333,12 +328,6 @@ class LibraryCollectionsState { return _playlistsById[playlistId]; } - bool playlistContainsTrack(String playlistId, String trackKey) { - final playlist = _playlistsById[playlistId]; - if (playlist == null) return false; - return playlist.containsTrackKey(trackKey); - } - bool isTrackInAnyPlaylist(String trackKey) { return _allPlaylistTrackKeys.contains(trackKey); } diff --git a/lib/providers/music_player_provider.dart b/lib/providers/music_player_provider.dart index e4601433..f907b755 100644 --- a/lib/providers/music_player_provider.dart +++ b/lib/providers/music_player_provider.dart @@ -114,8 +114,6 @@ class MusicPlayerController { MusicPlayerHandler? get _handler => musicPlayerHandler; - bool get isAvailable => _handler != null; - DateTime? get sleepTimerEndsAt => _handler?.sleepTimerEndsAt; Future ensureInitialized() async { diff --git a/lib/providers/recent_access_provider.dart b/lib/providers/recent_access_provider.dart index d5e5c026..7bb60054 100644 --- a/lib/providers/recent_access_provider.dart +++ b/lib/providers/recent_access_provider.dart @@ -182,26 +182,6 @@ class RecentAccessNotifier extends Notifier { ); } - void recordTrackAccess({ - required String id, - required String name, - String? artistName, - String? imageUrl, - String? providerId, - }) { - _recordAccess( - RecentAccessItem( - id: id, - name: name, - subtitle: artistName, - imageUrl: imageUrl, - type: RecentAccessType.track, - accessedAt: DateTime.now(), - providerId: providerId, - ), - ); - } - void recordPlaylistAccess({ required String id, required String name, @@ -261,10 +241,6 @@ class RecentAccessNotifier extends Notifier { unawaited(_appStateDb.addHiddenRecentDownloadId(downloadId)); } - bool isDownloadHidden(String downloadId) { - return state.hiddenDownloadIds.contains(downloadId); - } - Future clearHistory() async { final clearedAt = await _appStateDb.clearAllRecentAccess(); state = state.copyWith( diff --git a/lib/providers/repo_provider.dart b/lib/providers/repo_provider.dart index 7ad391d1..9667dedc 100644 --- a/lib/providers/repo_provider.dart +++ b/lib/providers/repo_provider.dart @@ -40,23 +40,6 @@ class RepoCategory { lyrics, integration, ]; - - static String getDisplayName(String category) { - switch (category) { - case metadata: - return 'Metadata'; - case download: - return 'Download'; - case utility: - return 'Utility'; - case lyrics: - return 'Lyrics'; - case integration: - return 'Integration'; - default: - return category; - } - } } class RepoExtension { diff --git a/lib/providers/theme_provider.dart b/lib/providers/theme_provider.dart index bd205040..a22526fb 100644 --- a/lib/providers/theme_provider.dart +++ b/lib/providers/theme_provider.dart @@ -76,11 +76,6 @@ class ThemeNotifier extends Notifier { await _saveToStorage(); } - Future setSeedColorValue(int colorValue) async { - state = state.copyWith(seedColorValue: colorValue); - await _saveToStorage(); - } - Future setUseAmoled(bool value) async { state = state.copyWith(useAmoled: value); await _saveToStorage(); diff --git a/lib/providers/track_provider.dart b/lib/providers/track_provider.dart index 6b45bd2b..58cbecff 100644 --- a/lib/providers/track_provider.dart +++ b/lib/providers/track_provider.dart @@ -572,23 +572,6 @@ class TrackNotifier extends Notifier { state = state.copyWith(isShowingRecentAccess: showing); } - void setTracksFromCollection({ - required List tracks, - String? albumName, - String? playlistName, - String? coverUrl, - }) { - state = TrackState( - tracks: tracks, - isLoading: false, - albumName: albumName, - playlistName: playlistName, - coverUrl: coverUrl, - hasSearchText: state.hasSearchText, - isShowingRecentAccess: state.isShowingRecentAccess, - ); - } - ArtistAlbum _parseArtistAlbum(Map data) { return ArtistAlbum( id: data['id'] as String? ?? '', diff --git a/lib/services/backup_service.dart b/lib/services/backup_service.dart index e62ccafd..f63ec9e1 100644 --- a/lib/services/backup_service.dart +++ b/lib/services/backup_service.dart @@ -294,14 +294,6 @@ class BackupService { ); } - /// Legacy JSON writer retained for compatibility with callers outside the - /// settings UI. It no longer defines the default backup format. - static Future writeBackupFile(Map envelope) async { - final output = await _newBackupFile(); - await output.writeAsString(encode(envelope), flush: true); - return output; - } - static Future parseFile( String path, { Directory? temporaryDirectory, diff --git a/lib/utils/file_access.dart b/lib/utils/file_access.dart index 97f063da..51f712a4 100644 --- a/lib/utils/file_access.dart +++ b/lib/utils/file_access.dart @@ -168,14 +168,9 @@ Future validateOrFixIosPath( /// Detailed result for iOS path validation class IosPathValidationResult { final bool isValid; - final String? correctedPath; final String? errorReason; - const IosPathValidationResult({ - required this.isValid, - this.correctedPath, - this.errorReason, - }); + const IosPathValidationResult({required this.isValid, this.errorReason}); } /// Validates an iOS path and returns detailed information about the result. diff --git a/lib/utils/progress_stream_poller.dart b/lib/utils/progress_stream_poller.dart index dc02136b..ddafc8f2 100644 --- a/lib/utils/progress_stream_poller.dart +++ b/lib/utils/progress_stream_poller.dart @@ -60,8 +60,6 @@ class ProgressStreamPoller { int _errorCount = 0; int _generation = 0; - bool get usingStream => _usingStream; - /// (Re)starts progress consumption. When [useStream] is true, attaches the /// stream (falling back to polling on timeout/error); otherwise starts /// polling immediately. diff --git a/lib/utils/synced_lyrics_scroll.dart b/lib/utils/synced_lyrics_scroll.dart index c03631c2..540677c5 100644 --- a/lib/utils/synced_lyrics_scroll.dart +++ b/lib/utils/synced_lyrics_scroll.dart @@ -114,13 +114,3 @@ double syncedLyricSegmentLift({ final emphasis = wordLift > 0 ? normalLift / wordLift : 0; return normalLift + 1.3 * heldStrength * rise * settle * emphasis; } - -/// Horizontal leading edge for a highlight that fills left to right. -double syncedLyricsLeftToRightBoundary({ - required double left, - required double right, - required double progress, -}) { - final value = progress.clamp(0.0, 1.0); - return left + ((right - left) * value); -} diff --git a/lib/widgets/animation_utils.dart b/lib/widgets/animation_utils.dart index c69bb03f..1b2434a0 100644 --- a/lib/widgets/animation_utils.dart +++ b/lib/widgets/animation_utils.dart @@ -7,14 +7,13 @@ import 'package:spotiflac_android/theme/mornye_theme.dart'; /// [index] controls the stagger delay (each item delayed by [staggerDelay]). /// Set [animate] to false to skip the animation (e.g. when scrolling back). class StaggeredListItem extends StatelessWidget { - static const int _defaultMaxAnimatedItems = 10; + static const int _maxAnimatedItems = 10; final int index; final Widget child; final Duration duration; final Duration staggerDelay; final bool animate; - final int maxAnimatedItems; const StaggeredListItem({ super.key, @@ -23,17 +22,16 @@ class StaggeredListItem extends StatelessWidget { this.duration = const Duration(milliseconds: 250), this.staggerDelay = const Duration(milliseconds: 40), this.animate = true, - this.maxAnimatedItems = _defaultMaxAnimatedItems, }); @override Widget build(BuildContext context) { if (!animate || - index >= maxAnimatedItems || + index >= _maxAnimatedItems || MediaQuery.disableAnimationsOf(context)) { return child; } - final cappedIndex = index.clamp(0, maxAnimatedItems - 1); + final cappedIndex = index.clamp(0, _maxAnimatedItems - 1); final delay = staggerDelay * cappedIndex; final totalDuration = duration + delay; @@ -545,14 +543,14 @@ class ArtistHeaderActionsSkeleton extends StatelessWidget { /// discography loads: optional cover placeholder, "Popular" section, and the /// horizontal album sections. class ArtistScreenSkeleton extends StatelessWidget { - final int popularCount; + static const int _popularCount = 5; + final int albumCount; final bool showCoverHeader; final bool showPopularSection; const ArtistScreenSkeleton({ super.key, - this.popularCount = 5, this.albumCount = 5, this.showCoverHeader = true, this.showPopularSection = true, @@ -650,7 +648,7 @@ class ArtistScreenSkeleton extends StatelessWidget { padding: EdgeInsets.fromLTRB(20, 24, 20, 12), child: SkeletonBox(width: 110, height: 24, borderRadius: 4), ), - for (var index = 0; index < popularCount; index++) + for (var index = 0; index < _popularCount; index++) _MornyeTrackSkeleton(numbered: false, index: index), ], const Padding( @@ -700,7 +698,7 @@ class ArtistScreenSkeleton extends StatelessWidget { padding: EdgeInsets.fromLTRB(16, 24, 16, 12), child: SkeletonBox(width: 110, height: 22, borderRadius: 4), ), - ...List.generate(popularCount, (index) { + ...List.generate(_popularCount, (index) { return Padding( padding: const EdgeInsets.symmetric( horizontal: 16, diff --git a/lib/widgets/app_bottom_sheet.dart b/lib/widgets/app_bottom_sheet.dart index 110e93e7..ee562bdd 100644 --- a/lib/widgets/app_bottom_sheet.dart +++ b/lib/widgets/app_bottom_sheet.dart @@ -42,39 +42,6 @@ class AppSheetHandle extends StatelessWidget { } } -/// A modal-sheet surface whose height and inner scroll position share one drag. -/// -/// The child must attach the supplied [ScrollController] to its primary -/// vertical scroll view. Pulling down at the top then moves the whole surface; -/// releasing either restores it or dismisses it at the minimum extent. -class AppDraggableSheet extends StatelessWidget { - const AppDraggableSheet({ - super.key, - required this.builder, - this.initialChildSize = 0.88, - this.minChildSize = 0.25, - this.maxChildSize = 0.88, - }); - - final ScrollableWidgetBuilder builder; - final double initialChildSize; - final double minChildSize; - final double maxChildSize; - - @override - Widget build(BuildContext context) { - return DraggableScrollableSheet( - expand: false, - snap: true, - shouldCloseOnMinExtent: true, - initialChildSize: initialChildSize, - minChildSize: minChildSize, - maxChildSize: maxChildSize, - builder: builder, - ); - } -} - /// Standard chrome for modal sheet content: drag handle, optional title block, /// height cap, keyboard inset and bottom safe area, so each sheet only supplies /// its own body. diff --git a/lib/widgets/cached_cover_image.dart b/lib/widgets/cached_cover_image.dart index b530db90..5cd79d3a 100644 --- a/lib/widgets/cached_cover_image.dart +++ b/lib/widgets/cached_cover_image.dart @@ -20,7 +20,6 @@ class CachedCoverImage extends StatelessWidget { final Widget Function(BuildContext, String, Object)? errorWidget; final Widget Function(BuildContext, String)? placeholder; final BorderRadius? borderRadius; - final bool resizeDiskCache; final Duration fadeInDuration; final Duration fadeOutDuration; @@ -36,7 +35,6 @@ class CachedCoverImage extends StatelessWidget { this.errorWidget, this.placeholder, this.borderRadius, - this.resizeDiskCache = false, this.fadeInDuration = Duration.zero, this.fadeOutDuration = Duration.zero, }); @@ -75,8 +73,6 @@ class CachedCoverImage extends StatelessWidget { ); } } - final diskCacheWidth = resizeDiskCache ? autoMemCacheWidth : null; - final diskCacheHeight = resizeDiskCache ? autoMemCacheHeight : null; final image = CachedNetworkImage( imageUrl: imageUrl, width: width, @@ -85,8 +81,6 @@ class CachedCoverImage extends StatelessWidget { alignment: alignment, memCacheWidth: autoMemCacheWidth, memCacheHeight: autoMemCacheHeight, - maxWidthDiskCache: diskCacheWidth, - maxHeightDiskCache: diskCacheHeight, cacheManager: CoverCacheManager.instance, fadeInDuration: fadeInDuration, fadeOutDuration: fadeOutDuration, diff --git a/lib/widgets/mornye_chrome.dart b/lib/widgets/mornye_chrome.dart index 0799ee60..4a018045 100644 --- a/lib/widgets/mornye_chrome.dart +++ b/lib/widgets/mornye_chrome.dart @@ -132,7 +132,6 @@ class MornyeGlassPanel extends ConsumerWidget { this.tintOpacity, this.tintColor, this.backdropFilter, - this.liquidGlass = false, this.blurEnabled = true, }); @@ -146,7 +145,6 @@ class MornyeGlassPanel extends ConsumerWidget { this.tintOpacity = 0.78, this.tintColor, this.backdropFilter, - this.liquidGlass = false, this.blurEnabled = true, }) : strongTint = false; @@ -158,7 +156,6 @@ class MornyeGlassPanel extends ConsumerWidget { final double? tintOpacity; final Color? tintColor; final ImageFilter? backdropFilter; - final bool liquidGlass; /// Disable backdrop sampling for surfaces that scroll over a plain page. final bool blurEnabled; @@ -170,16 +167,6 @@ class MornyeGlassPanel extends ConsumerWidget { !MediaQuery.highContrastOf(context) && (!ref.watch(lowEndDeviceProvider) || ref.watch(backdropBlurEnabledProvider)); - if (liquidGlass) { - return MornyeGlass( - radius: radius, - tintOpacity: tintOpacity, - tintColor: tintColor, - backdropFilter: backdropFilter, - blurEnabled: blur, - child: Material(color: Colors.transparent, child: child), - ); - } return MornyeGlass.navigation( radius: radius, firstInGroup: firstInGroup, diff --git a/lib/widgets/priority_settings_scaffold.dart b/lib/widgets/priority_settings_scaffold.dart index 13716469..9c9ca73e 100644 --- a/lib/widgets/priority_settings_scaffold.dart +++ b/lib/widgets/priority_settings_scaffold.dart @@ -8,7 +8,6 @@ class PrioritySettingsScaffold extends StatelessWidget { final String title; final String description; final String infoText; - final String? saveLabel; final EdgeInsetsGeometry descriptionPadding; final List slivers; final Future Function() onSave; @@ -23,7 +22,6 @@ class PrioritySettingsScaffold extends StatelessWidget { required this.slivers, required this.onSave, required this.onConfirmDiscard, - this.saveLabel, this.descriptionPadding = const EdgeInsets.fromLTRB(16, 4, 16, 8), }); @@ -66,7 +64,7 @@ class PrioritySettingsScaffold extends StatelessWidget { if (hasChanges) TextButton( onPressed: onSave, - child: Text(saveLabel ?? context.l10n.dialogSave), + child: Text(context.l10n.dialogSave), ), ], ), diff --git a/lib/widgets/track_card.dart b/lib/widgets/track_card.dart index bbd6ffcf..14e86866 100644 --- a/lib/widgets/track_card.dart +++ b/lib/widgets/track_card.dart @@ -34,7 +34,6 @@ class TrackCard extends StatelessWidget { this.onTap, this.onLongPress, this.margin, - this.titleStyle, }); /// Track number, cover art, or anything else that identifies the row. @@ -64,10 +63,6 @@ class TrackCard extends StatelessWidget { /// aligned with each other. final EdgeInsetsGeometry? margin; - /// Overrides the title text style for rows that need to signal a different - /// state (e.g. a dimmed unavailable track). - final TextStyle? titleStyle; - @override Widget build(BuildContext context) { final tokens = context.tokens; @@ -140,11 +135,9 @@ class TrackCard extends StatelessWidget { title, maxLines: 1, overflow: TextOverflow.ellipsis, - style: - titleStyle ?? - theme.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: theme.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + ), ), if (subtitle != null) ...[ const SizedBox(height: 2), diff --git a/test/design_system_test.dart b/test/design_system_test.dart index ae0a8e3c..b3f5d107 100644 --- a/test/design_system_test.dart +++ b/test/design_system_test.dart @@ -556,64 +556,6 @@ void main() { expect(find.text('body'), findsOneWidget); }); - testWidgets('draggable content moves as one surface and dismisses', ( - tester, - ) async { - const sheetKey = ValueKey('scrollable-sheet'); - await tester.pumpWidget( - MaterialApp( - theme: AppTheme.light(), - home: Scaffold( - body: Builder( - builder: (context) => ElevatedButton( - onPressed: () => showModalBottomSheet( - context: context, - isScrollControlled: true, - // Disable the route recognizer so the draggable surface, - // rather than the modal's fallback gesture, is under test. - enableDrag: false, - builder: (_) => AppDraggableSheet( - builder: (_, scrollController) => Material( - key: sheetKey, - child: ListView( - controller: scrollController, - children: const [ - SizedBox(height: 800, child: Text('sheet body')), - ], - ), - ), - ), - ), - child: const Text('open'), - ), - ), - ), - ), - ); - - await tester.tap(find.text('open')); - await tester.pumpAndSettle(); - expect(find.byKey(sheetKey), findsOneWidget); - - final initialTop = tester.getTopLeft(find.byKey(sheetKey)).dy; - final gesture = await tester.startGesture( - tester.getCenter(find.byKey(sheetKey)), - ); - await gesture.moveBy(const Offset(0, 160)); - await tester.pump(); - - expect( - tester.getTopLeft(find.byKey(sheetKey)).dy, - greaterThan(initialTop + 100), - ); - - await gesture.moveBy(const Offset(0, 260)); - await gesture.up(); - await tester.pumpAndSettle(); - - expect(find.byKey(sheetKey), findsNothing); - }); - testWidgets('sheet shape comes from the token scale', (tester) async { final shape = AppTheme.light().bottomSheetTheme.shape! as RoundedRectangleBorder; diff --git a/test/models_and_utils_test.dart b/test/models_and_utils_test.dart index 4594aca0..0c7a6f1e 100644 --- a/test/models_and_utils_test.dart +++ b/test/models_and_utils_test.dart @@ -717,8 +717,6 @@ void main() { expect(album.isPlaylistItem, isFalse); expect(album.isArtistItem, isFalse); expect(album.isCollection, isTrue); - expect(album.isFromExtension, isTrue); - expect(album.hasAudioQuality, isTrue); expect(album.isDolbyAtmos, isTrue); }); diff --git a/test/synced_lyrics_scroll_test.dart b/test/synced_lyrics_scroll_test.dart index 0b3d4560..801ec88d 100644 --- a/test/synced_lyrics_scroll_test.dart +++ b/test/synced_lyrics_scroll_test.dart @@ -189,20 +189,5 @@ void main() { 1, ); }); - - test('moves the reveal boundary from left to right', () { - expect( - syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 0), - 10, - ); - expect( - syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 0.5), - 60, - ); - expect( - syncedLyricsLeftToRightBoundary(left: 10, right: 110, progress: 1), - 110, - ); - }); }); }