mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
Perfction on new Playlist & Album View
TrackCollectionView shows ShimmerTrackTile (loading) seperately
This commit is contained in:
parent
71d6fc5a4a
commit
41102b3bb8
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/Shared/HeartButton.dart';
|
import 'package:spotube/components/Shared/HeartButton.dart';
|
||||||
@ -59,6 +60,7 @@ class AlbumView extends HookConsumerWidget {
|
|||||||
titleImage: albumArt,
|
titleImage: albumArt,
|
||||||
tracksSnapshot: tracksSnapshot,
|
tracksSnapshot: tracksSnapshot,
|
||||||
album: album,
|
album: album,
|
||||||
|
routePath: "/album/${album.id}",
|
||||||
onPlay: ([track]) {
|
onPlay: ([track]) {
|
||||||
if (tracksSnapshot.asData?.value != null) {
|
if (tracksSnapshot.asData?.value != null) {
|
||||||
playPlaylist(
|
playPlaylist(
|
||||||
|
@ -23,6 +23,7 @@ import 'package:spotube/hooks/usePaginatedFutureProvider.dart';
|
|||||||
import 'package:spotube/hooks/useUpdateChecker.dart';
|
import 'package:spotube/hooks/useUpdateChecker.dart';
|
||||||
import 'package:spotube/models/Logger.dart';
|
import 'package:spotube/models/Logger.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/provider/SpotifyRequests.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
List<String> spotifyScopes = [
|
List<String> spotifyScopes = [
|
||||||
"playlist-modify-public",
|
"playlist-modify-public",
|
||||||
@ -73,7 +74,7 @@ class Home extends HookConsumerWidget {
|
|||||||
child: MoveWindow(),
|
child: MoveWindow(),
|
||||||
),
|
),
|
||||||
Expanded(child: MoveWindow()),
|
Expanded(child: MoveWindow()),
|
||||||
if (!Platform.isMacOS && !Platform.isAndroid && !Platform.isIOS)
|
if (!Platform.isMacOS && !kIsMobile)
|
||||||
const TitleBarActionButtons(),
|
const TitleBarActionButtons(),
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
@ -98,7 +99,7 @@ class Home extends HookConsumerWidget {
|
|||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Platform.isAndroid || Platform.isIOS
|
kIsMobile
|
||||||
? titleBarContents
|
? titleBarContents
|
||||||
: WindowTitleBarBox(child: titleBarContents),
|
: WindowTitleBarBox(child: titleBarContents),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -44,7 +44,7 @@ class ShimmerArtistProfile extends HookWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
const Flexible(child: ShimmerTrackTile()),
|
const Flexible(child: ShimmerTrackTile(noSliver: true)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,12 @@ import 'package:skeleton_text/skeleton_text.dart';
|
|||||||
import 'package:spotube/extensions/ShimmerColorTheme.dart';
|
import 'package:spotube/extensions/ShimmerColorTheme.dart';
|
||||||
|
|
||||||
class ShimmerTrackTile extends StatelessWidget {
|
class ShimmerTrackTile extends StatelessWidget {
|
||||||
const ShimmerTrackTile({Key? key}) : super(key: key);
|
final bool noSliver;
|
||||||
|
|
||||||
|
const ShimmerTrackTile({
|
||||||
|
Key? key,
|
||||||
|
this.noSliver = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -13,15 +18,7 @@ class ShimmerTrackTile extends StatelessWidget {
|
|||||||
.extension<ShimmerColorTheme>()!
|
.extension<ShimmerColorTheme>()!
|
||||||
.shimmerBackgroundColor!;
|
.shimmerBackgroundColor!;
|
||||||
|
|
||||||
return Padding(
|
final single = Container(
|
||||||
padding: const EdgeInsets.only(top: 30),
|
|
||||||
child: ListView.builder(
|
|
||||||
scrollDirection: Axis.vertical,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
itemCount: 5,
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@ -78,7 +75,20 @@ class ShimmerTrackTile extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
|
||||||
|
if (noSliver) {
|
||||||
|
return ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: 5,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, _) => single,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) => single,
|
||||||
|
childCount: 5,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
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/services.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@ -12,6 +11,7 @@ import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
|
|||||||
import 'package:spotube/helpers/artists-to-clickable-artists.dart';
|
import 'package:spotube/helpers/artists-to-clickable-artists.dart';
|
||||||
import 'package:spotube/helpers/image-to-url-string.dart';
|
import 'package:spotube/helpers/image-to-url-string.dart';
|
||||||
import 'package:spotube/hooks/useBreakpoints.dart';
|
import 'package:spotube/hooks/useBreakpoints.dart';
|
||||||
|
import 'package:spotube/hooks/useCustomStatusBarColor.dart';
|
||||||
import 'package:spotube/hooks/usePaletteColor.dart';
|
import 'package:spotube/hooks/usePaletteColor.dart';
|
||||||
import 'package:spotube/provider/Playback.dart';
|
import 'package:spotube/provider/Playback.dart';
|
||||||
|
|
||||||
@ -46,29 +46,10 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
|
|
||||||
final PaletteColor paletteColor = usePaletteColor(context, albumArt, ref);
|
final PaletteColor paletteColor = usePaletteColor(context, albumArt, ref);
|
||||||
|
|
||||||
final backgroundColor = Theme.of(context).backgroundColor;
|
useCustomStatusBarColor(
|
||||||
|
paletteColor.color,
|
||||||
useEffect(() {
|
GoRouter.of(context).location == "/player",
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
|
||||||
SystemUiOverlayStyle(
|
|
||||||
statusBarColor: paletteColor.color, // status bar color
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}, [paletteColor.color]);
|
|
||||||
|
|
||||||
useEffect(() {
|
|
||||||
return () {
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
|
||||||
SystemUiOverlayStyle(
|
|
||||||
statusBarColor: backgroundColor, // status bar color
|
|
||||||
statusBarIconBrightness: backgroundColor.computeLuminance() > 0.179
|
|
||||||
? Brightness.dark
|
|
||||||
: Brightness.light,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/components/Shared/HeartButton.dart';
|
import 'package:spotube/components/Shared/HeartButton.dart';
|
||||||
import 'package:spotube/components/Shared/TrackCollectionView.dart';
|
import 'package:spotube/components/Shared/TrackCollectionView.dart';
|
||||||
@ -80,6 +81,7 @@ class PlaylistView extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
showShare: playlist.id != "user-liked-tracks",
|
showShare: playlist.id != "user-liked-tracks",
|
||||||
|
routePath: "/playlist/${playlist.id}",
|
||||||
onShare: () {
|
onShare: () {
|
||||||
final data = "https://open.spotify.com/playlist/${playlist.id}";
|
final data = "https://open.spotify.com/playlist/${playlist.id}";
|
||||||
Clipboard.setData(
|
Clipboard.setData(
|
||||||
|
@ -13,6 +13,7 @@ import 'package:spotube/models/SpotifyMarkets.dart';
|
|||||||
import 'package:spotube/models/SpotubeTrack.dart';
|
import 'package:spotube/models/SpotubeTrack.dart';
|
||||||
import 'package:spotube/provider/Auth.dart';
|
import 'package:spotube/provider/Auth.dart';
|
||||||
import 'package:spotube/provider/UserPreferences.dart';
|
import 'package:spotube/provider/UserPreferences.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
class Settings extends HookConsumerWidget {
|
class Settings extends HookConsumerWidget {
|
||||||
@ -56,7 +57,7 @@ class Settings extends HookConsumerWidget {
|
|||||||
constraints: const BoxConstraints(maxWidth: 1366),
|
constraints: const BoxConstraints(maxWidth: 1366),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
if (!Platform.isAndroid && !Platform.isIOS) ...[
|
if (!kIsMobile) ...[
|
||||||
SettingsHotKeyTile(
|
SettingsHotKeyTile(
|
||||||
title: "Next track global shortcut",
|
title: "Next track global shortcut",
|
||||||
currentHotKey: preferences.nextTrackHotKey,
|
currentHotKey: preferences.nextTrackHotKey,
|
||||||
|
@ -9,6 +9,7 @@ import 'package:spotube/helpers/getLyrics.dart';
|
|||||||
import 'package:spotube/models/SpotubeTrack.dart';
|
import 'package:spotube/models/SpotubeTrack.dart';
|
||||||
import 'package:spotube/provider/Playback.dart';
|
import 'package:spotube/provider/Playback.dart';
|
||||||
import 'package:spotube/provider/UserPreferences.dart';
|
import 'package:spotube/provider/UserPreferences.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
||||||
import 'package:path_provider/path_provider.dart' as path_provider;
|
import 'package:path_provider/path_provider.dart' as path_provider;
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
@ -30,7 +31,7 @@ class DownloadTrackButton extends HookConsumerWidget {
|
|||||||
|
|
||||||
final _downloadTrack = useCallback(() async {
|
final _downloadTrack = useCallback(() async {
|
||||||
if (track == null) return;
|
if (track == null) return;
|
||||||
if ((Platform.isAndroid || Platform.isIOS) &&
|
if ((kIsMobile) &&
|
||||||
!await Permission.storage.isGranted &&
|
!await Permission.storage.isGranted &&
|
||||||
!await Permission.storage.isPermanentlyDenied) {
|
!await Permission.storage.isPermanentlyDenied) {
|
||||||
final status = await Permission.storage.request();
|
final status = await Permission.storage.request();
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
class TitleBarActionButtons extends StatelessWidget {
|
class TitleBarActionButtons extends StatelessWidget {
|
||||||
final Color? color;
|
final Color? color;
|
||||||
@ -67,20 +68,23 @@ class PageWindowTitleBar extends StatelessWidget
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
Size get preferredSize => Size.fromHeight(
|
Size get preferredSize => Size.fromHeight(
|
||||||
!Platform.isIOS && !Platform.isAndroid ? appWindow.titleBarHeight : 35,
|
(kIsDesktop ? appWindow.titleBarHeight : 35),
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (Platform.isIOS || Platform.isAndroid) {
|
if (kIsMobile) {
|
||||||
return PreferredSize(
|
return PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(300),
|
preferredSize: const Size.fromHeight(300),
|
||||||
|
child: Container(
|
||||||
|
color: backgroundColor,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (leading != null) leading!,
|
if (leading != null) leading!,
|
||||||
Expanded(child: Center(child: center)),
|
Expanded(child: Center(child: center)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return WindowTitleBarBox(
|
return WindowTitleBarBox(
|
||||||
@ -94,7 +98,7 @@ class PageWindowTitleBar extends StatelessWidget
|
|||||||
),
|
),
|
||||||
if (leading != null) leading!,
|
if (leading != null) leading!,
|
||||||
Expanded(child: MoveWindow(child: Center(child: center))),
|
Expanded(child: MoveWindow(child: Center(child: center))),
|
||||||
if (!Platform.isMacOS && !Platform.isIOS && !Platform.isAndroid)
|
if (!Platform.isMacOS && !kIsMobile)
|
||||||
TitleBarActionButtons(color: foregroundColor)
|
TitleBarActionButtons(color: foregroundColor)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/components/LoaderShimmers/ShimmerTrackTile.dart';
|
import 'package:spotube/components/LoaderShimmers/ShimmerTrackTile.dart';
|
||||||
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
||||||
import 'package:spotube/components/Shared/TracksTableView.dart';
|
import 'package:spotube/components/Shared/TracksTableView.dart';
|
||||||
import 'package:spotube/helpers/simple-track-to-track.dart';
|
import 'package:spotube/helpers/simple-track-to-track.dart';
|
||||||
|
import 'package:spotube/hooks/useCustomStatusBarColor.dart';
|
||||||
import 'package:spotube/hooks/usePaletteColor.dart';
|
import 'package:spotube/hooks/usePaletteColor.dart';
|
||||||
import 'package:spotube/models/Logger.dart';
|
import 'package:spotube/models/Logger.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
class TrackCollectionView extends HookConsumerWidget {
|
class TrackCollectionView extends HookConsumerWidget {
|
||||||
final logger = getLogger(TrackCollectionView);
|
final logger = getLogger(TrackCollectionView);
|
||||||
@ -25,6 +28,8 @@ class TrackCollectionView extends HookConsumerWidget {
|
|||||||
|
|
||||||
final bool showShare;
|
final bool showShare;
|
||||||
final bool isOwned;
|
final bool isOwned;
|
||||||
|
|
||||||
|
final String routePath;
|
||||||
TrackCollectionView({
|
TrackCollectionView({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.id,
|
required this.id,
|
||||||
@ -33,6 +38,7 @@ class TrackCollectionView extends HookConsumerWidget {
|
|||||||
required this.isPlaying,
|
required this.isPlaying,
|
||||||
required this.onPlay,
|
required this.onPlay,
|
||||||
required this.onShare,
|
required this.onShare,
|
||||||
|
required this.routePath,
|
||||||
this.heartBtn,
|
this.heartBtn,
|
||||||
this.album,
|
this.album,
|
||||||
this.description,
|
this.description,
|
||||||
@ -83,6 +89,11 @@ class TrackCollectionView extends HookConsumerWidget {
|
|||||||
|
|
||||||
final collapsed = useState(false);
|
final collapsed = useState(false);
|
||||||
|
|
||||||
|
useCustomStatusBarColor(
|
||||||
|
color?.color ?? Theme.of(context).backgroundColor,
|
||||||
|
GoRouter.of(context).location == routePath,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
listener() {
|
listener() {
|
||||||
if (controller.position.pixels >= 400 && !collapsed.value) {
|
if (controller.position.pixels >= 400 && !collapsed.value) {
|
||||||
@ -99,25 +110,16 @@ class TrackCollectionView extends HookConsumerWidget {
|
|||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: (kIsDesktop)
|
||||||
backgroundColor:
|
? PageWindowTitleBar(
|
||||||
tracksSnapshot.asData?.value != null ? color?.color : null,
|
backgroundColor: color?.color,
|
||||||
foregroundColor: tracksSnapshot.asData?.value != null
|
foregroundColor: color?.titleTextColor,
|
||||||
? color?.titleTextColor
|
|
||||||
: null,
|
|
||||||
leading: Row(
|
leading: Row(
|
||||||
children: [
|
children: [BackButton(color: color?.titleTextColor)],
|
||||||
BackButton(
|
),
|
||||||
color: tracksSnapshot.asData?.value != null
|
|
||||||
? color?.titleTextColor
|
|
||||||
: null,
|
|
||||||
)
|
)
|
||||||
],
|
: null,
|
||||||
),
|
body: CustomScrollView(
|
||||||
),
|
|
||||||
body: tracksSnapshot.when(
|
|
||||||
data: (tracks) {
|
|
||||||
return CustomScrollView(
|
|
||||||
controller: controller,
|
controller: controller,
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
@ -125,19 +127,19 @@ class TrackCollectionView extends HookConsumerWidget {
|
|||||||
floating: false,
|
floating: false,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
expandedHeight: 400,
|
expandedHeight: 400,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: kIsMobile,
|
||||||
|
iconTheme: IconThemeData(color: color?.titleTextColor),
|
||||||
primary: true,
|
primary: true,
|
||||||
|
backgroundColor: color?.color,
|
||||||
title: collapsed.value
|
title: collapsed.value
|
||||||
? Text(
|
? Text(
|
||||||
title,
|
title,
|
||||||
style:
|
style: Theme.of(context).textTheme.headline4?.copyWith(
|
||||||
Theme.of(context).textTheme.headline4?.copyWith(
|
|
||||||
color: color?.titleTextColor,
|
color: color?.titleTextColor,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
backgroundColor: color?.color.withOpacity(0.8),
|
|
||||||
flexibleSpace: LayoutBuilder(builder: (context, constrains) {
|
flexibleSpace: LayoutBuilder(builder: (context, constrains) {
|
||||||
return FlexibleSpaceBar(
|
return FlexibleSpaceBar(
|
||||||
background: Container(
|
background: Container(
|
||||||
@ -218,7 +220,9 @@ class TrackCollectionView extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
TracksTableView(
|
tracksSnapshot.when(
|
||||||
|
data: (tracks) {
|
||||||
|
return TracksTableView(
|
||||||
tracks is! List<Track>
|
tracks is! List<Track>
|
||||||
? tracks
|
? tracks
|
||||||
.map((track) => simpleTrackToTrack(track, album!))
|
.map((track) => simpleTrackToTrack(track, album!))
|
||||||
@ -227,14 +231,14 @@ class TrackCollectionView extends HookConsumerWidget {
|
|||||||
onTrackPlayButtonPressed: onPlay,
|
onTrackPlayButtonPressed: onPlay,
|
||||||
playlistId: id,
|
playlistId: id,
|
||||||
userPlaylist: isOwned,
|
userPlaylist: isOwned,
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
error: (error, _) => Text("Error $error"),
|
error: (error, _) =>
|
||||||
|
SliverToBoxAdapter(child: Text("Error $error")),
|
||||||
loading: () => const ShimmerTrackTile(),
|
loading: () => const ShimmerTrackTile(),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,77 +97,5 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
}).toList()
|
}).toList()
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
return Container(
|
|
||||||
color: Theme.of(context).backgroundColor,
|
|
||||||
child: Scrollbar(
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
if (heading != null) heading!,
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"#",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: tableHeadStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Title",
|
|
||||||
style: tableHeadStyle,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// used alignment of this table-head
|
|
||||||
if (breakpoint.isMoreThan(Breakpoints.md)) ...[
|
|
||||||
const SizedBox(width: 100),
|
|
||||||
Expanded(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Album",
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: tableHeadStyle,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
if (!breakpoint.isSm) ...[
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text("Time", style: tableHeadStyle),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
|
||||||
const SizedBox(width: 40),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
...tracks.asMap().entries.map((track) {
|
|
||||||
String? thumbnailUrl = imageToUrlString(
|
|
||||||
track.value.album?.images,
|
|
||||||
index: (track.value.album?.images?.length ?? 1) - 1,
|
|
||||||
);
|
|
||||||
String duration =
|
|
||||||
"${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}";
|
|
||||||
return TrackTile(
|
|
||||||
playback,
|
|
||||||
playlistId: playlistId,
|
|
||||||
track: track,
|
|
||||||
duration: duration,
|
|
||||||
thumbnailUrl: thumbnailUrl,
|
|
||||||
userPlaylist: userPlaylist,
|
|
||||||
onTrackPlayButtonPressed: onTrackPlayButtonPressed,
|
|
||||||
);
|
|
||||||
}).toList()
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
lib/hooks/useCustomStatusBarColor.dart
Normal file
38
lib/hooks/useCustomStatusBarColor.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
|
void useCustomStatusBarColor(Color color, bool isCurrentRoute) {
|
||||||
|
final context = useContext();
|
||||||
|
final backgroundColor = Theme.of(context).backgroundColor;
|
||||||
|
resetStatusbar() => SystemChrome.setSystemUIOverlayStyle(
|
||||||
|
SystemUiOverlayStyle(
|
||||||
|
statusBarColor: backgroundColor, // status bar color
|
||||||
|
statusBarIconBrightness: backgroundColor.computeLuminance() > 0.179
|
||||||
|
? Brightness.dark
|
||||||
|
: Brightness.light,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final statusBarColor = SystemChrome.latestStyle?.statusBarColor;
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
if (isCurrentRoute && statusBarColor != color) {
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
|
SystemUiOverlayStyle(
|
||||||
|
statusBarColor: color, // status bar color
|
||||||
|
statusBarIconBrightness: color.computeLuminance() > 0.179
|
||||||
|
? Brightness.dark
|
||||||
|
: Brightness.light,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (!isCurrentRoute && statusBarColor == color) {
|
||||||
|
resetStatusbar();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}, [color, isCurrentRoute, statusBarColor]);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
return resetStatusbar;
|
||||||
|
}, []);
|
||||||
|
}
|
@ -7,6 +7,7 @@ import 'package:spotube/hooks/playback.dart';
|
|||||||
import 'package:spotube/models/GlobalKeyActions.dart';
|
import 'package:spotube/models/GlobalKeyActions.dart';
|
||||||
import 'package:spotube/provider/Playback.dart';
|
import 'package:spotube/provider/Playback.dart';
|
||||||
import 'package:spotube/provider/UserPreferences.dart';
|
import 'package:spotube/provider/UserPreferences.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
useHotKeys(WidgetRef ref) {
|
useHotKeys(WidgetRef ref) {
|
||||||
final playback = ref.watch(playbackProvider);
|
final playback = ref.watch(playbackProvider);
|
||||||
@ -20,7 +21,7 @@ useHotKeys(WidgetRef ref) {
|
|||||||
final _playOrPause = useTogglePlayPause(playback);
|
final _playOrPause = useTogglePlayPause(playback);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (Platform.isIOS || Platform.isAndroid) return null;
|
if (kIsMobile) return null;
|
||||||
_hotKeys = [
|
_hotKeys = [
|
||||||
GlobalKeyActions(
|
GlobalKeyActions(
|
||||||
HotKey(KeyCode.space, scope: HotKeyScope.inapp),
|
HotKey(KeyCode.space, scope: HotKeyScope.inapp),
|
||||||
|
@ -16,13 +16,9 @@ import 'package:spotube/provider/YouTube.dart';
|
|||||||
import 'package:spotube/themes/dark-theme.dart';
|
import 'package:spotube/themes/dark-theme.dart';
|
||||||
import 'package:spotube/themes/light-theme.dart';
|
import 'package:spotube/themes/light-theme.dart';
|
||||||
import 'package:spotube/utils/AudioPlayerHandler.dart';
|
import 'package:spotube/utils/AudioPlayerHandler.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
// await JustAudioBackground.init(
|
|
||||||
// androidNotificationChannelId: 'oss.krtirtho.Spotube',
|
|
||||||
// androidNotificationChannelName: 'Spotube',
|
|
||||||
// androidNotificationOngoing: true,
|
|
||||||
// );
|
|
||||||
AudioPlayerHandler audioPlayerHandler = await AudioService.init(
|
AudioPlayerHandler audioPlayerHandler = await AudioService.init(
|
||||||
builder: () => AudioPlayerHandler(),
|
builder: () => AudioPlayerHandler(),
|
||||||
config: const AudioServiceConfig(
|
config: const AudioServiceConfig(
|
||||||
@ -31,12 +27,11 @@ void main() async {
|
|||||||
androidNotificationOngoing: true,
|
androidNotificationOngoing: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (!Platform.isAndroid && !Platform.isIOS) {
|
if (kIsDesktop) {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await hotKeyManager.unregisterAll();
|
await hotKeyManager.unregisterAll();
|
||||||
doWhenWindowReady(() {
|
doWhenWindowReady(() {
|
||||||
appWindow.minSize =
|
appWindow.minSize = const Size(359, 700);
|
||||||
Size(Platform.isAndroid || Platform.isIOS ? 280 : 359, 700);
|
|
||||||
appWindow.alignment = Alignment.center;
|
appWindow.alignment = Alignment.center;
|
||||||
appWindow.title = "Spotube";
|
appWindow.title = "Spotube";
|
||||||
appWindow.maximize();
|
appWindow.maximize();
|
||||||
@ -75,6 +70,7 @@ class Spotube extends HookConsumerWidget {
|
|||||||
.watch(userPreferencesProvider.select((s) => s.backgroundColorScheme));
|
.watch(userPreferencesProvider.select((s) => s.backgroundColorScheme));
|
||||||
final player = ref.watch(audioPlayerProvider);
|
final player = ref.watch(audioPlayerProvider);
|
||||||
final youtube = ref.watch(youtubeProvider);
|
final youtube = ref.watch(youtubeProvider);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
return () {
|
return () {
|
||||||
player.dispose();
|
player.dispose();
|
||||||
|
@ -67,8 +67,8 @@ GoRouter createGoRouter() => GoRouter(
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/player",
|
path: "/player",
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return SpotubePage(
|
return const SpotubePage(
|
||||||
child: const PlayerView(),
|
child: PlayerView(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -72,7 +72,9 @@ class Auth extends PersistedChangeNotifier {
|
|||||||
_clientSecret = map["clientSecret"];
|
_clientSecret = map["clientSecret"];
|
||||||
_accessToken = map["accessToken"];
|
_accessToken = map["accessToken"];
|
||||||
_refreshToken = map["refreshToken"];
|
_refreshToken = map["refreshToken"];
|
||||||
_expiration = DateTime.tryParse(map["expiration"]);
|
_expiration = map["expiration"] != null
|
||||||
|
? DateTime.tryParse(map["expiration"])
|
||||||
|
: _expiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
5
lib/utils/platform.dart
Normal file
5
lib/utils/platform.dart
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
final kIsDesktop = Platform.isLinux || Platform.isWindows || Platform.isMacOS;
|
||||||
|
|
||||||
|
final kIsMobile = Platform.isAndroid || Platform.isIOS;
|
Loading…
Reference in New Issue
Block a user