diff --git a/lib/components/Home/Home.dart b/lib/components/Home/Home.dart index 53bb1bb4..09ed26f9 100644 --- a/lib/components/Home/Home.dart +++ b/lib/components/Home/Home.dart @@ -94,82 +94,79 @@ class Home extends HookConsumerWidget { return null; }, [backgroundColor]); - return SafeArea( - child: Scaffold( - body: Column( - children: [ - if (_selectedIndex.value != 3) - kIsMobile - ? titleBarContents - : WindowTitleBarBox(child: titleBarContents), - Expanded( - child: Row( - children: [ - Sidebar( - selectedIndex: _selectedIndex.value, - onSelectedIndexChanged: _onSelectedIndexChanged, - ), - // contents of the spotify - if (_selectedIndex.value == 0) - Expanded( - child: Padding( - padding: const EdgeInsets.only( - bottom: 8.0, - top: 8.0, - left: 8.0, - ), - child: HookBuilder(builder: (context) { - final pagingController = usePaginatedFutureProvider< - Page, int, Category>( - (pageKey) => categoriesQuery(pageKey), - ref: ref, - firstPageKey: 0, - onData: (categories, pagingController, pageKey) { - final items = categories.items?.toList(); - if (pageKey == 0) { - Category category = Category(); - category.id = "user-featured-playlists"; - category.name = "Featured"; - items?.insert(0, category); - } - if (categories.isLast && items != null) { - pagingController.appendLastPage(items); - } else if (categories.items != null) { - pagingController.appendPage( - items!, categories.nextOffset); - } - }, - ); - return PagedListView( - pagingController: pagingController, - builderDelegate: - PagedChildBuilderDelegate( - firstPageProgressIndicatorBuilder: (_) => - const ShimmerCategories(), - newPageProgressIndicatorBuilder: (_) => - const ShimmerCategories(), - itemBuilder: (context, item, index) { - return CategoryCard(item); - }, - ), - ); - }), + return Scaffold( + body: Column( + children: [ + if (_selectedIndex.value != 3) + kIsMobile + ? titleBarContents + : WindowTitleBarBox(child: titleBarContents), + Expanded( + child: Row( + children: [ + Sidebar( + selectedIndex: _selectedIndex.value, + onSelectedIndexChanged: _onSelectedIndexChanged, + ), + // contents of the spotify + if (_selectedIndex.value == 0) + Expanded( + child: Padding( + padding: const EdgeInsets.only( + bottom: 8.0, + top: 8.0, + left: 8.0, ), + child: HookBuilder(builder: (context) { + final pagingController = usePaginatedFutureProvider< + Page, int, Category>( + (pageKey) => categoriesQuery(pageKey), + ref: ref, + firstPageKey: 0, + onData: (categories, pagingController, pageKey) { + final items = categories.items?.toList(); + if (pageKey == 0) { + Category category = Category(); + category.id = "user-featured-playlists"; + category.name = "Featured"; + items?.insert(0, category); + } + if (categories.isLast && items != null) { + pagingController.appendLastPage(items); + } else if (categories.items != null) { + pagingController.appendPage( + items!, categories.nextOffset); + } + }, + ); + return PagedListView( + pagingController: pagingController, + builderDelegate: PagedChildBuilderDelegate( + firstPageProgressIndicatorBuilder: (_) => + const ShimmerCategories(), + newPageProgressIndicatorBuilder: (_) => + const ShimmerCategories(), + itemBuilder: (context, item, index) { + return CategoryCard(item); + }, + ), + ); + }), ), - if (_selectedIndex.value == 1) const Search(), - if (_selectedIndex.value == 2) const UserLibrary(), - if (_selectedIndex.value == 3) const SyncedLyrics(), - ], - ), + ), + if (_selectedIndex.value == 1) const Search(), + if (_selectedIndex.value == 2) const UserLibrary(), + if (_selectedIndex.value == 3) const SyncedLyrics(), + ], ), - // player itself - Player(), - SpotubeNavigationBar( - selectedIndex: _selectedIndex.value, - onSelectedIndexChanged: _onSelectedIndexChanged, - ), - ], - ), + ), + // player itself + Player(), + SpotubeNavigationBar( + selectedIndex: _selectedIndex.value, + onSelectedIndexChanged: _onSelectedIndexChanged, + ), + ], ), ); } diff --git a/lib/components/Library/UserLibrary.dart b/lib/components/Library/UserLibrary.dart index 3db22ab4..0132e8f2 100644 --- a/lib/components/Library/UserLibrary.dart +++ b/lib/components/Library/UserLibrary.dart @@ -15,24 +15,27 @@ class UserLibrary extends ConsumerWidget { return Expanded( child: DefaultTabController( length: 3, - child: Scaffold( - appBar: TabBar( - indicator: const BoxDecoration(color: Colors.transparent), - labelColor: Theme.of(context).primaryColor, - unselectedLabelColor: Theme.of(context).textTheme.bodyText1?.color, - tabs: const [ - Tab(text: "Playlist"), - Tab(text: "Artists"), - Tab(text: "Album"), - ], + child: SafeArea( + child: Scaffold( + appBar: TabBar( + indicator: const BoxDecoration(color: Colors.transparent), + labelColor: Theme.of(context).primaryColor, + unselectedLabelColor: + Theme.of(context).textTheme.bodyText1?.color, + tabs: const [ + Tab(text: "Playlist"), + Tab(text: "Artists"), + Tab(text: "Album"), + ], + ), + body: auth.isLoggedIn + ? TabBarView(children: [ + const UserPlaylists(), + UserArtists(), + const UserAlbums(), + ]) + : const AnonymousFallback(), ), - body: auth.isLoggedIn - ? TabBarView(children: [ - const UserPlaylists(), - UserArtists(), - const UserAlbums(), - ]) - : const AnonymousFallback(), ), ), ); diff --git a/lib/components/Lyrics/Lyrics.dart b/lib/components/Lyrics/Lyrics.dart index 967fc7be..9ea4b853 100644 --- a/lib/components/Lyrics/Lyrics.dart +++ b/lib/components/Lyrics/Lyrics.dart @@ -52,7 +52,7 @@ class Lyrics extends HookConsumerWidget { return Text( lyrics == null && playback.track == null ? "No Track being played currently" - : lyrics!, + : lyrics ?? "", style: textTheme.headline6 ?.copyWith(color: textTheme.headline1?.color), ); diff --git a/lib/components/Lyrics/SyncedLyrics.dart b/lib/components/Lyrics/SyncedLyrics.dart index 6b1e9a55..3ecfb3d5 100644 --- a/lib/components/Lyrics/SyncedLyrics.dart +++ b/lib/components/Lyrics/SyncedLyrics.dart @@ -14,6 +14,7 @@ import 'package:spotube/helpers/artist-to-string.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/hooks/useAutoScrollController.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; +import 'package:spotube/hooks/useCustomStatusBarColor.dart'; import 'package:spotube/hooks/usePaletteColor.dart'; import 'package:spotube/hooks/useSyncedLyrics.dart'; import 'package:spotube/provider/Playback.dart'; @@ -110,6 +111,12 @@ class SyncedLyrics extends HookConsumerWidget { : textTheme.headline4?.copyWith(fontSize: 25)) ?.copyWith(color: palette.titleTextColor); + useCustomStatusBarColor( + palette.color, + true, + noSetBGColor: true, + ); + return Expanded( child: Container( clipBehavior: Clip.hardEdge, @@ -126,83 +133,85 @@ class SyncedLyrics extends HookConsumerWidget { filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), child: Container( color: palette.color.withOpacity(.7), - child: failed.value - ? Lyrics(titleBarForegroundColor: palette.bodyTextColor) - : Column( - children: [ - PageWindowTitleBar( - foregroundColor: palette.bodyTextColor, - ), - Center( - child: SizedBox( - height: breakpoint >= Breakpoints.md ? 50 : 30, - child: playback.track?.name != null && - playback.track!.name!.length > 29 - ? SpotubeMarqueeText( - text: playback.track?.name ?? "Not Playing", - style: headlineTextStyle, - ) - : Text( - playback.track?.name ?? "Not Playing", - style: headlineTextStyle, - ), - )), - Center( - child: Text( - artistsToString( - playback.track?.artists ?? []), - style: breakpoint >= Breakpoints.md - ? textTheme.headline5 - : textTheme.headline6, + child: SafeArea( + child: failed.value + ? Lyrics(titleBarForegroundColor: palette.bodyTextColor) + : Column( + children: [ + PageWindowTitleBar( + foregroundColor: palette.bodyTextColor, ), - ), - if (lyricValue != null && lyricValue.lyrics.isNotEmpty) - Expanded( - child: ListView.builder( - controller: controller, - itemCount: lyricValue.lyrics.length, - itemBuilder: (context, index) { - final lyricSlice = lyricValue.lyrics[index]; - final isActive = - lyricSlice.time.inSeconds == currentTime; - if (isActive) { - controller.scrollToIndex( - index, - preferPosition: AutoScrollPosition.middle, - ); - } - return AutoScrollTag( - key: ValueKey(index), - index: index, - controller: controller, - child: Center( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - lyricSlice.text, - style: TextStyle( - // indicating the active state of that lyric slice - color: isActive - ? Theme.of(context).primaryColor - : palette.bodyTextColor, - fontWeight: - isActive ? FontWeight.bold : null, - fontSize: 30, - ), - textAlign: TextAlign.center, - ), - ), + Center( + child: SizedBox( + height: breakpoint >= Breakpoints.md ? 50 : 30, + child: playback.track?.name != null && + playback.track!.name!.length > 29 + ? SpotubeMarqueeText( + text: playback.track?.name ?? "Not Playing", + style: headlineTextStyle, + ) + : Text( + playback.track?.name ?? "Not Playing", + style: headlineTextStyle, ), - ); - }, + )), + Center( + child: Text( + artistsToString( + playback.track?.artists ?? []), + style: breakpoint >= Breakpoints.md + ? textTheme.headline5 + : textTheme.headline6, ), ), - if (playback.track != null && - (lyricValue == null || - lyricValue.lyrics.isEmpty == true)) - const Expanded(child: ShimmerLyrics()), - ], - ), + if (lyricValue != null && lyricValue.lyrics.isNotEmpty) + Expanded( + child: ListView.builder( + controller: controller, + itemCount: lyricValue.lyrics.length, + itemBuilder: (context, index) { + final lyricSlice = lyricValue.lyrics[index]; + final isActive = + lyricSlice.time.inSeconds == currentTime; + if (isActive) { + controller.scrollToIndex( + index, + preferPosition: AutoScrollPosition.middle, + ); + } + return AutoScrollTag( + key: ValueKey(index), + index: index, + controller: controller, + child: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + lyricSlice.text, + style: TextStyle( + // indicating the active state of that lyric slice + color: isActive + ? Theme.of(context).primaryColor + : palette.bodyTextColor, + fontWeight: + isActive ? FontWeight.bold : null, + fontSize: 30, + ), + textAlign: TextAlign.center, + ), + ), + ), + ); + }, + ), + ), + if (playback.track != null && + (lyricValue == null || + lyricValue.lyrics.isEmpty == true)) + const Expanded(child: ShimmerLyrics()), + ], + ), + ), ), ), ), diff --git a/lib/components/Player/PlayerView.dart b/lib/components/Player/PlayerView.dart index 586f039b..9754409b 100644 --- a/lib/components/Player/PlayerView.dart +++ b/lib/components/Player/PlayerView.dart @@ -120,13 +120,22 @@ class PlayerView extends HookConsumerWidget { )..repeat(); return RotationTransition( turns: Tween(begin: 0.0, end: 1.0).animate(controller), - child: CircleAvatar( - backgroundImage: CachedNetworkImageProvider( - albumArt, - cacheKey: albumArt, + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: paletteColor.titleTextColor, + width: 2, + ), + shape: BoxShape.circle, + ), + child: CircleAvatar( + backgroundImage: CachedNetworkImageProvider( + albumArt, + cacheKey: albumArt, + ), + radius: MediaQuery.of(context).size.width * + (breakpoint.isSm ? 0.4 : 0.3), ), - radius: MediaQuery.of(context).size.width * - (breakpoint.isSm ? 0.4 : 0.3), ), ); }), diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index e11507ed..6aac8872 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -37,183 +37,188 @@ class Search extends HookConsumerWidget { } final searchSnapshot = ref.watch(searchQuery(searchTerm)); - return Expanded( - child: Container( - color: Theme.of(context).backgroundColor, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Row( - children: [ - Expanded( - child: TextField( - controller: controller, - decoration: const InputDecoration(hintText: "Search..."), - onSubmitted: (value) { + return SafeArea( + child: Expanded( + child: Container( + color: Theme.of(context).backgroundColor, + child: Column( + children: [ + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Row( + children: [ + Expanded( + child: TextField( + controller: controller, + decoration: + const InputDecoration(hintText: "Search..."), + onSubmitted: (value) { + ref.read(searchTermStateProvider.notifier).state = + controller.value.text; + }, + ), + ), + const SizedBox(width: 5), + MaterialButton( + elevation: 3, + splashColor: Theme.of(context).primaryColor, + padding: const EdgeInsets.symmetric(vertical: 21), + color: Theme.of(context).primaryColor, + textColor: Colors.white, + child: const Icon(Icons.search_rounded), + onPressed: () { ref.read(searchTermStateProvider.notifier).state = controller.value.text; }, ), - ), - const SizedBox(width: 5), - MaterialButton( - elevation: 3, - splashColor: Theme.of(context).primaryColor, - padding: const EdgeInsets.symmetric(vertical: 21), - color: Theme.of(context).primaryColor, - textColor: Colors.white, - child: const Icon(Icons.search_rounded), - onPressed: () { - ref.read(searchTermStateProvider.notifier).state = - controller.value.text; - }, - ), - ], + ], + ), ), - ), - searchSnapshot.when( - data: (data) { - Playback playback = ref.watch(playbackProvider); - List albums = []; - List artists = []; - List tracks = []; - List playlists = []; - for (MapEntry page in data.asMap().entries) { - for (var item in page.value.items ?? []) { - if (item is AlbumSimple) { - albums.add(item); - } else if (item is PlaylistSimple) { - playlists.add(item); - } else if (item is Artist) { - artists.add(item); - } else if (item is Track) { - tracks.add(item); + searchSnapshot.when( + data: (data) { + Playback playback = ref.watch(playbackProvider); + List albums = []; + List artists = []; + List tracks = []; + List playlists = []; + for (MapEntry page in data.asMap().entries) { + for (var item in page.value.items ?? []) { + if (item is AlbumSimple) { + albums.add(item); + } else if (item is PlaylistSimple) { + playlists.add(item); + } else if (item is Artist) { + artists.add(item); + } else if (item is Track) { + tracks.add(item); + } } } - } - return Expanded( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (tracks.isNotEmpty) - Text( - "Songs", - style: Theme.of(context).textTheme.headline5, - ), - ...tracks.asMap().entries.map((track) { - String duration = - "${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; - return TrackTile( - playback, - track: track, - duration: duration, - thumbnailUrl: - imageToUrlString(track.value.album?.images), - isActive: playback.track?.id == track.value.id, - onTrackPlayButtonPressed: (currentTrack) async { - var isPlaylistPlaying = playback.playlist?.id != - null && - playback.playlist?.id == currentTrack.id; - if (!isPlaylistPlaying) { - playback.playPlaylist( - CurrentPlaylist( - tracks: [currentTrack], - id: currentTrack.id!, - name: currentTrack.name!, - thumbnail: imageToUrlString( - currentTrack.album?.images), - ), - ); - } else if (isPlaylistPlaying && - currentTrack.id != null && - currentTrack.id != playback.track?.id) { - playback.play(currentTrack); - } - }, - ); - }), - if (albums.isNotEmpty) - Text( - "Albums", - style: Theme.of(context).textTheme.headline5, - ), - const SizedBox(height: 10), - Scrollbar( - controller: albumController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: albumController, - child: Row( - children: albums.map((album) { - return AlbumCard(simpleAlbumToAlbum(album)); - }).toList(), + return Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (tracks.isNotEmpty) + Text( + "Songs", + style: Theme.of(context).textTheme.headline5, ), - ), - ), - const SizedBox(height: 20), - if (artists.isNotEmpty) - Text( - "Artists", - style: Theme.of(context).textTheme.headline5, - ), - const SizedBox(height: 10), - Scrollbar( - controller: artistController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: artistController, - child: Row( - children: artists - .map( - (artist) => Container( - margin: const EdgeInsets.symmetric( - horizontal: 15), - child: ArtistCard(artist), + ...tracks.asMap().entries.map((track) { + String duration = + "${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; + return TrackTile( + playback, + track: track, + duration: duration, + thumbnailUrl: + imageToUrlString(track.value.album?.images), + isActive: playback.track?.id == track.value.id, + onTrackPlayButtonPressed: (currentTrack) async { + var isPlaylistPlaying = + playback.playlist?.id != null && + playback.playlist?.id == + currentTrack.id; + if (!isPlaylistPlaying) { + playback.playPlaylist( + CurrentPlaylist( + tracks: [currentTrack], + id: currentTrack.id!, + name: currentTrack.name!, + thumbnail: imageToUrlString( + currentTrack.album?.images), ), - ) - .toList(), + ); + } else if (isPlaylistPlaying && + currentTrack.id != null && + currentTrack.id != playback.track?.id) { + playback.play(currentTrack); + } + }, + ); + }), + if (albums.isNotEmpty) + Text( + "Albums", + style: Theme.of(context).textTheme.headline5, + ), + const SizedBox(height: 10), + Scrollbar( + controller: albumController, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: albumController, + child: Row( + children: albums.map((album) { + return AlbumCard(simpleAlbumToAlbum(album)); + }).toList(), + ), ), ), - ), - const SizedBox(height: 20), - if (playlists.isNotEmpty) - Text( - "Playlists", - style: Theme.of(context).textTheme.headline5, + const SizedBox(height: 20), + if (artists.isNotEmpty) + Text( + "Artists", + style: Theme.of(context).textTheme.headline5, + ), + const SizedBox(height: 10), + Scrollbar( + controller: artistController, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: artistController, + child: Row( + children: artists + .map( + (artist) => Container( + margin: const EdgeInsets.symmetric( + horizontal: 15), + child: ArtistCard(artist), + ), + ) + .toList(), + ), + ), ), - const SizedBox(height: 10), - Scrollbar( - scrollbarOrientation: breakpoint > Breakpoints.md - ? ScrollbarOrientation.bottom - : ScrollbarOrientation.top, - controller: playlistController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, + const SizedBox(height: 20), + if (playlists.isNotEmpty) + Text( + "Playlists", + style: Theme.of(context).textTheme.headline5, + ), + const SizedBox(height: 10), + Scrollbar( + scrollbarOrientation: breakpoint > Breakpoints.md + ? ScrollbarOrientation.bottom + : ScrollbarOrientation.top, controller: playlistController, - child: Row( - children: playlists - .map( - (playlist) => PlaylistCard(playlist), - ) - .toList(), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: playlistController, + child: Row( + children: playlists + .map( + (playlist) => PlaylistCard(playlist), + ) + .toList(), + ), ), ), - ), - ], + ], + ), ), ), - ), - ); - }, - error: (error, __) => Text("Error $error"), - loading: () => const CircularProgressIndicator(), - ) - ], + ); + }, + error: (error, __) => Text("Error $error"), + loading: () => const CircularProgressIndicator(), + ) + ], + ), ), ), );