PlayerOverlay flickering fix

track Share popup menu not showing when not logged in fix
page tranistions changed to default material 3 design's
Wrong link in Login page fix
Changing UserPreferences provider resets the Playback provider
using using hookified_infinite_scroll_pagination officially
This commit is contained in:
Kingkor Roy Tirtho 2022-06-07 19:53:33 +06:00
parent b20aa1911c
commit 74df7a91b1
15 changed files with 103 additions and 169 deletions

View File

@ -20,7 +20,7 @@ class PlayerOverlay extends HookConsumerWidget {
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final breakpoint = useBreakpoints(); final breakpoint = useBreakpoints();
final isCurrentRoute = useIsCurrentRoute("/"); final isCurrentRoute = useIsCurrentRoute("/");
final paletteColor = usePaletteColor(context, albumArt); final paletteColor = usePaletteColor(context, albumArt, ref);
final playback = ref.watch(playbackProvider); final playback = ref.watch(playbackProvider);
if (isCurrentRoute == false) { if (isCurrentRoute == false) {
@ -37,7 +37,8 @@ class PlayerOverlay extends HookConsumerWidget {
right: (breakpoint.isMd ? 10 : 5), right: (breakpoint.isMd ? 10 : 5),
left: (breakpoint.isSm ? 5 : 80), left: (breakpoint.isSm ? 5 : 80),
bottom: (breakpoint.isSm ? 63 : 10), bottom: (breakpoint.isSm ? 63 : 10),
child: Container( child: AnimatedContainer(
duration: const Duration(milliseconds: 500),
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
height: 50, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(

View File

@ -44,7 +44,7 @@ class PlayerView extends HookConsumerWidget {
[currentTrack?.album?.images], [currentTrack?.album?.images],
); );
final PaletteColor paletteColor = usePaletteColor(context, albumArt); final PaletteColor paletteColor = usePaletteColor(context, albumArt, ref);
final backgroundColor = Theme.of(context).backgroundColor; final backgroundColor = Theme.of(context).backgroundColor;

View File

@ -61,7 +61,7 @@ class Login extends HookConsumerWidget {
const Text( const Text(
"Don't worry, any of your credentials won't be collected or shared with anyone"), "Don't worry, any of your credentials won't be collected or shared with anyone"),
const Hyperlink("How to get these client-id & client-secret?", const Hyperlink("How to get these client-id & client-secret?",
"https://github.com/KRTirtho/spotube#configuration"), "https://github.com/KRTirtho/spotube#optional-configurations"),
const SizedBox( const SizedBox(
height: 10, height: 10,
), ),

View File

@ -280,9 +280,6 @@ class Settings extends HookConsumerWidget {
MaterialStateProperty.all(Colors.white), MaterialStateProperty.all(Colors.white),
), ),
onPressed: () async { onPressed: () async {
SharedPreferences localStorage =
await SharedPreferences.getInstance();
await localStorage.clear();
auth.logout(); auth.logout();
GoRouter.of(context).pop(); GoRouter.of(context).pop();
}, },

View File

@ -20,7 +20,7 @@ class TitleBarActionButtons extends StatelessWidget {
), ),
child: const Icon(Icons.minimize_rounded)), child: const Icon(Icons.minimize_rounded)),
TextButton( TextButton(
onPressed: () { onPressed: () async {
appWindow.maximizeOrRestore(); appWindow.maximizeOrRestore();
}, },
style: ButtonStyle( style: ButtonStyle(

View File

@ -1,36 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class SpotubePageRoute extends PageRouteBuilder { class SpotubePageRoute extends PageRouteBuilder {
final Widget child; final Widget child;
SpotubePageRoute({required this.child}) SpotubePageRoute({required this.child})
: super( : super(
pageBuilder: (context, animation, secondaryAnimation) => child, pageBuilder: (context, animation, secondaryAnimation) => child,
settings: RouteSettings(name: child.key.toString())); settings: RouteSettings(
name: child.key.toString(),
@override ),
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(
opacity: animation,
child: child,
);
}
}
class SpotubePage extends CustomTransitionPage {
SpotubePage({
required Widget child,
}) : super(
child: child,
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
); );
} }
class SpotubePage extends MaterialPage {
const SpotubePage({
required Widget child,
}) : super(child: child);
}

View File

@ -229,14 +229,14 @@ class TrackTile extends HookConsumerWidget {
Text(duration), Text(duration),
], ],
const SizedBox(width: 10), const SizedBox(width: 10),
if (auth.isLoggedIn) FutureBuilder<bool>(
FutureBuilder<bool>( future: spotify.tracks.me.containsOne(track.value.id!),
future: spotify.tracks.me.containsOne(track.value.id!), builder: (context, snapshot) {
builder: (context, snapshot) { return PopupMenuButton(
return PopupMenuButton( icon: const Icon(Icons.more_horiz_rounded),
icon: const Icon(Icons.more_horiz_rounded), itemBuilder: (context) {
itemBuilder: (context) { return [
return [ if (auth.isLoggedIn)
PopupMenuItem( PopupMenuItem(
child: Row( child: Row(
children: const [ children: const [
@ -247,17 +247,18 @@ class TrackTile extends HookConsumerWidget {
), ),
value: "add-playlist", value: "add-playlist",
), ),
if (userPlaylist) if (userPlaylist && auth.isLoggedIn)
PopupMenuItem( PopupMenuItem(
child: Row( child: Row(
children: const [ children: const [
Icon(Icons.remove_circle_outline_rounded), Icon(Icons.remove_circle_outline_rounded),
SizedBox(width: 10), SizedBox(width: 10),
Text("Remove from Playlist"), Text("Remove from Playlist"),
], ],
),
value: "remove-playlist",
), ),
value: "remove-playlist",
),
if (auth.isLoggedIn)
PopupMenuItem( PopupMenuItem(
child: Row( child: Row(
children: [ children: [
@ -270,36 +271,36 @@ class TrackTile extends HookConsumerWidget {
), ),
value: "favorite", value: "favorite",
), ),
PopupMenuItem( PopupMenuItem(
child: Row( child: Row(
children: const [ children: const [
Icon(Icons.share_rounded), Icon(Icons.share_rounded),
SizedBox(width: 10), SizedBox(width: 10),
Text("Share") Text("Share")
], ],
), ),
value: "share", value: "share",
) )
]; ];
}, },
onSelected: (value) { onSelected: (value) {
switch (value) { switch (value) {
case "favorite": case "favorite":
actionFavorite(snapshot.data == true); actionFavorite(snapshot.data == true);
break; break;
case "add-playlist": case "add-playlist":
actionAddToPlaylist(); actionAddToPlaylist();
break; break;
case "remove-playlist": case "remove-playlist":
actionRemoveFromPlaylist(); actionRemoveFromPlaylist();
break; break;
case "share": case "share":
actionShare(track.value); actionShare(track.value);
break; break;
} }
}, },
); );
}) })
], ],
); );
} }

View File

@ -1,7 +1,6 @@
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:hookified_infinite_scroll_pagination/hookified_infinite_scroll_pagination.dart';
import 'package:spotube/hooks/usePagingController.dart';
PagingController<P, ItemType> usePaginatedFutureProvider<T, P, ItemType>( PagingController<P, ItemType> usePaginatedFutureProvider<T, P, ItemType>(
FutureProvider<T> Function(P pageKey) createSnapshot, { FutureProvider<T> Function(P pageKey) createSnapshot, {
@ -18,18 +17,13 @@ PagingController<P, ItemType> usePaginatedFutureProvider<T, P, ItemType>(
}) { }) {
final currentPageKey = useState(firstPageKey); final currentPageKey = useState(firstPageKey);
final snapshot = ref.watch(createSnapshot(currentPageKey.value)); final snapshot = ref.watch(createSnapshot(currentPageKey.value));
final pagingController = final pagingController = usePagingController<P, ItemType>(
usePagingController<P, ItemType>(firstPageKey: firstPageKey); firstPageKey: firstPageKey,
useEffect(() { onPageRequest: (pageKey, pagingController) {
listener(pageKey) { if (currentPageKey.value != pageKey) {
if (currentPageKey.value != pageKey) { currentPageKey.value = pageKey;
currentPageKey.value = pageKey; }
} });
}
pagingController.addPageRequestListener(listener);
return () => pagingController.removePageRequestListener(listener);
}, [snapshot, currentPageKey]);
useEffect(() { useEffect(() {
snapshot.whenOrNull( snapshot.whenOrNull(

View File

@ -1,53 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
PagingController<PageKeyType, ItemType>
usePagingController<PageKeyType, ItemType>({
required final PageKeyType firstPageKey,
final int? invisibleItemsThreshold,
List<Object?>? keys,
}) {
return use(
_PagingControllerHook<PageKeyType, ItemType>(
firstPageKey: firstPageKey,
invisibleItemsThreshold: invisibleItemsThreshold,
keys: keys,
),
);
}
class _PagingControllerHook<PageKeyType, ItemType>
extends Hook<PagingController<PageKeyType, ItemType>> {
const _PagingControllerHook({
required this.firstPageKey,
this.invisibleItemsThreshold,
List<Object?>? keys,
}) : super(keys: keys);
final PageKeyType firstPageKey;
final int? invisibleItemsThreshold;
@override
HookState<PagingController<PageKeyType, ItemType>,
Hook<PagingController<PageKeyType, ItemType>>>
createState() => _PagingControllerHookState<PageKeyType, ItemType>();
}
class _PagingControllerHookState<PageKeyType, ItemType> extends HookState<
PagingController<PageKeyType, ItemType>,
_PagingControllerHook<PageKeyType, ItemType>> {
late final controller = PagingController<PageKeyType, ItemType>(
firstPageKey: hook.firstPageKey,
invisibleItemsThreshold: hook.invisibleItemsThreshold);
@override
PagingController<PageKeyType, ItemType> build(BuildContext context) =>
controller;
@override
void dispose() => controller.dispose();
@override
String get debugLabel => 'usePagingController';
}

View File

@ -1,11 +1,18 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
PaletteColor usePaletteColor(BuildContext context, String imageUrl) { final _paletteColorState = StateProvider<PaletteColor>(
final paletteColor = (ref) {
useState<PaletteColor>(PaletteColor(Colors.grey[300]!, 0)); return PaletteColor(Colors.grey[300]!, 0);
},
);
PaletteColor usePaletteColor(
BuildContext context, String imageUrl, WidgetRef ref) {
final paletteColor = ref.watch(_paletteColorState);
final mounted = useIsMounted(); final mounted = useIsMounted();
useEffect(() { useEffect(() {
@ -23,11 +30,11 @@ PaletteColor usePaletteColor(BuildContext context, String imageUrl) {
? palette.lightMutedColor ?? palette.lightVibrantColor ? palette.lightMutedColor ?? palette.lightVibrantColor
: palette.darkMutedColor ?? palette.darkVibrantColor; : palette.darkMutedColor ?? palette.darkVibrantColor;
if (color != null) { if (color != null) {
paletteColor.value = color; ref.read(_paletteColorState.notifier).state = color;
} }
}); });
return null; return null;
}, [imageUrl]); }, [imageUrl]);
return paletteColor.value; return paletteColor;
} }

View File

@ -37,10 +37,9 @@ void main() async {
doWhenWindowReady(() { doWhenWindowReady(() {
appWindow.minSize = appWindow.minSize =
Size(Platform.isAndroid || Platform.isIOS ? 280 : 359, 700); Size(Platform.isAndroid || Platform.isIOS ? 280 : 359, 700);
appWindow.size = const Size(900, 700);
appWindow.alignment = Alignment.center; appWindow.alignment = Alignment.center;
appWindow.maximize();
appWindow.title = "Spotube"; appWindow.title = "Spotube";
appWindow.maximize();
appWindow.show(); appWindow.show();
}); });
} }
@ -50,11 +49,10 @@ void main() async {
playbackProvider.overrideWithProvider(ChangeNotifierProvider( playbackProvider.overrideWithProvider(ChangeNotifierProvider(
(ref) { (ref) {
final youtube = ref.watch(youtubeProvider); final youtube = ref.watch(youtubeProvider);
final preferences = ref.watch(userPreferencesProvider);
return Playback( return Playback(
player: audioPlayerHandler, player: audioPlayerHandler,
youtube: youtube, youtube: youtube,
preferences: preferences, ref: ref,
); );
}, },
)) ))

View File

@ -51,7 +51,7 @@ class Auth extends PersistedChangeNotifier {
updatePersistence(); updatePersistence();
} }
logout() { void logout() {
_clientId = null; _clientId = null;
_clientSecret = null; _clientSecret = null;
_accessToken = null; _accessToken = null;

View File

@ -30,11 +30,11 @@ class Playback extends ChangeNotifier {
AudioPlayerHandler player; AudioPlayerHandler player;
YoutubeExplode youtube; YoutubeExplode youtube;
UserPreferences preferences; Ref ref;
Playback({ Playback({
required this.player, required this.player,
required this.youtube, required this.youtube,
required this.preferences, required this.ref,
CurrentPlaylist? currentPlaylist, CurrentPlaylist? currentPlaylist,
Track? currentTrack, Track? currentTrack,
}) : _currentPlaylist = currentPlaylist, }) : _currentPlaylist = currentPlaylist,
@ -206,6 +206,7 @@ class Playback extends ChangeNotifier {
// await player.play(); // await player.play();
return; return;
} }
final preferences = ref.read(userPreferencesProvider);
final spotubeTrack = await toSpotubeTrack( final spotubeTrack = await toSpotubeTrack(
youtube: youtube, youtube: youtube,
track: track, track: track,
@ -250,10 +251,9 @@ class Playback extends ChangeNotifier {
final playbackProvider = ChangeNotifierProvider<Playback>((ref) { final playbackProvider = ChangeNotifierProvider<Playback>((ref) {
final player = AudioPlayerHandler(); final player = AudioPlayerHandler();
final youtube = ref.watch(youtubeProvider); final youtube = ref.watch(youtubeProvider);
final preferences = ref.watch(userPreferencesProvider);
return Playback( return Playback(
player: player, player: player,
youtube: youtube, youtube: youtube,
preferences: preferences, ref: ref,
); );
}); });

View File

@ -268,6 +268,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.0" version: "3.1.0"
hookified_infinite_scroll_pagination:
dependency: "direct main"
description:
name: hookified_infinite_scroll_pagination
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
hooks_riverpod: hooks_riverpod:
dependency: "direct main" dependency: "direct main"
description: description:
@ -311,12 +318,12 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
infinite_scroll_pagination: infinite_scroll_pagination:
dependency: "direct main" dependency: transitive
description: description:
name: infinite_scroll_pagination name: infinite_scroll_pagination
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.0" version: "3.2.0"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -906,5 +913,5 @@ packages:
source: hosted source: hosted
version: "1.11.0" version: "1.11.0"
sdks: sdks:
dart: ">=2.17.0 <3.0.0" dart: ">=2.17.1 <3.0.0"
flutter: ">=3.0.0" flutter: ">=3.0.0"

View File

@ -41,7 +41,6 @@ dependencies:
git: https://github.com/KRTirtho/spotify-dart.git git: https://github.com/KRTirtho/spotify-dart.git
url_launcher: ^6.0.17 url_launcher: ^6.0.17
youtube_explode_dart: ^1.10.8 youtube_explode_dart: ^1.10.8
infinite_scroll_pagination: ^3.1.0
bitsdojo_window: ^0.1.2 bitsdojo_window: ^0.1.2
hotkey_manager: ^0.1.6 hotkey_manager: ^0.1.6
just_audio: ^0.9.18 just_audio: ^0.9.18
@ -61,6 +60,7 @@ dependencies:
package_info_plus: ^1.4.2 package_info_plus: ^1.4.2
version: ^2.0.0 version: ^2.0.0
audio_service: ^0.18.4 audio_service: ^0.18.4
hookified_infinite_scroll_pagination: ^0.1.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: