diff --git a/assets/placeholder.png b/assets/placeholder.png new file mode 100644 index 00000000..6f6d451f Binary files /dev/null and b/assets/placeholder.png differ diff --git a/lib/components/Album/AlbumCard.dart b/lib/components/Album/AlbumCard.dart index e5ea9423..bdc4d864 100644 --- a/lib/components/Album/AlbumCard.dart +++ b/lib/components/Album/AlbumCard.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Album/AlbumView.dart'; import 'package:spotube/components/Shared/PlaybuttonCard.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/helpers/artist-to-string.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/simple-track-to-track.dart'; @@ -27,10 +28,8 @@ class AlbumCard extends ConsumerWidget { description: "Album • ${artistsToString(album.artists ?? [])}", onTap: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return AlbumView(album); - }, + Navigator.of(context).push(SpotubePageRoute( + child: AlbumView(album), )); }, onPlaybuttonPressed: () async { diff --git a/lib/components/Artist/ArtistCard.dart b/lib/components/Artist/ArtistCard.dart index 0f5099e8..6f92cb59 100644 --- a/lib/components/Artist/ArtistCard.dart +++ b/lib/components/Artist/ArtistCard.dart @@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Artist/ArtistProfile.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; class ArtistCard extends StatelessWidget { final Artist artist; @@ -9,12 +10,15 @@ class ArtistCard extends StatelessWidget { @override Widget build(BuildContext context) { + final backgroundImage = CachedNetworkImageProvider((artist + .images?.isNotEmpty ?? + false) + ? artist.images!.first.url! + : "https://avatars.dicebear.com/api/open-peeps/${artist.id}.png?b=%231ed760&r=50&flip=1&translateX=3&translateY=-6"); return InkWell( onTap: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return ArtistProfile(artist.id!); - }, + Navigator.of(context).push(SpotubePageRoute( + child: ArtistProfile(artist.id!), )); }, borderRadius: BorderRadius.circular(10), @@ -38,11 +42,7 @@ class ArtistCard extends StatelessWidget { CircleAvatar( maxRadius: 80, minRadius: 20, - backgroundImage: CachedNetworkImageProvider((artist - .images?.isNotEmpty ?? - false) - ? artist.images!.first.url! - : "https://avatars.dicebear.com/api/open-peeps/${artist.id}.png?b=%231ed760&r=50&flip=1&translateX=3&translateY=-6"), + backgroundImage: backgroundImage, ), Text( artist.name!, diff --git a/lib/components/Artist/ArtistProfile.dart b/lib/components/Artist/ArtistProfile.dart index a947c37b..8b2b0cdd 100644 --- a/lib/components/Artist/ArtistProfile.dart +++ b/lib/components/Artist/ArtistProfile.dart @@ -7,6 +7,7 @@ import 'package:spotube/components/Album/AlbumCard.dart'; import 'package:spotube/components/Artist/ArtistAlbumView.dart'; import 'package:spotube/components/Artist/ArtistCard.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/components/Shared/TracksTableView.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/readable-number.dart'; @@ -215,8 +216,8 @@ class ArtistProfile extends ConsumerWidget { TextButton( child: const Text("See All"), onPressed: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => ArtistAlbumView( + Navigator.of(context).push(SpotubePageRoute( + child: ArtistAlbumView( artistId, snapshot.data?.name ?? "KRTX", ), diff --git a/lib/components/Category/CategoryCard.dart b/lib/components/Category/CategoryCard.dart index 19811f70..99745883 100644 --- a/lib/components/Category/CategoryCard.dart +++ b/lib/components/Category/CategoryCard.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Playlist/PlaylistCard.dart'; import 'package:spotube/components/Playlist/PlaylistGenreView.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/provider/SpotifyDI.dart'; class CategoryCard extends StatelessWidget { @@ -30,14 +31,12 @@ class CategoryCard extends StatelessWidget { TextButton( onPressed: () { Navigator.of(context).push( - MaterialPageRoute( - builder: (context) { - return PlaylistGenreView( - category.id!, - category.name!, - playlists: playlists, - ); - }, + SpotubePageRoute( + child: PlaylistGenreView( + category.id!, + category.name!, + playlists: playlists, + ), ), ); }, diff --git a/lib/components/Home.dart b/lib/components/Home.dart index 200433f3..2d6cfa97 100644 --- a/lib/components/Home.dart +++ b/lib/components/Home.dart @@ -16,6 +16,7 @@ import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/components/Player/Player.dart'; import 'package:spotube/components/Settings.dart'; import 'package:spotube/components/Library/UserLibrary.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/oauth-login.dart'; import 'package:spotube/models/LocalStorageKeys.dart'; @@ -228,10 +229,8 @@ class _HomeState extends ConsumerState { IconButton( icon: const Icon(Icons.settings_outlined), onPressed: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return const Settings(); - }, + Navigator.of(context).push(SpotubePageRoute( + child: const Settings(), )); }), ], diff --git a/lib/components/Lyrics.dart b/lib/components/Lyrics.dart index 8d42e8e0..71db1357 100644 --- a/lib/components/Lyrics.dart +++ b/lib/components/Lyrics.dart @@ -3,6 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Settings.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/helpers/artist-to-string.dart'; import 'package:spotube/helpers/getLyrics.dart'; import 'package:spotube/provider/Playback.dart'; @@ -68,10 +69,8 @@ class Lyrics extends HookConsumerWidget { ), ElevatedButton( onPressed: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return const Settings(); - }, + Navigator.of(context).push(SpotubePageRoute( + child: const Settings(), )); }, child: const Text("Add Access Token")) diff --git a/lib/components/Player/Player.dart b/lib/components/Player/Player.dart index 41cbe480..96c70f4f 100644 --- a/lib/components/Player/Player.dart +++ b/lib/components/Player/Player.dart @@ -4,12 +4,14 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:just_audio/just_audio.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Shared/DownloadTrackButton.dart'; import 'package:spotube/components/Player/PlayerControls.dart'; import 'package:spotube/helpers/artists-to-clickable-artists.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/search-youtube.dart'; +import 'package:spotube/models/LocalStorageKeys.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:flutter/material.dart'; import 'package:spotube/provider/SpotifyDI.dart'; @@ -20,14 +22,18 @@ class Player extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { - var _isPlaying = useState(false); - var _shuffled = useState(false); - var _volume = useState(0.0); - var _duration = useState(null); - var _currentTrackId = useState(null); + final _isPlaying = useState(false); + final _shuffled = useState(false); + final _volume = useState(0.0); + final _duration = useState(null); + final _currentTrackId = useState(null); - AudioPlayer player = useMemoized(() => AudioPlayer(), []); - YoutubeExplode youtube = useMemoized(() => YoutubeExplode(), []); + final AudioPlayer player = useMemoized(() => AudioPlayer(), []); + final YoutubeExplode youtube = useMemoized(() => YoutubeExplode(), []); + final Future future = + useMemoized(SharedPreferences.getInstance); + final AsyncSnapshot localStorage = + useFuture(future, initialData: null); var _movePlaylistPositionBy = useCallback((int pos) { Playback playback = ref.read(playbackProvider); @@ -53,8 +59,6 @@ class Player extends HookConsumerWidget { }, [_duration]); useEffect(() { - _volume.value = player.volume; - var playingStreamListener = player.playingStream.listen((playing) async { _isPlaying.value = playing; }); @@ -97,6 +101,13 @@ class Player extends HookConsumerWidget { }; }, []); + useEffect(() { + if (localStorage.hasData) { + _volume.value = localStorage.data?.getDouble(LocalStorageKeys.volume) ?? + player.volume; + } + }, [localStorage.data]); + var _playTrack = useCallback((Track currentTrack, Playback playback) async { try { if (currentTrack.id != _currentTrackId.value) { @@ -285,6 +296,10 @@ class Player extends HookConsumerWidget { try { await player.setVolume(value).then((_) { _volume.value = value; + localStorage.data?.setDouble( + LocalStorageKeys.volume, + value, + ); }); } catch (e, stack) { print("[VolumeSlider.onChange()] $e"); diff --git a/lib/components/Playlist/PlaylistCard.dart b/lib/components/Playlist/PlaylistCard.dart index 862ee9fc..2da322ee 100644 --- a/lib/components/Playlist/PlaylistCard.dart +++ b/lib/components/Playlist/PlaylistCard.dart @@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Playlist/PlaylistView.dart'; import 'package:spotube/components/Shared/PlaybuttonCard.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/SpotifyDI.dart'; @@ -20,10 +21,8 @@ class PlaylistCard extends ConsumerWidget { imageUrl: playlist.images![0].url!, isPlaying: isPlaylistPlaying, onTap: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) { - return PlaylistView(playlist); - }, + Navigator.of(context).push(SpotubePageRoute( + child: PlaylistView(playlist), )); }, onPlaybuttonPressed: () async { diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index 42b350e0..d0561b2b 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -46,11 +46,8 @@ class PlaybuttonCard extends StatelessWidget { borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: imageUrl, - progressIndicatorBuilder: (context, url, progress) { - return CircularProgressIndicator.adaptive( - value: progress.progress, - ); - }, + placeholder: (context, url) => + Image.asset("assets/placeholder.png"), ), ), Positioned.directional( diff --git a/lib/components/Shared/SpotubePageRoute.dart b/lib/components/Shared/SpotubePageRoute.dart new file mode 100644 index 00000000..0cc47129 --- /dev/null +++ b/lib/components/Shared/SpotubePageRoute.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class SpotubePageRoute extends PageRouteBuilder { + final Widget child; + SpotubePageRoute({required this.child}) + : super( + pageBuilder: (context, animation, secondaryAnimation) => child, + ); + + @override + Widget buildTransitions(BuildContext context, Animation animation, + Animation secondaryAnimation, Widget child) { + return FadeTransition( + opacity: animation, + child: child, + ); + } +} diff --git a/lib/components/Shared/TracksTableView.dart b/lib/components/Shared/TracksTableView.dart index 9ae45a14..b3a6dffb 100644 --- a/lib/components/Shared/TracksTableView.dart +++ b/lib/components/Shared/TracksTableView.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Album/AlbumView.dart'; import 'package:spotube/components/Shared/LinkText.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; import 'package:spotube/helpers/artists-to-clickable-artists.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/zero-pad-num-str.dart'; @@ -83,8 +84,8 @@ class TracksTableView extends ConsumerWidget { Expanded( child: LinkText( track.value.album!.name!, - MaterialPageRoute( - builder: (context) => AlbumView(track.value.album!), + SpotubePageRoute( + child: AlbumView(track.value.album!), ), overflow: TextOverflow.ellipsis, ), diff --git a/lib/helpers/artists-to-clickable-artists.dart b/lib/helpers/artists-to-clickable-artists.dart index a4b11fa5..0cb5a7f6 100644 --- a/lib/helpers/artists-to-clickable-artists.dart +++ b/lib/helpers/artists-to-clickable-artists.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Artist/ArtistProfile.dart'; import 'package:spotube/components/Shared/LinkText.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; Widget artistsToClickableArtists( List artists, { @@ -19,8 +20,8 @@ Widget artistsToClickableArtists( (artist.key != artists.length - 1) ? "${artist.value.name}, " : artist.value.name!, - MaterialPageRoute( - builder: (context) => ArtistProfile(artist.value.id!), + SpotubePageRoute( + child: ArtistProfile(artist.value.id!), ), overflow: TextOverflow.ellipsis, ), diff --git a/lib/models/LocalStorageKeys.dart b/lib/models/LocalStorageKeys.dart index 70b5936a..1af5088c 100644 --- a/lib/models/LocalStorageKeys.dart +++ b/lib/models/LocalStorageKeys.dart @@ -10,4 +10,6 @@ abstract class LocalStorageKeys { static String nextTrackHotKey = "next_track_hot_key"; static String prevTrackHotKey = "prev_track_hot_key"; static String playPauseHotKey = "play_pause_hot_key"; + + static String volume = "volume"; } diff --git a/pubspec.lock b/pubspec.lock index 491d288a..0ad07331 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,7 +14,7 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.1.8" + version: "3.2.1" args: dependency: transitive description: @@ -180,7 +180,7 @@ packages: name: flutter_blurhash url: "https://pub.dartlang.org" source: hosted - version: "0.6.0" + version: "0.6.4" flutter_cache_manager: dependency: transitive description: @@ -225,7 +225,7 @@ packages: name: freezed_annotation url: "https://pub.dartlang.org" source: hosted - version: "0.14.3" + version: "1.1.0" hooks_riverpod: dependency: "direct main" description: @@ -267,7 +267,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "3.1.1" + version: "3.1.3" infinite_scroll_pagination: dependency: "direct main" description: @@ -275,13 +275,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0" - injector: - dependency: transitive - description: - name: injector - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" js: dependency: transitive description: @@ -302,7 +295,7 @@ packages: name: just_audio url: "https://pub.dartlang.org" source: hosted - version: "0.9.18" + version: "0.9.20" just_audio_libwinmedia: dependency: "direct main" description: @@ -316,14 +309,14 @@ packages: name: just_audio_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "4.1.0" just_audio_web: dependency: transitive description: name: just_audio_web url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.4" libwinmedia: dependency: transitive description: @@ -365,7 +358,7 @@ packages: name: msix url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.8.18" oauth2: dependency: transitive description: @@ -400,7 +393,7 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.0.9" path_provider_android: dependency: transitive description: @@ -421,28 +414,28 @@ packages: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.3" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" pedantic: dependency: transitive description: @@ -470,7 +463,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.1.2" process: dependency: transitive description: @@ -498,35 +491,35 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.13" shared_preferences_android: dependency: transitive description: name: shared_preferences_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.9" + version: "2.0.11" shared_preferences_ios: dependency: transitive description: name: shared_preferences_ios url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.1.0" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.1.0" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" shared_preferences_platform_interface: dependency: transitive description: @@ -540,14 +533,14 @@ packages: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.1.0" sky_engine: dependency: transitive description: flutter @@ -580,14 +573,14 @@ packages: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" sqflite_common: dependency: transitive description: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" stack_trace: dependency: transitive description: @@ -650,63 +643,63 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.17" + version: "6.0.20" url_launcher_android: dependency: transitive description: name: url_launcher_android url: "https://pub.dartlang.org" source: hosted - version: "6.0.13" + version: "6.0.15" url_launcher_ios: dependency: transitive description: name: url_launcher_ios url: "https://pub.dartlang.org" source: hosted - version: "6.0.13" + version: "6.0.15" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.8" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.0" uuid: dependency: transitive description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "3.0.5" + version: "3.0.6" vector_math: dependency: transitive description: @@ -720,14 +713,14 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.3.3" + version: "2.4.1" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.2.0+1" xml: dependency: transitive description: @@ -748,7 +741,7 @@ packages: name: youtube_explode_dart url: "https://pub.dartlang.org" source: hosted - version: "1.10.8" + version: "1.10.9+1" sdks: dart: ">=2.15.1 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.10.0"