chore: use navigateTo instead of pushRoute

This commit is contained in:
Kingkor Roy Tirtho 2025-01-31 22:10:40 +06:00
parent 0037677383
commit 4b2d259add
45 changed files with 307 additions and 304 deletions

View File

@ -37,7 +37,7 @@ class AppRouter extends RootStackRouter {
initial: true, initial: true,
children: [ children: [
AutoRoute( AutoRoute(
path: "browse", path: "home",
page: HomeRoute.page, page: HomeRoute.page,
initial: true, initial: true,
), ),
@ -70,7 +70,7 @@ class AppRouter extends RootStackRouter {
page: UserArtistsRoute.page, page: UserArtistsRoute.page,
), ),
AutoRoute( AutoRoute(
path: "album", path: "albums",
page: UserAlbumsRoute.page, page: UserAlbumsRoute.page,
), ),
AutoRoute( AutoRoute(

View File

@ -8,6 +8,7 @@ class SideBarTiles {
final IconData icon; final IconData icon;
final String title; final String title;
final String id; final String id;
final String pathPrefix;
final PageRouteInfo route; final PageRouteInfo route;
SideBarTiles({ SideBarTiles({
@ -15,30 +16,35 @@ class SideBarTiles {
required this.title, required this.title,
required this.id, required this.id,
required this.route, required this.route,
required this.pathPrefix,
}); });
} }
List<SideBarTiles> getSidebarTileList(AppLocalizations l10n) => [ List<SideBarTiles> getSidebarTileList(AppLocalizations l10n) => [
SideBarTiles( SideBarTiles(
id: "browse", id: "home",
pathPrefix: "/home",
route: const HomeRoute(), route: const HomeRoute(),
icon: SpotubeIcons.home, icon: SpotubeIcons.home,
title: l10n.browse, title: l10n.browse,
), ),
SideBarTiles( SideBarTiles(
id: "search", id: "search",
pathPrefix: "/search",
route: const SearchRoute(), route: const SearchRoute(),
icon: SpotubeIcons.search, icon: SpotubeIcons.search,
title: l10n.search, title: l10n.search,
), ),
SideBarTiles( SideBarTiles(
id: "lyrics", id: "lyrics",
pathPrefix: "/lyrics",
route: LyricsRoute(), route: LyricsRoute(),
icon: SpotubeIcons.music, icon: SpotubeIcons.music,
title: l10n.lyrics, title: l10n.lyrics,
), ),
SideBarTiles( SideBarTiles(
id: "stats", id: "stats",
pathPrefix: "/stats",
route: const StatsRoute(), route: const StatsRoute(),
icon: SpotubeIcons.chart, icon: SpotubeIcons.chart,
title: l10n.stats, title: l10n.stats,
@ -48,24 +54,28 @@ List<SideBarTiles> getSidebarTileList(AppLocalizations l10n) => [
List<SideBarTiles> getSidebarLibraryTileList(AppLocalizations l10n) => [ List<SideBarTiles> getSidebarLibraryTileList(AppLocalizations l10n) => [
SideBarTiles( SideBarTiles(
id: "playlists", id: "playlists",
pathPrefix: "/library/playlists",
title: l10n.playlists, title: l10n.playlists,
route: const UserPlaylistsRoute(), route: const UserPlaylistsRoute(),
icon: SpotubeIcons.playlist, icon: SpotubeIcons.playlist,
), ),
SideBarTiles( SideBarTiles(
id: "artists", id: "artists",
pathPrefix: "/library/artists",
title: l10n.artists, title: l10n.artists,
route: const UserArtistsRoute(), route: const UserArtistsRoute(),
icon: SpotubeIcons.artist, icon: SpotubeIcons.artist,
), ),
SideBarTiles( SideBarTiles(
id: "albums", id: "albums",
pathPrefix: "/library/albums",
title: l10n.albums, title: l10n.albums,
route: const UserAlbumsRoute(), route: const UserAlbumsRoute(),
icon: SpotubeIcons.album, icon: SpotubeIcons.album,
), ),
SideBarTiles( SideBarTiles(
id: "local_library", id: "local_library",
pathPrefix: "/library/local",
title: l10n.local_library, title: l10n.local_library,
route: const UserLocalLibraryRoute(), route: const UserLocalLibraryRoute(),
icon: SpotubeIcons.device, icon: SpotubeIcons.device,
@ -74,25 +84,29 @@ List<SideBarTiles> getSidebarLibraryTileList(AppLocalizations l10n) => [
List<SideBarTiles> getNavbarTileList(AppLocalizations l10n) => [ List<SideBarTiles> getNavbarTileList(AppLocalizations l10n) => [
SideBarTiles( SideBarTiles(
id: "browse", id: "home",
pathPrefix: "/home",
route: const HomeRoute(), route: const HomeRoute(),
icon: SpotubeIcons.home, icon: SpotubeIcons.home,
title: l10n.browse, title: l10n.browse,
), ),
SideBarTiles( SideBarTiles(
id: "search", id: "search",
pathPrefix: "/search",
route: const SearchRoute(), route: const SearchRoute(),
icon: SpotubeIcons.search, icon: SpotubeIcons.search,
title: l10n.search, title: l10n.search,
), ),
SideBarTiles( SideBarTiles(
id: "library", id: "library",
pathPrefix: "/library",
route: const UserPlaylistsRoute(), route: const UserPlaylistsRoute(),
icon: SpotubeIcons.library, icon: SpotubeIcons.library,
title: l10n.library, title: l10n.library,
), ),
SideBarTiles( SideBarTiles(
id: "stats", id: "stats",
pathPrefix: "/stats",
route: const StatsRoute(), route: const StatsRoute(),
icon: SpotubeIcons.chart, icon: SpotubeIcons.chart,
title: l10n.stats, title: l10n.stats,

View File

@ -40,7 +40,7 @@ class AnonymousFallback extends ConsumerWidget {
Text(context.l10n.not_logged_in), Text(context.l10n.not_logged_in),
Button.primary( Button.primary(
child: Text(context.l10n.login_with_spotify), child: Text(context.l10n.login_with_spotify),
onPressed: () => context.pushRoute(const SettingsRoute()), onPressed: () => context.navigateTo(const SettingsRoute()),
) )
], ],
), ),

View File

@ -50,7 +50,7 @@ class ArtistLink extends StatelessWidget {
onRouteChange?.call("/artist/${artist.value.id}"); onRouteChange?.call("/artist/${artist.value.id}");
} else { } else {
context context
.pushRoute(ArtistRoute(artistId: artist.value.id!)); .navigateTo(ArtistRoute(artistId: artist.value.id!));
} }
}, },
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,

View File

@ -28,7 +28,7 @@ class LinkText<T> extends StatelessWidget {
text, text,
onTap: () { onTap: () {
if (push) { if (push) {
context.pushRoute(route); context.navigateTo(route);
} else { } else {
context.navigateTo(route); context.navigateTo(route);
} }

View File

@ -210,7 +210,7 @@ class TrackOptions extends HookConsumerWidget {
onSelected: (value) async { onSelected: (value) async {
switch (value) { switch (value) {
case TrackOptionValue.album: case TrackOptionValue.album:
await context.pushRoute( await context.navigateTo(
AlbumRoute(id: track.album!.id!, album: track.album!), AlbumRoute(id: track.album!.id!, album: track.album!),
); );
break; break;
@ -345,7 +345,7 @@ class TrackOptions extends HookConsumerWidget {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: ArtistLink( child: ArtistLink(
artists: track.artists!, artists: track.artists!,
onOverflowArtistClick: () => context.pushRoute( onOverflowArtistClick: () => context.navigateTo(
TrackRoute(trackId: track.id!), TrackRoute(trackId: track.id!),
), ),
), ),

View File

@ -234,7 +234,7 @@ class TrackTile extends HookConsumerWidget {
), ),
onPressed: () { onPressed: () {
context context
.pushRoute(TrackRoute(trackId: track.id!)); .navigateTo(TrackRoute(trackId: track.id!));
}, },
child: Text( child: Text(
track.name!, track.name!,
@ -284,7 +284,7 @@ class TrackTile extends HookConsumerWidget {
child: ArtistLink( child: ArtistLink(
artists: track.artists ?? [], artists: track.artists ?? [],
onOverflowArtistClick: () { onOverflowArtistClick: () {
context.pushRoute( context.navigateTo(
TrackRoute(trackId: track.id!), TrackRoute(trackId: track.id!),
); );
}, },

View File

@ -28,19 +28,20 @@ void useDeepLinking(WidgetRef ref, AppRouter router) {
switch (url.pathSegments.first) { switch (url.pathSegments.first) {
case "album": case "album":
final album = await spotify.albums.get(url.pathSegments.last); final album = await spotify.albums.get(url.pathSegments.last);
router.push( router.navigate(
AlbumRoute(id: album.id!, album: album), AlbumRoute(id: album.id!, album: album),
); );
break; break;
case "artist": case "artist":
router.push(ArtistRoute(artistId: url.pathSegments.last)); router.navigate(ArtistRoute(artistId: url.pathSegments.last));
break; break;
case "playlist": case "playlist":
final playlist = await spotify.playlists.get(url.pathSegments.last); final playlist = await spotify.playlists.get(url.pathSegments.last);
router.push(PlaylistRoute(id: playlist.id!, playlist: playlist)); router
.navigate(PlaylistRoute(id: playlist.id!, playlist: playlist));
break; break;
case "track": case "track":
router.push(TrackRoute(trackId: url.pathSegments.last)); router.navigate(TrackRoute(trackId: url.pathSegments.last));
break; break;
default: default:
break; break;
@ -65,19 +66,19 @@ void useDeepLinking(WidgetRef ref, AppRouter router) {
switch (startSegment) { switch (startSegment) {
case "spotify:album": case "spotify:album":
final album = await spotify.albums.get(endSegment); final album = await spotify.albums.get(endSegment);
await router.push( await router.navigate(
AlbumRoute(id: album.id!, album: album), AlbumRoute(id: album.id!, album: album),
); );
break; break;
case "spotify:artist": case "spotify:artist":
await router.push(ArtistRoute(artistId: endSegment)); await router.navigate(ArtistRoute(artistId: endSegment));
break; break;
case "spotify:track": case "spotify:track":
await router.push(TrackRoute(trackId: endSegment)); await router.navigate(TrackRoute(trackId: endSegment));
break; break;
case "spotify:playlist": case "spotify:playlist":
final playlist = await spotify.playlists.get(endSegment); final playlist = await spotify.playlists.get(endSegment);
await router.push( await router.navigate(
PlaylistRoute(id: playlist.id!, playlist: playlist), PlaylistRoute(id: playlist.id!, playlist: playlist),
); );
break; break;

View File

@ -69,7 +69,7 @@ class AlbumCard extends HookConsumerWidget {
"${album.albumType?.formatted}${album.artists?.asString() ?? ""}"; "${album.albumType?.formatted}${album.artists?.asString() ?? ""}";
void onTap() { void onTap() {
context.pushRoute(AlbumRoute(id: album.id!, album: album)); context.navigateTo(AlbumRoute(id: album.id!, album: album));
} }
void onPlaybuttonPressed() async { void onPlaybuttonPressed() async {

View File

@ -36,7 +36,7 @@ class ArtistCard extends HookConsumerWidget {
width: 180, width: 180,
child: Button.card( child: Button.card(
onPressed: () { onPressed: () {
context.pushRoute(ArtistRoute(artistId: artist.id!)); context.navigateTo(ArtistRoute(artistId: artist.id!));
}, },
child: Column( child: Column(
children: [ children: [

View File

@ -26,7 +26,7 @@ class ConnectDeviceButton extends HookConsumerWidget {
return IconButton.ghost( return IconButton.ghost(
icon: const Icon(SpotubeIcons.speaker), icon: const Icon(SpotubeIcons.speaker),
onPressed: () { onPressed: () {
context.pushRoute(const ConnectRoute()); context.navigateTo(const ConnectRoute());
}, },
); );
} }
@ -35,7 +35,7 @@ class ConnectDeviceButton extends HookConsumerWidget {
width: double.infinity, width: double.infinity,
child: Button.primary( child: Button.primary(
onPressed: () { onPressed: () {
context.pushRoute(const ConnectRoute()); context.navigateTo(const ConnectRoute());
}, },
trailing: const Icon(SpotubeIcons.speaker), trailing: const Icon(SpotubeIcons.speaker),
child: Text( child: Text(
@ -50,7 +50,7 @@ class ConnectDeviceButton extends HookConsumerWidget {
children: [ children: [
SecondaryBadge( SecondaryBadge(
onPressed: () { onPressed: () {
context.pushRoute(const ConnectRoute()); context.navigateTo(const ConnectRoute());
}, },
style: const ButtonStyle.secondary(size: ButtonSize(.8)), style: const ButtonStyle.secondary(size: ButtonSize(.8)),
leading: connectClients.asData?.value.resolvedService != null leading: connectClients.asData?.value.resolvedService != null
@ -70,7 +70,7 @@ class ConnectDeviceButton extends HookConsumerWidget {
IconButton.primary( IconButton.primary(
icon: const Icon(SpotubeIcons.speaker), icon: const Icon(SpotubeIcons.speaker),
onPressed: () { onPressed: () {
context.pushRoute(const ConnectRoute()); context.navigateTo(const ConnectRoute());
}, },
) )
], ],

View File

@ -40,7 +40,7 @@ class HomePageFeedSection extends HookConsumerWidget {
titleTrailing: Button.text( titleTrailing: Button.text(
child: Text(context.l10n.browse_all), child: Text(context.l10n.browse_all),
onPressed: () { onPressed: () {
context.pushRoute(HomeFeedSectionRoute(sectionUri: section.uri)); context.navigateTo(HomeFeedSectionRoute(sectionUri: section.uri));
}, },
), ),
); );

View File

@ -50,7 +50,7 @@ class FriendItem extends HookConsumerWidget {
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () {
context context
.pushRoute(TrackRoute(trackId: friend.track.id)); .navigateTo(TrackRoute(trackId: friend.track.id));
}, },
), ),
const TextSpan(text: ""), const TextSpan(text: ""),
@ -64,7 +64,7 @@ class FriendItem extends HookConsumerWidget {
text: " ${friend.track.artist.name}", text: " ${friend.track.artist.name}",
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () {
context.pushRoute( context.navigateTo(
ArtistRoute(artistId: friend.track.artist.id), ArtistRoute(artistId: friend.track.artist.id),
); );
}, },
@ -74,7 +74,7 @@ class FriendItem extends HookConsumerWidget {
text: friend.track.context.name, text: friend.track.context.name,
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () async { ..onTap = () async {
context.router.pushNamed( context.router.navigateNamed(
"/${friend.track.context.path}", "/${friend.track.context.path}",
// extra: // extra:
// !friend.track.context.path.startsWith("album") // !friend.track.context.path.startsWith("album")
@ -98,7 +98,7 @@ class FriendItem extends HookConsumerWidget {
final album = final album =
await spotify.albums.get(friend.track.album.id); await spotify.albums.get(friend.track.album.id);
if (context.mounted) { if (context.mounted) {
context.pushRoute( context.navigateTo(
AlbumRoute(id: album.id!, album: album), AlbumRoute(id: album.id!, album: album),
); );
} }

View File

@ -76,7 +76,7 @@ class GenreSectionCard extends HookConsumerWidget {
).h3(), ).h3(),
Button.link( Button.link(
onPressed: () { onPressed: () {
context.router.push( context.navigateTo(
GenrePlaylistsRoute( GenrePlaylistsRoute(
id: category.id!, id: category.id!,
category: category, category: category,

View File

@ -47,7 +47,7 @@ class GenreSectionCardPlaylistCard extends HookConsumerWidget {
}, },
), ),
onPressed: () { onPressed: () {
context.pushRoute( context.navigateTo(
PlaylistRoute(id: playlist.id!, playlist: playlist), PlaylistRoute(id: playlist.id!, playlist: playlist),
); );
}, },

View File

@ -48,7 +48,7 @@ class HomeGenresSection extends HookConsumerWidget {
), ),
Button.link( Button.link(
onPressed: () { onPressed: () {
context.pushRoute(const GenreRoute()); context.navigateTo(const GenreRoute());
}, },
child: Text( child: Text(
context.l10n.browse_all, context.l10n.browse_all,

View File

@ -59,7 +59,7 @@ class LocalFolderItem extends HookConsumerWidget {
return Button( return Button(
onPressed: () { onPressed: () {
context.pushRoute( context.navigateTo(
LocalLibraryRoute( LocalLibraryRoute(
location: folder, location: folder,
isCache: isCacheFolder, isCache: isCacheFolder,

View File

@ -67,7 +67,7 @@ class DownloadItem extends HookConsumerWidget {
artists: track.artists ?? <Artist>[], artists: track.artists ?? <Artist>[],
mainAxisAlignment: WrapAlignment.start, mainAxisAlignment: WrapAlignment.start,
onOverflowArtistClick: () { onOverflowArtistClick: () {
context.pushRoute(TrackRoute(trackId: track.id!)); context.navigateTo(TrackRoute(trackId: track.id!));
}, },
), ),
trailing: isQueryingSourceInfo trailing: isQueryingSourceInfo

View File

@ -93,7 +93,7 @@ class PlayerView extends HookConsumerWidget {
}, [panelController.isAttached && panelController.isPanelOpen]); }, [panelController.isAttached && panelController.isPanelOpen]);
return AppPopScope( return AppPopScope(
canPop: context.watchRouter.canPop(), canPop: false,
onPopInvoked: (didPop) async { onPopInvoked: (didPop) async {
await panelController.close(); await panelController.close();
}, },
@ -209,9 +209,9 @@ class PlayerView extends HookConsumerWidget {
.copyWith(fontWeight: FontWeight.bold), .copyWith(fontWeight: FontWeight.bold),
onRouteChange: (route) { onRouteChange: (route) {
panelController.close(); panelController.close();
context.router.pushNamed(route); context.router.navigateNamed(route);
}, },
onOverflowArtistClick: () => context.pushRoute( onOverflowArtistClick: () => context.navigateTo(
TrackRoute(trackId: currentTrack!.id!), TrackRoute(trackId: currentTrack!.id!),
), ),
), ),

View File

@ -1,4 +1,3 @@
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:sliding_up_panel/sliding_up_panel.dart'; import 'package:sliding_up_panel/sliding_up_panel.dart';
@ -8,6 +7,10 @@ import 'package:spotube/modules/root/spotube_navigation_bar.dart';
import 'package:spotube/modules/player/player.dart'; import 'package:spotube/modules/player/player.dart';
import 'package:spotube/provider/audio_player/audio_player.dart'; import 'package:spotube/provider/audio_player/audio_player.dart';
final playerOverlayControllerProvider = StateProvider<PanelController>((ref) {
return PanelController();
});
class PlayerOverlay extends HookConsumerWidget { class PlayerOverlay extends HookConsumerWidget {
final String albumArt; final String albumArt;
@ -23,7 +26,7 @@ class PlayerOverlay extends HookConsumerWidget {
final screenSize = MediaQuery.sizeOf(context); final screenSize = MediaQuery.sizeOf(context);
final panelController = useMemoized(() => PanelController(), []); final panelController = ref.watch(playerOverlayControllerProvider);
return SlidingUpPanel( return SlidingUpPanel(
maxHeight: screenSize.height, maxHeight: screenSize.height,

View File

@ -81,10 +81,10 @@ class PlayerTrackDetails extends HookConsumerWidget {
ArtistLink( ArtistLink(
artists: playback.activeTrack?.artists ?? [], artists: playback.activeTrack?.artists ?? [],
onRouteChange: (route) { onRouteChange: (route) {
context.router.pushNamed(route); context.router.navigateNamed(route);
}, },
onOverflowArtistClick: () => onOverflowArtistClick: () =>
context.pushRoute(TrackRoute(trackId: track!.id!)), context.navigateTo(TrackRoute(trackId: track!.id!)),
) )
], ],
), ),

View File

@ -73,7 +73,7 @@ class PlaylistCard extends HookConsumerWidget {
} }
void onTap() { void onTap() {
context.pushRoute(PlaylistRoute(id: playlist.id!, playlist: playlist)); context.navigateTo(PlaylistRoute(id: playlist.id!, playlist: playlist));
} }
void onPlaybuttonPressed() async { void onPlaybuttonPressed() async {

View File

@ -52,12 +52,12 @@ class Sidebar extends HookConsumerWidget {
final router = context.watchRouter; final router = context.watchRouter;
final selectedIndex = tileList.indexWhere( final selectedIndex = tileList.indexWhere(
(e) => router.topRoute.name == e.route.routeName, (e) => router.currentPath.startsWith(e.pathPrefix),
); );
if (layoutMode == LayoutMode.compact || if (layoutMode == LayoutMode.compact ||
(mediaQuery.smAndDown && layoutMode == LayoutMode.adaptive)) { (mediaQuery.smAndDown && layoutMode == LayoutMode.adaptive)) {
return Scaffold(child: child); return child;
} }
final navigationButtons = [ final navigationButtons = [

View File

@ -97,7 +97,7 @@ class SidebarFooter extends HookConsumerWidget implements NavigationBarItem {
Flexible( Flexible(
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
context.pushRoute(const ProfileRoute()); context.navigateTo(const ProfileRoute());
}, },
child: Row( child: Row(
children: [ children: [
@ -125,7 +125,7 @@ class SidebarFooter extends HookConsumerWidget implements NavigationBarItem {
variance: ButtonVariance.ghost, variance: ButtonVariance.ghost,
icon: const Icon(SpotubeIcons.settings), icon: const Icon(SpotubeIcons.settings),
onPressed: () { onPressed: () {
context.pushRoute(const SettingsRoute()); context.navigateTo(const SettingsRoute());
}, },
), ),
], ],

View File

@ -6,7 +6,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/collections/side_bar_tiles.dart'; import 'package:spotube/collections/side_bar_tiles.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
@ -35,23 +34,13 @@ class SpotubeNavigationBar extends HookConsumerWidget {
[context.l10n], [context.l10n],
); );
final libraryTiles = useMemoized(
() => getSidebarLibraryTileList(context.l10n)
.map((e) => e.route.routeName)
.toList(),
[context.l10n],
);
final panelHeight = ref.watch(navigationPanelHeight); final panelHeight = ref.watch(navigationPanelHeight);
final router = context.watchRouter; final router = context.watchRouter;
final selectedIndex = max( final selectedIndex = max(
0, 0,
navbarTileList.indexWhere( navbarTileList.indexWhere(
(e) => (e) => router.currentPath.startsWith(e.pathPrefix),
router.topRoute.name == e.route.routeName ||
(libraryTiles.contains(router.topRoute.name) &&
e.route.routeName == LibraryRoute.name),
), ),
); );

View File

@ -37,14 +37,14 @@ class StatsAlbumItem extends StatelessWidget {
artists: album.artists ?? [], artists: album.artists ?? [],
mainAxisAlignment: WrapAlignment.start, mainAxisAlignment: WrapAlignment.start,
onOverflowArtistClick: () => onOverflowArtistClick: () =>
context.pushRoute(AlbumRoute(id: album.id!, album: album)), context.navigateTo(AlbumRoute(id: album.id!, album: album)),
), ),
), ),
], ],
), ),
trailing: info, trailing: info,
onPressed: () { onPressed: () {
context.pushRoute(AlbumRoute(id: album.id!, album: album)); context.navigateTo(AlbumRoute(id: album.id!, album: album));
}, },
); );
} }

View File

@ -30,7 +30,7 @@ class StatsArtistItem extends StatelessWidget {
), ),
trailing: info, trailing: info,
onPressed: () { onPressed: () {
context.pushRoute(ArtistRoute(artistId: artist.id!)); context.navigateTo(ArtistRoute(artistId: artist.id!));
}, },
); );
} }

View File

@ -35,7 +35,7 @@ class StatsPlaylistItem extends StatelessWidget {
), ),
trailing: info, trailing: info,
onPressed: () { onPressed: () {
context.pushRoute(PlaylistRoute(id: playlist.id!, playlist: playlist)); context.navigateTo(PlaylistRoute(id: playlist.id!, playlist: playlist));
}, },
); );
} }

View File

@ -35,12 +35,12 @@ class StatsTrackItem extends StatelessWidget {
artists: track.artists!, artists: track.artists!,
mainAxisAlignment: WrapAlignment.start, mainAxisAlignment: WrapAlignment.start,
onOverflowArtistClick: () { onOverflowArtistClick: () {
context.pushRoute(TrackRoute(trackId: track.id!)); context.navigateTo(TrackRoute(trackId: track.id!));
}, },
), ),
trailing: info, trailing: info,
onPressed: () { onPressed: () {
context.pushRoute(TrackRoute(trackId: track.id!)); context.navigateTo(TrackRoute(trackId: track.id!));
}, },
); );
} }

View File

@ -45,7 +45,7 @@ class StatsPageSummarySection extends HookConsumerWidget {
description: context.l10n.summary_listened_to_music, description: context.l10n.summary_listened_to_music,
color: Colors.indigo, color: Colors.indigo,
onTap: () { onTap: () {
context.pushRoute(const StatsMinutesRoute()); context.navigateTo(const StatsMinutesRoute());
}, },
), ),
SummaryCard( SummaryCard(
@ -54,7 +54,7 @@ class StatsPageSummarySection extends HookConsumerWidget {
description: context.l10n.summary_streamed_overall, description: context.l10n.summary_streamed_overall,
color: Colors.blue, color: Colors.blue,
onTap: () { onTap: () {
context.pushRoute(const StatsStreamsRoute()); context.navigateTo(const StatsStreamsRoute());
}, },
), ),
SummaryCard.unformatted( SummaryCard.unformatted(
@ -63,7 +63,7 @@ class StatsPageSummarySection extends HookConsumerWidget {
description: context.l10n.summary_owed_to_artists, description: context.l10n.summary_owed_to_artists,
color: Colors.green, color: Colors.green,
onTap: () { onTap: () {
context.pushRoute(const StatsStreamsRoute()); context.navigateTo(const StatsStreamsRoute());
}, },
), ),
SummaryCard( SummaryCard(
@ -72,7 +72,7 @@ class StatsPageSummarySection extends HookConsumerWidget {
description: context.l10n.summary_music_reached_you, description: context.l10n.summary_music_reached_you,
color: Colors.yellow, color: Colors.yellow,
onTap: () { onTap: () {
context.pushRoute(const StatsArtistsRoute()); context.navigateTo(const StatsArtistsRoute());
}, },
), ),
SummaryCard( SummaryCard(
@ -81,7 +81,7 @@ class StatsPageSummarySection extends HookConsumerWidget {
description: context.l10n.summary_got_your_love, description: context.l10n.summary_got_your_love,
color: Colors.pink, color: Colors.pink,
onTap: () { onTap: () {
context.pushRoute(const StatsAlbumsRoute()); context.navigateTo(const StatsAlbumsRoute());
}, },
), ),
SummaryCard( SummaryCard(
@ -90,7 +90,7 @@ class StatsPageSummarySection extends HookConsumerWidget {
description: context.l10n.summary_were_on_repeat, description: context.l10n.summary_were_on_repeat,
color: Colors.teal, color: Colors.teal,
onTap: () { onTap: () {
context.pushRoute(const StatsPlaylistsRoute()); context.navigateTo(const StatsPlaylistsRoute());
}, },
), ),
]), ]),

View File

@ -72,7 +72,7 @@ class ConnectPage extends HookConsumerWidget {
: null, : null,
onPressed: () { onPressed: () {
if (selected) { if (selected) {
context.pushRoute(const ConnectControlRoute()); context.navigateTo(const ConnectControlRoute());
} else { } else {
connectClientsNotifier.resolveService(device); connectClientsNotifier.resolveService(device);
} }

View File

@ -115,7 +115,7 @@ class ConnectControlPage extends HookConsumerWidget {
style: typography.h4, style: typography.h4,
onTap: () { onTap: () {
if (playlist.activeTrack == null) return; if (playlist.activeTrack == null) return;
context.pushRoute( context.navigateTo(
TrackRoute( TrackRoute(
trackId: playlist.activeTrack!.id!), trackId: playlist.activeTrack!.id!),
); );
@ -127,7 +127,7 @@ class ConnectControlPage extends HookConsumerWidget {
artists: playlist.activeTrack?.artists ?? [], artists: playlist.activeTrack?.artists ?? [],
textStyle: typography.normal, textStyle: typography.normal,
mainAxisAlignment: WrapAlignment.start, mainAxisAlignment: WrapAlignment.start,
onOverflowArtistClick: () => context.pushRoute( onOverflowArtistClick: () => context.navigateTo(
TrackRoute(trackId: playlist.activeTrack!.id!), TrackRoute(trackId: playlist.activeTrack!.id!),
), ),
), ),

View File

@ -51,7 +51,7 @@ class GenrePage extends HookConsumerWidget {
final gradient = gradients[Random().nextInt(gradients.length)]; final gradient = gradients[Random().nextInt(gradients.length)];
return CardImage( return CardImage(
onPressed: () { onPressed: () {
context.pushRoute( context.navigateTo(
GenrePlaylistsRoute( GenrePlaylistsRoute(
id: category.id!, id: category.id!,
category: category, category: category,

View File

@ -54,7 +54,7 @@ class HomePage extends HookConsumerWidget {
IconButton.ghost( IconButton.ghost(
icon: const Icon(SpotubeIcons.settings, size: 20), icon: const Icon(SpotubeIcons.settings, size: 20),
onPressed: () { onPressed: () {
context.pushRoute(const SettingsRoute()); context.navigateTo(const SettingsRoute());
}, },
), ),
const Gap(10), const Gap(10),

View File

@ -24,6 +24,7 @@ class LibraryPage extends HookConsumerWidget {
...getSidebarLibraryTileList(context.l10n), ...getSidebarLibraryTileList(context.l10n),
SideBarTiles( SideBarTiles(
id: "downloads", id: "downloads",
pathPrefix: "library/downloads",
title: context.l10n.downloads, title: context.l10n.downloads,
route: const UserDownloadsRoute(), route: const UserDownloadsRoute(),
icon: SpotubeIcons.download, icon: SpotubeIcons.download,
@ -32,42 +33,48 @@ class LibraryPage extends HookConsumerWidget {
[context.l10n], [context.l10n],
); );
final index = sidebarLibraryTileList.indexWhere( final index = sidebarLibraryTileList.indexWhere(
(e) => router.topRoute.name == e.route.routeName, (e) => router.currentPath.startsWith(e.pathPrefix),
); );
return SafeArea( return PopScope(
bottom: false, canPop: false,
child: LayoutBuilder(builder: (context, constraints) { onPopInvokedWithResult: (didPop, result) {
return Scaffold( context.navigateTo(const HomeRoute());
headers: [ },
if (constraints.smAndDown) child: SafeArea(
TitleBar( bottom: false,
child: SingleChildScrollView( child: LayoutBuilder(builder: (context, constraints) {
scrollDirection: Axis.horizontal, return Scaffold(
child: TabList( headers: [
index: index, if (constraints.smAndDown)
children: [ TitleBar(
for (final tile in sidebarLibraryTileList) child: SingleChildScrollView(
TabButton( scrollDirection: Axis.horizontal,
child: Badge( child: TabList(
isLabelVisible: index: index,
tile.id == 'downloads' && downloadingCount > 0, children: [
label: Text(downloadingCount.toString()), for (final tile in sidebarLibraryTileList)
child: Text(tile.title), TabButton(
child: Badge(
isLabelVisible: tile.id == 'downloads' &&
downloadingCount > 0,
label: Text(downloadingCount.toString()),
child: Text(tile.title),
),
onPressed: () {
context.navigateTo(tile.route);
},
), ),
onPressed: () { ],
context.navigateTo(tile.route); ),
},
),
],
), ),
), ),
), const Gap(10),
const Gap(10), ],
], child: const AutoRouter(),
child: const AutoRouter(), );
); }),
}), ),
); );
} }
} }

View File

@ -663,7 +663,7 @@ class PlaylistGeneratorPage extends HookConsumerWidget {
min: min.value, min: min.value,
target: target.value, target: target.value,
); );
context.pushRoute( context.navigateTo(
PlaylistGenerateResultRoute( PlaylistGenerateResultRoute(
state: routeState, state: routeState,
), ),

View File

@ -114,7 +114,7 @@ class UserPlaylistsPage extends HookConsumerWidget {
leading: const Icon(SpotubeIcons.magic), leading: const Icon(SpotubeIcons.magic),
child: Text(context.l10n.generate), child: Text(context.l10n.generate),
onPressed: () { onPressed: () {
context.pushRoute(const PlaylistGeneratorRoute()); context.navigateTo(const PlaylistGeneratorRoute());
}, },
), ),
const Gap(10), const Gap(10),

View File

@ -21,7 +21,7 @@ Future<void> Function() useLoginCallback(WidgetRef ref) {
return useCallback(() async { return useCallback(() async {
if (kIsMobile || kIsMacOS) { if (kIsMobile || kIsMacOS) {
context.pushRoute(const WebViewLoginRoute()); context.navigateTo(const WebViewLoginRoute());
return; return;
} }

View File

@ -3,18 +3,13 @@ import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/collections/side_bar_tiles.dart';
import 'package:spotube/components/framework/app_pop_scope.dart';
import 'package:spotube/modules/root/bottom_player.dart'; import 'package:spotube/modules/root/bottom_player.dart';
import 'package:spotube/modules/root/sidebar/sidebar.dart'; import 'package:spotube/modules/root/sidebar/sidebar.dart';
import 'package:spotube/modules/root/spotube_navigation_bar.dart'; import 'package:spotube/modules/root/spotube_navigation_bar.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/configurators/use_endless_playback.dart'; import 'package:spotube/hooks/configurators/use_endless_playback.dart';
import 'package:spotube/modules/root/use_downloader_dialogs.dart'; import 'package:spotube/modules/root/use_downloader_dialogs.dart';
import 'package:spotube/modules/root/use_global_subscriptions.dart'; import 'package:spotube/modules/root/use_global_subscriptions.dart';
import 'package:spotube/provider/glance/glance.dart'; import 'package:spotube/provider/glance/glance.dart';
import 'package:spotube/utils/platform.dart';
@RoutePage() @RoutePage()
class RootAppPage extends HookConsumerWidget { class RootAppPage extends HookConsumerWidget {
@ -42,10 +37,6 @@ class RootAppPage extends HookConsumerWidget {
return null; return null;
}, [backgroundColor, brightness]); }, [backgroundColor, brightness]);
final navTileNames = useMemoized(() {
return getSidebarTileList(context.l10n).map((s) => s.route).toList();
}, []);
final scaffold = MediaQuery.removeViewInsets( final scaffold = MediaQuery.removeViewInsets(
context: context, context: context,
removeBottom: true, removeBottom: true,
@ -59,25 +50,6 @@ class RootAppPage extends HookConsumerWidget {
), ),
); );
if (!kIsAndroid) { return scaffold;
return scaffold;
}
final topRoute = context.router.topRoute;
final canPop = navTileNames.any((name) => name.routeName == topRoute.name);
return AppPopScope(
canPop: canPop,
onPopInvoked: (didPop) {
if (didPop) return;
if (topRoute.path == const HomeRoute().fragment) {
SystemNavigator.pop();
} else {
context.navigateTo(const HomeRoute());
}
},
child: scaffold,
);
} }
} }

View File

@ -6,6 +6,7 @@ import 'package:spotify/spotify.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart'; import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
@ -68,165 +69,174 @@ class SearchPage extends HookConsumerWidget {
); );
} }
return SafeArea( return PopScope(
bottom: false, canPop: false,
child: Scaffold( onPopInvokedWithResult: (didPop, result) {
headers: [ context.navigateTo(const HomeRoute());
if (kTitlebarVisible) },
const TitleBar(automaticallyImplyLeading: true, height: 30) child: SafeArea(
], bottom: false,
child: auth.asData?.value == null child: Scaffold(
? const AnonymousFallback() headers: [
: Column( if (kTitlebarVisible)
children: [ const TitleBar(automaticallyImplyLeading: true, height: 30)
Row( ],
crossAxisAlignment: CrossAxisAlignment.center, child: auth.asData?.value == null
children: [ ? const AnonymousFallback()
Expanded( : Column(
child: Padding( children: [
padding: const EdgeInsets.all(20), Row(
child: ListenableBuilder( crossAxisAlignment: CrossAxisAlignment.center,
listenable: controller, children: [
builder: (context, _) { Expanded(
final suggestions = controller.text.isEmpty child: Padding(
? KVStoreService.recentSearches padding: const EdgeInsets.all(20),
: KVStoreService.recentSearches child: ListenableBuilder(
.where( listenable: controller,
(s) => builder: (context, _) {
weightedRatio( final suggestions = controller.text.isEmpty
s.toLowerCase(), ? KVStoreService.recentSearches
controller.text.toLowerCase(), : KVStoreService.recentSearches
) > .where(
50, (s) =>
) weightedRatio(
.toList(); s.toLowerCase(),
controller.text.toLowerCase(),
) >
50,
)
.toList();
return KeyboardListener( return KeyboardListener(
focusNode: focusNode, focusNode: focusNode,
autofocus: true,
onKeyEvent: (value) {
final isEnter = value.logicalKey ==
LogicalKeyboardKey.enter;
if (isEnter) {
onSubmitted(controller.text);
focusNode.unfocus();
}
},
child: AutoComplete(
autofocus: true, autofocus: true,
controller: controller, onKeyEvent: (value) {
suggestions: suggestions, final isEnter = value.logicalKey ==
leading: const Icon(SpotubeIcons.search), LogicalKeyboardKey.enter;
textInputAction: TextInputAction.search,
placeholder: Text(context.l10n.search), if (isEnter) {
trailing: AnimatedCrossFade( onSubmitted(controller.text);
duration: focusNode.unfocus();
const Duration(milliseconds: 300), }
crossFadeState: controller.text.isNotEmpty
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
firstChild: IconButton.ghost(
size: ButtonSize.small,
icon: const Icon(SpotubeIcons.close),
onPressed: () {
controller.clear();
},
),
secondChild:
const SizedBox.square(dimension: 28),
),
onAcceptSuggestion: (index) {
controller.text =
KVStoreService.recentSearches[index];
ref
.read(searchTermStateProvider
.notifier)
.state =
KVStoreService.recentSearches[index];
}, },
onChanged: (value) {}, child: AutoComplete(
onSubmitted: onSubmitted, autofocus: true,
), controller: controller,
); suggestions: suggestions,
}), leading: const Icon(SpotubeIcons.search),
), textInputAction: TextInputAction.search,
), placeholder: Text(context.l10n.search),
], trailing: AnimatedCrossFade(
), duration:
Expanded( const Duration(milliseconds: 300),
child: AnimatedSwitcher( crossFadeState:
duration: const Duration(milliseconds: 300), controller.text.isNotEmpty
child: switch ((searchTerm.isEmpty, isFetching)) { ? CrossFadeState.showFirst
(true, false) => Column( : CrossFadeState.showSecond,
children: [ firstChild: IconButton.ghost(
SizedBox( size: ButtonSize.small,
height: mediaQuery.height * 0.2, icon: const Icon(SpotubeIcons.close),
), onPressed: () {
Undraw( controller.clear();
illustration: UndrawIllustration.explore, },
color: theme.colorScheme.primary, ),
height: 200 * theme.scaling, secondChild: const SizedBox.square(
), dimension: 28),
const SizedBox(height: 20), ),
Text(context.l10n.search_to_get_results).large(), onAcceptSuggestion: (index) {
], controller.text = KVStoreService
.recentSearches[index];
ref
.read(searchTermStateProvider
.notifier)
.state =
KVStoreService
.recentSearches[index];
},
onChanged: (value) {},
onSubmitted: onSubmitted,
),
);
}),
), ),
(false, true) => Container( ),
constraints: BoxConstraints( ],
maxWidth: mediaQuery.lgAndUp ),
? mediaQuery.width * 0.5 Expanded(
: mediaQuery.width, child: AnimatedSwitcher(
), duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.symmetric( child: switch ((searchTerm.isEmpty, isFetching)) {
horizontal: 20, (true, false) => Column(
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text( SizedBox(
context.l10n.crunching_results, height: mediaQuery.height * 0.2,
style: TextStyle( ),
fontSize: 20, Undraw(
fontWeight: FontWeight.w900, illustration: UndrawIllustration.explore,
color: theme.colorScheme.foreground color: theme.colorScheme.primary,
.withOpacity(0.7), height: 200 * theme.scaling,
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
const LinearProgressIndicator(), Text(context.l10n.search_to_get_results)
.large(),
], ],
), ),
), (false, true) => Container(
_ => InterScrollbar( constraints: BoxConstraints(
controller: scrollController, maxWidth: mediaQuery.lgAndUp
child: SingleChildScrollView( ? mediaQuery.width * 0.5
: mediaQuery.width,
),
padding: const EdgeInsets.symmetric(
horizontal: 20,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
context.l10n.crunching_results,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
color: theme.colorScheme.foreground
.withOpacity(0.7),
),
),
const SizedBox(height: 20),
const LinearProgressIndicator(),
],
),
),
_ => InterScrollbar(
controller: scrollController, controller: scrollController,
child: const Padding( child: SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 8), controller: scrollController,
child: SafeArea( child: const Padding(
child: Column( padding: EdgeInsets.symmetric(vertical: 8),
crossAxisAlignment: child: SafeArea(
CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment:
SearchTracksSection(), CrossAxisAlignment.start,
SearchPlaylistsSection(), children: [
Gap(20), SearchTracksSection(),
SearchArtistsSection(), SearchPlaylistsSection(),
Gap(20), Gap(20),
SearchAlbumsSection(), SearchArtistsSection(),
], Gap(20),
SearchAlbumsSection(),
],
),
), ),
), ),
), ),
), ),
), },
}, ),
), ),
), ],
], ),
), ),
), ),
); );
} }

View File

@ -90,7 +90,7 @@ class SettingsAboutSection extends HookConsumerWidget {
title: Text(context.l10n.about_spotube), title: Text(context.l10n.about_spotube),
trailing: const Icon(SpotubeIcons.angleRight), trailing: const Icon(SpotubeIcons.angleRight),
onTap: () { onTap: () {
context.pushRoute(const AboutSpotubeRoute()); context.navigateTo(const AboutSpotubeRoute());
}, },
) )
], ],

View File

@ -49,7 +49,7 @@ class SettingsAccountSection extends HookConsumerWidget {
), ),
), ),
onTap: () { onTap: () {
context.pushRoute(ProfileRoute()); context.navigateTo(ProfileRoute());
}, },
), ),
if (auth.asData?.value == null) if (auth.asData?.value == null)
@ -112,7 +112,7 @@ class SettingsAccountSection extends HookConsumerWidget {
trailing: Button.secondary( trailing: Button.secondary(
leading: const Icon(SpotubeIcons.lastFm), leading: const Icon(SpotubeIcons.lastFm),
onPressed: () { onPressed: () {
context.pushRoute(const LastFMLoginRoute()); context.navigateTo(const LastFMLoginRoute());
}, },
child: Text(context.l10n.connect), child: Text(context.l10n.connect),
), ),

View File

@ -21,7 +21,7 @@ class SettingsDevelopersSection extends HookWidget {
title: Text(context.l10n.logs), title: Text(context.l10n.logs),
trailing: const Icon(SpotubeIcons.angleRight), trailing: const Icon(SpotubeIcons.angleRight),
onTap: () { onTap: () {
context.pushRoute(const LogsRoute()); context.navigateTo(const LogsRoute());
}, },
) )
], ],

View File

@ -268,7 +268,7 @@ class SettingsPlaybackSection extends HookConsumerWidget {
title: Text(context.l10n.blacklist), title: Text(context.l10n.blacklist),
subtitle: Text(context.l10n.blacklist_description), subtitle: Text(context.l10n.blacklist_description),
onTap: () { onTap: () {
context.pushRoute(const BlackListRoute()); context.navigateTo(const BlackListRoute());
}, },
trailing: const Icon(SpotubeIcons.angleRight), trailing: const Icon(SpotubeIcons.angleRight),
), ),

View File

@ -1,5 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/components/titlebar/titlebar.dart'; import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/modules/stats/summary/summary.dart'; import 'package:spotube/modules/stats/summary/summary.dart';
import 'package:spotube/modules/stats/top/top.dart'; import 'package:spotube/modules/stats/top/top.dart';
@ -14,23 +15,29 @@ class StatsPage extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
return SafeArea( return PopScope(
bottom: false, canPop: false,
child: Scaffold( onPopInvokedWithResult: (didPop, result) {
headers: [ context.navigateTo(const HomeRoute());
if (kTitlebarVisible) const TitleBar(), },
], child: SafeArea(
child: CustomScrollView( bottom: false,
slivers: [ child: Scaffold(
if (kIsMacOS) const SliverGap(20), headers: [
const StatsPageSummarySection(), if (kTitlebarVisible) const TitleBar(),
const StatsPageTopSection(),
const SliverToBoxAdapter(
child: SafeArea(
child: SizedBox(),
),
)
], ],
child: CustomScrollView(
slivers: [
if (kIsMacOS) const SliverGap(20),
const StatsPageSummarySection(),
const StatsPageTopSection(),
const SliverToBoxAdapter(
child: SafeArea(
child: SizedBox(),
),
)
],
),
), ),
), ),
); );