persisted volume support

Placeholder image instead of progress indicator in PlaybuttonCard
Custom Fade Page Transition
This commit is contained in:
Kingkor Roy Tirtho 2022-02-24 13:26:21 +06:00
parent 5a9a988d26
commit a86b6bc40b
15 changed files with 119 additions and 96 deletions

BIN
assets/placeholder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -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<ArtistSimple>(album.artists ?? [])}",
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return AlbumView(album);
},
Navigator.of(context).push(SpotubePageRoute(
child: AlbumView(album),
));
},
onPlaybuttonPressed: () async {

View File

@ -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!,

View File

@ -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",
),

View File

@ -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,
),
),
);
},

View File

@ -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<Home> {
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(),
));
}),
],

View File

@ -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"))

View File

@ -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<Duration?>(null);
var _currentTrackId = useState<String?>(null);
final _isPlaying = useState(false);
final _shuffled = useState(false);
final _volume = useState(0.0);
final _duration = useState<Duration?>(null);
final _currentTrackId = useState<String?>(null);
AudioPlayer player = useMemoized(() => AudioPlayer(), []);
YoutubeExplode youtube = useMemoized(() => YoutubeExplode(), []);
final AudioPlayer player = useMemoized(() => AudioPlayer(), []);
final YoutubeExplode youtube = useMemoized(() => YoutubeExplode(), []);
final Future<SharedPreferences> future =
useMemoized(SharedPreferences.getInstance);
final AsyncSnapshot<SharedPreferences?> 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");

View File

@ -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 {

View File

@ -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(

View File

@ -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<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(
opacity: animation,
child: child,
);
}
}

View File

@ -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,
),

View File

@ -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<ArtistSimple> artists, {
@ -19,8 +20,8 @@ Widget artistsToClickableArtists(
(artist.key != artists.length - 1)
? "${artist.value.name}, "
: artist.value.name!,
MaterialPageRoute<ArtistProfile>(
builder: (context) => ArtistProfile(artist.value.id!),
SpotubePageRoute(
child: ArtistProfile(artist.value.id!),
),
overflow: TextOverflow.ellipsis,
),

View File

@ -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";
}

View File

@ -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"