From 9eee573ce928aa6c03dcb50bf1521350d2de32cc Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 29 Oct 2022 14:23:17 +0600 Subject: [PATCH 01/23] feat: initial platform_ui integration What's changed: - Sidebar - Settings - UserLibrary (root) - Search (search field) --- lib/components/Home/Shell.dart | 18 +- lib/components/Home/Sidebar.dart | 276 +++++++++--------- lib/components/Library/UserDownloads.dart | 4 +- lib/components/Library/UserLibrary.dart | 47 +-- lib/components/Library/UserLocalTracks.dart | 21 +- .../LoaderShimmers/ShimmerArtistProfile.dart | 7 +- .../LoaderShimmers/ShimmerCategories.dart | 8 +- .../LoaderShimmers/ShimmerLyrics.dart | 8 +- .../LoaderShimmers/ShimmerPlaybuttonCard.dart | 8 +- .../LoaderShimmers/ShimmerTrackTile.dart | 8 +- lib/components/Lyrics/Lyrics.dart | 39 ++- .../Playlist/PlaylistCreateDialog.dart | 21 +- lib/components/Search/Search.dart | 19 +- lib/components/Settings/About.dart | 3 +- lib/components/Settings/Settings.dart | 126 ++++---- lib/components/Shared/AdaptiveListTile.dart | 3 +- lib/components/Shared/PlaybuttonCard.dart | 3 +- lib/components/Shared/SortTracksDropdown.dart | 71 +++-- lib/main.dart | 62 ++-- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 47 +++ pubspec.yaml | 2 + 22 files changed, 452 insertions(+), 351 deletions(-) diff --git a/lib/components/Home/Shell.dart b/lib/components/Home/Shell.dart index 8844b6c6..dc51c464 100644 --- a/lib/components/Home/Shell.dart +++ b/lib/components/Home/Shell.dart @@ -84,17 +84,13 @@ class Shell extends HookConsumerWidget { ) : null, extendBodyBehindAppBar: true, - body: Row( - children: [ - Sidebar( - selectedIndex: index.value, - onSelectedIndexChanged: (selectedIndex) { - index.value = selectedIndex; - GoRouter.of(context).go(_path[selectedIndex]!); - }, - ), - Expanded(child: child), - ], + body: Sidebar( + selectedIndex: index.value, + onSelectedIndexChanged: (i) { + index.value = i; + GoRouter.of(context).go(_path[index.value]!); + }, + child: child, ), extendBody: true, bottomNavigationBar: Column( diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart index 5903d44d..517c4039 100644 --- a/lib/components/Home/Sidebar.dart +++ b/lib/components/Home/Sidebar.dart @@ -5,6 +5,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:flutter/material.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; import 'package:spotube/models/sideBarTiles.dart'; @@ -15,16 +16,19 @@ import 'package:spotube/provider/SpotifyRequests.dart'; import 'package:spotube/provider/UserPreferences.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; +import 'package:fluent_ui/fluent_ui.dart' as FluentUI; final sidebarExtendedStateProvider = StateProvider((ref) => null); class Sidebar extends HookConsumerWidget { final int selectedIndex; final void Function(int) onSelectedIndexChanged; + final Widget child; const Sidebar({ required this.selectedIndex, required this.onSelectedIndexChanged, + required this.child, Key? key, }) : super(key: key); @@ -45,7 +49,6 @@ class Sidebar extends HookConsumerWidget { final breakpoints = useBreakpoints(); final extended = useState(false); - final auth = ref.watch(authProvider); final downloadCount = ref.watch( downloaderProvider.select((s) => s.currentlyRunning), ); @@ -81,10 +84,31 @@ class Sidebar extends HookConsumerWidget { return SafeArea( top: false, - child: Material( - color: Theme.of(context).navigationRailTheme.backgroundColor, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, + child: PlatformSidebar( + currentIndex: selectedIndex, + onIndexChanged: onSelectedIndexChanged, + body: Map.fromEntries( + sidebarTileList.map( + (e) { + final icon = Icon(e.icon); + return MapEntry( + PlatformSidebarItem( + icon: icon, + title: Text( + e.title, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + child, + ); + }, + ), + ), + expanded: extended.value, + header: Column( children: [ if (kIsDesktop) SizedBox( @@ -126,138 +150,120 @@ class Sidebar extends HookConsumerWidget { ], ) : _buildSmallLogo(), - Expanded( - child: NavigationRail( - destinations: sidebarTileList.map( - (e) { - final icon = Icon(e.icon); - return NavigationRailDestination( - icon: e.title == "Library" && downloadCount > 0 - ? Badge( - badgeColor: Colors.red[100]!, - badgeContent: Text( - downloadCount.toString(), - style: const TextStyle( - color: Colors.red, - fontWeight: FontWeight.bold, - ), - ), - animationType: BadgeAnimationType.fade, - child: icon, - ) - : icon, - label: Text( - e.title, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ); - }, - ).toList(), - selectedIndex: selectedIndex, - onDestinationSelected: onSelectedIndexChanged, - extended: extended.value, - ), - ), - SizedBox( - width: extended.value ? 256 : 80, - child: HookBuilder( - builder: (context) { - final me = useQuery( - job: currentUserQueryJob, - externalData: ref.watch(spotifyProvider), - ); - final data = me.data; - - final avatarImg = TypeConversionUtils.image_X_UrlString( - data?.images, - index: (data?.images?.length ?? 1) - 1, - placeholder: ImagePlaceholder.artist, - ); - - useEffect(() { - if (auth.isLoggedIn && !me.hasData) { - me.setExternalData(ref.read(spotifyProvider)); - me.refetch(); - } - return; - }, [auth.isLoggedIn, me.hasData]); - - if (extended.value) { - return Padding( - padding: const EdgeInsets.all(16).copyWith(left: 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (auth.isLoggedIn && data == null) - const Center( - child: CircularProgressIndicator(), - ) - else if (data != null) - Flexible( - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - CircleAvatar( - backgroundImage: - UniversalImage.imageProvider( - avatarImg), - onBackgroundImageError: - (exception, stackTrace) => - Image.asset( - "assets/user-placeholder.png", - height: 16, - width: 16, - ), - ), - const SizedBox( - width: 10, - ), - Flexible( - child: Text( - data.displayName ?? "Guest", - maxLines: 1, - softWrap: false, - overflow: TextOverflow.fade, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ), - ), - IconButton( - icon: const Icon(Icons.settings_outlined), - onPressed: () => goToSettings(context)), - ], - )); - } else { - return Padding( - padding: const EdgeInsets.all(8.0), - child: InkWell( - onTap: () => goToSettings(context), - child: CircleAvatar( - backgroundImage: - UniversalImage.imageProvider(avatarImg), - onBackgroundImageError: (exception, stackTrace) => - Image.asset( - "assets/user-placeholder.png", - height: 16, - width: 16, - ), - ), - ), - ); - } - }, - ), - ) ], ), + windowsFooterItems: [ + FluentUI.PaneItemAction( + icon: const FluentUI.Icon(FluentUI.FluentIcons.settings), + onTap: () => goToSettings(context), + ), + ], + footer: SidebarFooter(extended: extended.value), + ), + ); + } +} + +class SidebarFooter extends HookConsumerWidget { + final bool extended; + const SidebarFooter({ + Key? key, + required this.extended, + }) : super(key: key); + + @override + Widget build(BuildContext context, ref) { + final auth = ref.watch(authProvider); + + return SizedBox( + width: extended ? 256 : 80, + child: HookBuilder( + builder: (context) { + final me = useQuery( + job: currentUserQueryJob, + externalData: ref.watch(spotifyProvider), + ); + final data = me.data; + + final avatarImg = TypeConversionUtils.image_X_UrlString( + data?.images, + index: (data?.images?.length ?? 1) - 1, + placeholder: ImagePlaceholder.artist, + ); + + useEffect(() { + if (auth.isLoggedIn && !me.hasData) { + me.setExternalData(ref.read(spotifyProvider)); + me.refetch(); + } + return; + }, [auth.isLoggedIn, me.hasData]); + + if (extended) { + return Padding( + padding: const EdgeInsets.all(16).copyWith(left: 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (auth.isLoggedIn && data == null) + const Center( + child: CircularProgressIndicator(), + ) + else if (data != null) + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + CircleAvatar( + backgroundImage: + UniversalImage.imageProvider(avatarImg), + onBackgroundImageError: (exception, stackTrace) => + Image.asset( + "assets/user-placeholder.png", + height: 16, + width: 16, + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + child: Text( + data.displayName ?? "Guest", + maxLines: 1, + softWrap: false, + overflow: TextOverflow.fade, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ), + IconButton( + icon: const Icon(Icons.settings_outlined), + onPressed: () => Sidebar.goToSettings(context)), + ], + )); + } else { + return Padding( + padding: const EdgeInsets.all(8.0), + child: InkWell( + onTap: () => Sidebar.goToSettings(context), + child: CircleAvatar( + backgroundImage: UniversalImage.imageProvider(avatarImg), + onBackgroundImageError: (exception, stackTrace) => + Image.asset( + "assets/user-placeholder.png", + height: 16, + width: 16, + ), + ), + ), + ); + } + }, ), ); } diff --git a/lib/components/Library/UserDownloads.dart b/lib/components/Library/UserDownloads.dart index 557e94d3..2b522b1d 100644 --- a/lib/components/Library/UserDownloads.dart +++ b/lib/components/Library/UserDownloads.dart @@ -1,6 +1,7 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; import 'package:spotube/provider/Downloader.dart'; @@ -47,7 +48,7 @@ class UserDownloads extends HookConsumerWidget { itemCount: downloader.inQueue.length, itemBuilder: (context, index) { final track = downloader.inQueue.elementAt(index); - return ListTile( + return PlatformListTile( title: Text(track.name!), leading: Padding( padding: const EdgeInsets.symmetric(horizontal: 5), @@ -68,7 +69,6 @@ class UserDownloads extends HookConsumerWidget { height: 30, child: CircularProgressIndicator.adaptive(), ), - horizontalTitleGap: 5, subtitle: Text( TypeConversionUtils.artists_X_String( track.artists ?? [], diff --git a/lib/components/Library/UserLibrary.dart b/lib/components/Library/UserLibrary.dart index 26ef766a..ef0134d0 100644 --- a/lib/components/Library/UserLibrary.dart +++ b/lib/components/Library/UserLibrary.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart' hide Image; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Library/UserAlbums.dart'; import 'package:spotube/components/Library/UserArtists.dart'; import 'package:spotube/components/Library/UserDownloads.dart'; import 'package:spotube/components/Library/UserLocalTracks.dart'; import 'package:spotube/components/Library/UserPlaylists.dart'; import 'package:spotube/components/Shared/AnonymousFallback.dart'; -import 'package:spotube/components/Shared/ColoredTabBar.dart'; class UserLibrary extends ConsumerWidget { const UserLibrary({Key? key}) : super(key: key); @@ -15,27 +15,30 @@ class UserLibrary extends ConsumerWidget { return DefaultTabController( length: 5, child: SafeArea( - child: Scaffold( - appBar: ColoredTabBar( - color: Theme.of(context).backgroundColor, - child: const TabBar( - isScrollable: true, - tabs: [ - Tab(text: "Playlist"), - Tab(text: "Downloads"), - Tab(text: "Local"), - Tab(text: "Artists"), - Tab(text: "Album"), - ], - ), - ), - body: const TabBarView(children: [ - AnonymousFallback(child: UserPlaylists()), - UserDownloads(), - UserLocalTracks(), - AnonymousFallback(child: UserArtists()), - AnonymousFallback(child: UserAlbums()), - ]), + child: PlatformTabView( + placement: PlatformProperty.all(PlatformTabbarPlacement.top), + body: { + PlatformTab( + label: "Playlist", + icon: Container(), + ): const AnonymousFallback(child: UserPlaylists()), + PlatformTab( + label: "Downloads", + icon: Container(), + ): const UserDownloads(), + PlatformTab( + label: "Local", + icon: Container(), + ): const UserLocalTracks(), + PlatformTab( + label: "Artists", + icon: Container(), + ): const AnonymousFallback(child: UserArtists()), + PlatformTab( + label: "Album", + icon: Container(), + ): const AnonymousFallback(child: UserAlbums()), + }, ), ), ); diff --git a/lib/components/Library/UserLocalTracks.dart b/lib/components/Library/UserLocalTracks.dart index 221aa213..6aaf7d3b 100644 --- a/lib/components/Library/UserLocalTracks.dart +++ b/lib/components/Library/UserLocalTracks.dart @@ -9,6 +9,7 @@ import 'package:mime/mime.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerTrackTile.dart'; import 'package:spotube/components/Shared/SortTracksDropdown.dart'; @@ -169,13 +170,7 @@ class UserLocalTracks extends HookConsumerWidget { child: Row( children: [ const SizedBox(width: 10), - ElevatedButton.icon( - label: const Text("Play"), - icon: Icon( - isPlaylistPlaying - ? Icons.stop_rounded - : Icons.play_arrow_rounded, - ), + PlatformFilledButton( onPressed: trackSnapshot.value != null ? () { if (trackSnapshot.value?.isNotEmpty == true) { @@ -187,6 +182,16 @@ class UserLocalTracks extends HookConsumerWidget { } } : null, + child: Row( + children: [ + const Text("Play"), + Icon( + isPlaylistPlaying + ? Icons.stop_rounded + : Icons.play_arrow_rounded, + ) + ], + ), ), const Spacer(), SortTracksDropdown( @@ -196,7 +201,7 @@ class UserLocalTracks extends HookConsumerWidget { }, ), const SizedBox(width: 10), - ElevatedButton( + PlatformFilledButton( child: const Icon(Icons.refresh_rounded), onPressed: () { ref.refresh(localTracksProvider); diff --git a/lib/components/LoaderShimmers/ShimmerArtistProfile.dart b/lib/components/LoaderShimmers/ShimmerArtistProfile.dart index ccabeb64..6cd552f0 100644 --- a/lib/components/LoaderShimmers/ShimmerArtistProfile.dart +++ b/lib/components/LoaderShimmers/ShimmerArtistProfile.dart @@ -11,10 +11,11 @@ class ShimmerArtistProfile extends HookWidget { @override Widget build(BuildContext context) { final shimmerColor = - Theme.of(context).extension()!.shimmerColor!; + Theme.of(context).extension()?.shimmerColor ?? + Colors.white; final shimmerBackgroundColor = Theme.of(context) - .extension()! - .shimmerBackgroundColor!; + .extension() + ?.shimmerBackgroundColor; final avatarWidth = useBreakpointValue( sm: MediaQuery.of(context).size.width * 0.80, diff --git a/lib/components/LoaderShimmers/ShimmerCategories.dart b/lib/components/LoaderShimmers/ShimmerCategories.dart index 7c0d5227..4c231922 100644 --- a/lib/components/LoaderShimmers/ShimmerCategories.dart +++ b/lib/components/LoaderShimmers/ShimmerCategories.dart @@ -9,10 +9,12 @@ class ShimmerCategories extends StatelessWidget { @override Widget build(BuildContext context) { final shimmerColor = - Theme.of(context).extension()!.shimmerColor!; + Theme.of(context).extension()?.shimmerColor ?? + Colors.white; final shimmerBackgroundColor = Theme.of(context) - .extension()! - .shimmerBackgroundColor!; + .extension() + ?.shimmerBackgroundColor ?? + Colors.grey; return Padding( padding: const EdgeInsets.all(8.0), diff --git a/lib/components/LoaderShimmers/ShimmerLyrics.dart b/lib/components/LoaderShimmers/ShimmerLyrics.dart index b1da4453..27843c30 100644 --- a/lib/components/LoaderShimmers/ShimmerLyrics.dart +++ b/lib/components/LoaderShimmers/ShimmerLyrics.dart @@ -12,10 +12,12 @@ class ShimmerLyrics extends HookWidget { @override Widget build(BuildContext context) { final shimmerColor = - Theme.of(context).extension()!.shimmerColor!; + Theme.of(context).extension()?.shimmerColor ?? + Colors.white; final shimmerBackgroundColor = Theme.of(context) - .extension()! - .shimmerBackgroundColor!; + .extension() + ?.shimmerBackgroundColor ?? + Colors.grey; final breakpoint = useBreakpoints(); diff --git a/lib/components/LoaderShimmers/ShimmerPlaybuttonCard.dart b/lib/components/LoaderShimmers/ShimmerPlaybuttonCard.dart index e15fcc81..cfed9690 100644 --- a/lib/components/LoaderShimmers/ShimmerPlaybuttonCard.dart +++ b/lib/components/LoaderShimmers/ShimmerPlaybuttonCard.dart @@ -9,10 +9,12 @@ class ShimmerPlaybuttonCard extends StatelessWidget { @override Widget build(BuildContext context) { final shimmerColor = - Theme.of(context).extension()!.shimmerColor!; + Theme.of(context).extension()?.shimmerColor ?? + Colors.white; final shimmerBackgroundColor = Theme.of(context) - .extension()! - .shimmerBackgroundColor!; + .extension() + ?.shimmerBackgroundColor ?? + Colors.grey; final card = Stack( children: [ diff --git a/lib/components/LoaderShimmers/ShimmerTrackTile.dart b/lib/components/LoaderShimmers/ShimmerTrackTile.dart index e9160bcf..f9c8d9ef 100644 --- a/lib/components/LoaderShimmers/ShimmerTrackTile.dart +++ b/lib/components/LoaderShimmers/ShimmerTrackTile.dart @@ -13,10 +13,12 @@ class ShimmerTrackTile extends StatelessWidget { @override Widget build(BuildContext context) { final shimmerColor = - Theme.of(context).extension()!.shimmerColor!; + Theme.of(context).extension()?.shimmerColor ?? + Colors.white; final shimmerBackgroundColor = Theme.of(context) - .extension()! - .shimmerBackgroundColor!; + .extension() + ?.shimmerBackgroundColor ?? + Colors.grey; final single = Container( margin: const EdgeInsets.symmetric(horizontal: 20), diff --git a/lib/components/Lyrics/Lyrics.dart b/lib/components/Lyrics/Lyrics.dart index 77573eea..225e13be 100644 --- a/lib/components/Lyrics/Lyrics.dart +++ b/lib/components/Lyrics/Lyrics.dart @@ -3,6 +3,8 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:palette_generator/palette_generator.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Lyrics/GeniusLyrics.dart'; import 'package:spotube/components/Lyrics/SyncedLyrics.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; @@ -14,6 +16,25 @@ import 'package:spotube/utils/type_conversion_utils.dart'; class Lyrics extends HookConsumerWidget { const Lyrics({Key? key}) : super(key: key); + Widget buildContainer(Widget child, String albumArt, PaletteColor palette) { + return Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + image: DecorationImage( + image: UniversalImage.imageProvider(albumArt), + fit: BoxFit.cover, + ), + ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), + child: Container( + color: palette.color.withOpacity(.7), + child: child, + ), + ), + ); + } + @override Widget build(BuildContext context, ref) { Playback playback = ref.watch(playbackProvider); @@ -33,8 +54,22 @@ class Lyrics extends HookConsumerWidget { noSetBGColor: true, ); - return DefaultTabController( - length: 2, + return SafeArea( + child: PlatformTabView( + body: { + PlatformTab( + label: "Synced Lyrics", + icon: Container(), + ): buildContainer(SyncedLyrics(palette: palette), albumArt, palette), + PlatformTab( + label: "Lyrics (genius.com)", + icon: Container(), + ): buildContainer(GeniusLyrics(palette: palette), albumArt, palette), + }, + ), + ); + + return SafeArea( child: Scaffold( extendBodyBehindAppBar: true, appBar: const TabBar( diff --git a/lib/components/Playlist/PlaylistCreateDialog.dart b/lib/components/Playlist/PlaylistCreateDialog.dart index 6cae933b..dc085786 100644 --- a/lib/components/Playlist/PlaylistCreateDialog.dart +++ b/lib/components/Playlist/PlaylistCreateDialog.dart @@ -2,6 +2,7 @@ import 'package:fl_query/fl_query.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; @@ -12,7 +13,7 @@ class PlaylistCreateDialog extends HookConsumerWidget { Widget build(BuildContext context, ref) { final spotify = ref.watch(spotifyProvider); - return TextButton( + return PlatformTextButton( onPressed: () { showDialog( context: context, @@ -26,11 +27,11 @@ class PlaylistCreateDialog extends HookConsumerWidget { return AlertDialog( title: const Text("Create a Playlist"), actions: [ - TextButton( + PlatformTextButton( child: const Text("Cancel"), onPressed: () => Navigator.of(context).pop(), ), - ElevatedButton( + PlatformFilledButton( child: const Text("Create"), onPressed: () async { if (playlistName.text.isEmpty) return; @@ -58,19 +59,15 @@ class PlaylistCreateDialog extends HookConsumerWidget { child: ListView( shrinkWrap: true, children: [ - TextField( + PlatformTextField( controller: playlistName, - decoration: const InputDecoration( - hintText: "Name of the playlist", - label: Text("Playlist Name"), - ), + placeholder: "Name of the playlist", + label: "Playlist Name", ), const SizedBox(height: 10), - TextField( + PlatformTextField( controller: description, - decoration: const InputDecoration( - hintText: "Description...", - ), + placeholder: "Description...", keyboardType: TextInputType.multiline, maxLines: 5, ), diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index 3195b195..10ba07f4 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -3,6 +3,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart' hide Page; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Album/AlbumCard.dart'; import 'package:spotube/components/Artist/ArtistCard.dart'; @@ -85,23 +86,15 @@ class Search extends HookConsumerWidget { vertical: 10, ), color: Theme.of(context).backgroundColor, - child: TextField( + child: PlatformTextField( onChanged: (value) { ref.read(searchTermStateProvider.notifier).state = value; }, - decoration: InputDecoration( - isDense: true, - suffix: ElevatedButton( - onPressed: onSearch, - child: const Icon(Icons.search_rounded), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 7, - ), - hintStyle: const TextStyle(height: 2), - hintText: "Search...", + suffix: PlatformFilledButton( + onPressed: onSearch, + child: const Icon(Icons.search_rounded), ), + placeholder: "Search...", onSubmitted: (value) { onSearch(); }, diff --git a/lib/components/Settings/About.dart b/lib/components/Settings/About.dart index 6a3eaeb1..f243b561 100644 --- a/lib/components/Settings/About.dart +++ b/lib/components/Settings/About.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/Hyperlink.dart'; import 'package:spotube/hooks/usePackageInfo.dart'; @@ -29,7 +30,7 @@ class About extends HookWidget { version: "2.5.0", ); - return ListTile( + return PlatformListTile( leading: const Icon(Icons.info_outline_rounded), title: const Text("About Spotube"), onTap: () { diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index b32307b6..cf2b6dee 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Settings/About.dart'; import 'package:spotube/components/Settings/ColorSchemePickerDialog.dart'; import 'package:spotube/components/Shared/AdaptiveListTile.dart'; @@ -88,7 +89,7 @@ class Settings extends HookConsumerWidget { ), ), ), - trailing: (context, update) => ElevatedButton( + trailing: (context, update) => PlatformFilledButton( onPressed: () { GoRouter.of(context).push("/login"); }, @@ -105,7 +106,7 @@ class Settings extends HookConsumerWidget { if (auth.isLoggedIn) Builder(builder: (context) { Auth auth = ref.watch(authProvider); - return ListTile( + return PlatformListTile( leading: const Icon(Icons.logout_rounded), title: const SizedBox( height: 50, @@ -118,7 +119,7 @@ class Settings extends HookConsumerWidget { ), ), ), - trailing: ElevatedButton( + trailing: PlatformFilledButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(Colors.red), @@ -144,24 +145,25 @@ class Settings extends HookConsumerWidget { subtitle: const Text( "Override responsive layout mode settings", ), - trailing: (context, update) => DropdownButton( + trailing: (context, update) => + PlatformDropDownMenu( value: preferences.layoutMode, - items: const [ - DropdownMenuItem( + items: [ + PlatformDropDownMenuItem( value: LayoutMode.adaptive, - child: Text( + child: const Text( "Adaptive", ), ), - DropdownMenuItem( + PlatformDropDownMenuItem( value: LayoutMode.compact, - child: Text( + child: const Text( "Compact", ), ), - DropdownMenuItem( + PlatformDropDownMenuItem( value: LayoutMode.extended, - child: Text("Extended"), + child: const Text("Extended"), ), ], onChanged: (value) { @@ -175,24 +177,25 @@ class Settings extends HookConsumerWidget { AdaptiveListTile( leading: const Icon(Icons.dark_mode_outlined), title: const Text("Theme"), - trailing: (context, update) => DropdownButton( + trailing: (context, update) => + PlatformDropDownMenu( value: preferences.themeMode, - items: const [ - DropdownMenuItem( + items: [ + PlatformDropDownMenuItem( value: ThemeMode.dark, - child: Text( + child: const Text( "Dark", ), ), - DropdownMenuItem( + PlatformDropDownMenuItem( value: ThemeMode.light, - child: Text( + child: const Text( "Light", ), ), - DropdownMenuItem( + PlatformDropDownMenuItem( value: ThemeMode.system, - child: Text("System"), + child: const Text("System"), ), ], onChanged: (value) { @@ -203,7 +206,7 @@ class Settings extends HookConsumerWidget { }, ), ), - ListTile( + PlatformListTile( leading: const Icon(Icons.palette_outlined), title: const Text("Accent Color Scheme"), contentPadding: const EdgeInsets.symmetric( @@ -217,7 +220,7 @@ class Settings extends HookConsumerWidget { ), onTap: pickColorScheme(ColorSchemeType.accent), ), - ListTile( + PlatformListTile( leading: const Icon(Icons.format_color_fill_rounded), title: const Text("Background Color Scheme"), contentPadding: const EdgeInsets.symmetric( @@ -231,11 +234,10 @@ class Settings extends HookConsumerWidget { ), onTap: pickColorScheme(ColorSchemeType.background), ), - ListTile( + PlatformListTile( leading: const Icon(Icons.album_rounded), title: const Text("Rotating Album Art"), - trailing: Switch.adaptive( - activeColor: Theme.of(context).primaryColor, + trailing: PlatformSwitch( value: preferences.rotatingAlbumArt, onChanged: (state) { preferences.setRotatingAlbumArt(state); @@ -251,18 +253,18 @@ class Settings extends HookConsumerWidget { leading: const Icon(Icons.multitrack_audio_rounded), title: const Text("Audio Quality"), trailing: (context, update) => - DropdownButton( + PlatformDropDownMenu( value: preferences.audioQuality, - items: const [ - DropdownMenuItem( + items: [ + PlatformDropDownMenuItem( value: AudioQuality.high, - child: Text( + child: const Text( "High", ), ), - DropdownMenuItem( + PlatformDropDownMenuItem( value: AudioQuality.low, - child: Text("Low"), + child: const Text("Low"), ), ], onChanged: (value) { @@ -274,7 +276,7 @@ class Settings extends HookConsumerWidget { ), ), if (kIsMobile) - ListTile( + PlatformListTile( leading: const Icon(Icons.download_for_offline_rounded), title: const Text( "Pre download and play", @@ -282,21 +284,19 @@ class Settings extends HookConsumerWidget { subtitle: const Text( "Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)", ), - trailing: Switch.adaptive( - activeColor: Theme.of(context).primaryColor, + trailing: PlatformSwitch( value: preferences.androidBytesPlay, onChanged: (state) { preferences.setAndroidBytesPlay(state); }, ), ), - ListTile( + PlatformListTile( leading: const Icon(Icons.fast_forward_rounded), title: const Text( "Skip non-music segments (SponsorBlock)", ), - trailing: Switch.adaptive( - activeColor: Theme.of(context).primaryColor, + trailing: PlatformSwitch( value: preferences.skipSponsorSegments, onChanged: (state) { preferences.setSkipSponsorSegments(state); @@ -320,12 +320,11 @@ class Settings extends HookConsumerWidget { ), trailing: (context, update) => ConstrainedBox( constraints: const BoxConstraints(maxWidth: 250), - child: DropdownButton( - isExpanded: true, + child: PlatformDropDownMenu( value: preferences.recommendationMarket, items: spotifyMarkets .map( - (country) => (DropdownMenuItem( + (country) => (PlatformDropDownMenuItem( value: country.first, child: Text(country.last), )), @@ -358,18 +357,15 @@ class Settings extends HookConsumerWidget { breakOn: Breakpoints.lg, trailing: (context, update) => ConstrainedBox( constraints: const BoxConstraints(maxWidth: 450), - child: TextField( + child: PlatformTextField( controller: ytSearchFormatController, - decoration: InputDecoration( - isDense: true, - suffix: ElevatedButton( - child: const Icon(Icons.save_rounded), - onPressed: () { - preferences.setYtSearchFormat( - ytSearchFormatController.value.text, - ); - }, - ), + suffix: PlatformFilledButton( + child: const Icon(Icons.save_rounded), + onPressed: () { + preferences.setYtSearchFormat( + ytSearchFormatController.value.text, + ); + }, ), onSubmitted: (value) { preferences.setYtSearchFormat(value); @@ -392,24 +388,24 @@ class Settings extends HookConsumerWidget { ), ), trailing: (context, update) => - DropdownButton( + PlatformDropDownMenu( value: preferences.trackMatchAlgorithm, - items: const [ - DropdownMenuItem( + items: [ + PlatformDropDownMenuItem( value: SpotubeTrackMatchAlgorithm.authenticPopular, - child: Text( + child: const Text( "Popular from Author", ), ), - DropdownMenuItem( + PlatformDropDownMenuItem( value: SpotubeTrackMatchAlgorithm.popular, - child: Text( + child: const Text( "Accurately Popular", ), ), - DropdownMenuItem( + PlatformDropDownMenuItem( value: SpotubeTrackMatchAlgorithm.youtube, - child: Text("YouTube's Top choice"), + child: const Text("YouTube's Top choice"), ), ], onChanged: (value) { @@ -425,21 +421,20 @@ class Settings extends HookConsumerWidget { style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), ), - ListTile( + PlatformListTile( leading: const Icon(Icons.file_download_outlined), title: const Text("Download Location"), subtitle: Text(preferences.downloadLocation), - trailing: ElevatedButton( + trailing: PlatformFilledButton( onPressed: pickDownloadLocation, child: const Icon(Icons.folder_rounded), ), onTap: pickDownloadLocation, ), - ListTile( + PlatformListTile( leading: const Icon(Icons.lyrics_rounded), title: const Text("Download lyrics along with the Track"), - trailing: Switch.adaptive( - activeColor: Theme.of(context).primaryColor, + trailing: PlatformSwitch( value: preferences.saveTrackLyrics, onChanged: (state) { preferences.setSaveTrackLyrics(state); @@ -487,11 +482,10 @@ class Settings extends HookConsumerWidget { }, ), ), - ListTile( + PlatformListTile( leading: const Icon(Icons.update_rounded), title: const Text("Check for Update"), - trailing: Switch.adaptive( - activeColor: Theme.of(context).primaryColor, + trailing: PlatformSwitch( value: preferences.checkUpdate, onChanged: (checked) => preferences.setCheckUpdate(checked), diff --git a/lib/components/Shared/AdaptiveListTile.dart b/lib/components/Shared/AdaptiveListTile.dart index 271c598d..ba81621b 100644 --- a/lib/components/Shared/AdaptiveListTile.dart +++ b/lib/components/Shared/AdaptiveListTile.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; class AdaptiveListTile extends HookWidget { @@ -24,7 +25,7 @@ class AdaptiveListTile extends HookWidget { Widget build(BuildContext context) { final breakpoint = useBreakpoints(); - return ListTile( + return PlatformListTile( title: title, subtitle: subtitle, trailing: diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index 772319ce..c523e246 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/HoverBuilder.dart'; import 'package:spotube/components/Shared/SpotubeMarqueeText.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; @@ -67,7 +68,7 @@ class PlaybuttonCard extends StatelessWidget { bottom: 10, end: 5, child: Builder(builder: (context) { - return ElevatedButton( + return PlatformFilledButton( onPressed: onPlaybuttonPressed, style: ButtonStyle( shape: MaterialStateProperty.all( diff --git a/lib/components/Shared/SortTracksDropdown.dart b/lib/components/Shared/SortTracksDropdown.dart index 2b503613..0b42dd2a 100644 --- a/lib/components/Shared/SortTracksDropdown.dart +++ b/lib/components/Shared/SortTracksDropdown.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Library/UserLocalTracks.dart'; class SortTracksDropdown extends StatelessWidget { @@ -12,43 +13,41 @@ class SortTracksDropdown extends StatelessWidget { @override Widget build(BuildContext context) { - return PopupMenuButton( - itemBuilder: (context) { - return [ - PopupMenuItem( - value: SortBy.none, - enabled: value != SortBy.none, - child: const Text("None"), - ), - PopupMenuItem( - value: SortBy.ascending, - enabled: value != SortBy.ascending, - child: const Text("Sort by A-Z"), - ), - PopupMenuItem( - value: SortBy.descending, - enabled: value != SortBy.descending, - child: const Text("Sort by Z-A"), - ), - PopupMenuItem( - value: SortBy.dateAdded, - enabled: value != SortBy.dateAdded, - child: const Text("Sort by Date"), - ), - PopupMenuItem( - value: SortBy.artist, - enabled: value != SortBy.artist, - child: const Text("Sort by Artist"), - ), - PopupMenuItem( - value: SortBy.album, - enabled: value != SortBy.album, - child: const Text("Sort by Album"), - ), - ]; - }, + return PlatformPopupMenuButton( + items: [ + PlatformPopupMenuItem( + value: SortBy.none, + enabled: value != SortBy.none, + child: const Text("None"), + ), + PlatformPopupMenuItem( + value: SortBy.ascending, + enabled: value != SortBy.ascending, + child: const Text("Sort by A-Z"), + ), + PlatformPopupMenuItem( + value: SortBy.descending, + enabled: value != SortBy.descending, + child: const Text("Sort by Z-A"), + ), + PlatformPopupMenuItem( + value: SortBy.dateAdded, + enabled: value != SortBy.dateAdded, + child: const Text("Sort by Date"), + ), + PlatformPopupMenuItem( + value: SortBy.artist, + enabled: value != SortBy.artist, + child: const Text("Sort by Artist"), + ), + PlatformPopupMenuItem( + value: SortBy.album, + enabled: value != SortBy.album, + child: const Text("Sort by Album"), + ), + ], onSelected: onChanged, - icon: const Icon(Icons.sort_rounded), + child: const Icon(Icons.sort_rounded), ); } } diff --git a/lib/main.dart b/lib/main.dart index ae6b3876..c26eff73 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotube/components/Shared/ReplaceDownloadedFileDialog.dart'; import 'package:spotube/entities/CacheTrack.dart'; @@ -198,57 +199,66 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - return MaterialApp.router( - routerConfig: router, + platform = TargetPlatform.macOS; + + return PlatformApp.router( + routeInformationParser: router.routeInformationParser, + routerDelegate: router.routerDelegate, + routeInformationProvider: router.routeInformationProvider, debugShowCheckedModeBanner: false, title: 'Spotube', - theme: lightTheme( + androidTheme: lightTheme( accentMaterialColor: accentMaterialColor, backgroundMaterialColor: backgroundMaterialColor, ), - darkTheme: darkTheme( + androidDarkTheme: darkTheme( accentMaterialColor: accentMaterialColor, backgroundMaterialColor: backgroundMaterialColor, ), themeMode: themeMode, - shortcuts: { - ...WidgetsApp.defaultShortcuts, - const SingleActivator(LogicalKeyboardKey.space): PlayPauseIntent(ref), - const SingleActivator(LogicalKeyboardKey.comma, control: true): + shortcuts: PlatformProperty.all({ + ...WidgetsApp.defaultShortcuts.map((key, value) { + return MapEntry( + LogicalKeySet.fromSet(key.triggers?.toSet() ?? {}), + value, + ); + }), + LogicalKeySet(LogicalKeyboardKey.space): PlayPauseIntent(ref), + LogicalKeySet(LogicalKeyboardKey.comma, LogicalKeyboardKey.control): NavigationIntent(router, "/settings"), - const SingleActivator( + LogicalKeySet( LogicalKeyboardKey.keyB, - control: true, - shift: true, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, ): HomeTabIntent(ref, tab: HomeTabs.browse), - const SingleActivator( + LogicalKeySet( LogicalKeyboardKey.keyS, - control: true, - shift: true, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, ): HomeTabIntent(ref, tab: HomeTabs.search), - const SingleActivator( + LogicalKeySet( LogicalKeyboardKey.keyL, - control: true, - shift: true, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, ): HomeTabIntent(ref, tab: HomeTabs.library), - const SingleActivator( + LogicalKeySet( LogicalKeyboardKey.keyY, - control: true, - shift: true, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, ): HomeTabIntent(ref, tab: HomeTabs.lyrics), - const SingleActivator( + LogicalKeySet( LogicalKeyboardKey.keyW, - control: true, - shift: true, + LogicalKeyboardKey.control, + LogicalKeyboardKey.shift, ): CloseAppIntent(), - }, - actions: { + }), + actions: PlatformProperty.all({ ...WidgetsApp.defaultActions, PlayPauseIntent: PlayPauseAction(), NavigationIntent: NavigationAction(), HomeTabIntent: HomeTabAction(), CloseAppIntent: CloseAppAction(), - }, + }), ); } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index f18fb8ac..7beb29a8 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import audio_session import audioplayers_darwin import bitsdojo_window_macos import connectivity_plus_macos +import macos_ui import metadata_god import package_info_plus_macos import path_provider_macos @@ -23,6 +24,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin")) MetadataGodPlugin.register(with: registry.registrar(forPlugin: "MetadataGodPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 7db67dcc..280f5c49 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -493,6 +493,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.1" + fluent_ui: + dependency: transitive + description: + name: fluent_ui + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.3" flutter: dependency: "direct main" description: flutter @@ -561,6 +568,11 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -690,6 +702,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.2.0" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" introduction_screen: dependency: "direct main" description: @@ -739,6 +758,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + macos_ui: + dependency: transitive + description: + name: macos_ui + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.5" marquee: dependency: "direct main" description: @@ -972,6 +998,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0" + platform_ui: + dependency: "direct main" + description: + path: "../platform_ui" + relative: true + source: path + version: "0.0.1" plugin_platform_interface: dependency: transitive description: @@ -1021,6 +1054,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0+1" + recase: + dependency: transitive + description: + name: recase + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" riverpod: dependency: transitive description: @@ -1035,6 +1075,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.27.3" + scroll_pos: + dependency: transitive + description: + name: scroll_pos + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" scroll_to_index: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 2f7175e6..db52cc89 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,8 @@ dependencies: flutter_inappwebview: ^5.4.3+7 tuple: ^2.0.1 uuid: ^3.0.6 + platform_ui: + path: ../platform_ui dev_dependencies: flutter_test: From 69739b457296a6b209aa6f73beb378ae1f089ac5 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 29 Oct 2022 15:26:55 +0600 Subject: [PATCH 02/23] feat: replace all types of buttons with platform buttons --- lib/components/Artist/ArtistProfile.dart | 15 +++++---- lib/components/Home/Sidebar.dart | 10 +++--- lib/components/Library/UserDownloads.dart | 2 +- lib/components/Login/LoginTutorial.dart | 5 +-- lib/components/Login/TokenLogin.dart | 3 +- lib/components/Login/TokenLoginForms.dart | 3 +- .../Lyrics/LyricDelayAdjustDialog.dart | 9 +++--- lib/components/Lyrics/SyncedLyrics.dart | 3 +- lib/components/Player/PlayerActions.dart | 7 ++-- lib/components/Player/PlayerControls.dart | 31 +++++++++++------- lib/components/Player/PlayerOverlay.dart | 21 +++++++----- lib/components/Player/PlayerView.dart | 3 +- lib/components/Search/Search.dart | 2 +- .../Settings/ColorSchemePickerDialog.dart | 5 +-- lib/components/Settings/Settings.dart | 10 ++++-- .../Shared/AdaptivePopupMenuButton.dart | 19 ++++++----- lib/components/Shared/AnonymousFallback.dart | 3 +- .../Shared/DownloadConfirmationDialog.dart | 5 +-- lib/components/Shared/HeartButton.dart | 3 +- lib/components/Shared/PageWindowTitleBar.dart | 7 ++-- .../Shared/ReplaceDownloadedFileDialog.dart | 5 +-- .../Shared/TrackCollectionView.dart | 13 +++----- lib/components/Shared/TrackTile.dart | 7 ++-- lib/components/Shared/TracksTableView.dart | 32 +++++++++---------- lib/hooks/useUpdateChecker.dart | 3 +- lib/main.dart | 2 +- 26 files changed, 131 insertions(+), 97 deletions(-) diff --git a/lib/components/Artist/ArtistProfile.dart b/lib/components/Artist/ArtistProfile.dart index 266137fe..5d1e288f 100644 --- a/lib/components/Artist/ArtistProfile.dart +++ b/lib/components/Artist/ArtistProfile.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Artist/ArtistAlbumList.dart'; import 'package:spotube/components/Artist/ArtistCard.dart'; @@ -176,7 +177,7 @@ class ArtistProfile extends HookConsumerWidget { ); }, ), - IconButton( + PlatformIconButton( icon: const Icon(Icons.share_rounded), onPressed: () { Clipboard.setData( @@ -260,11 +261,13 @@ class ArtistProfile extends HookConsumerWidget { color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(50), ), - child: IconButton( - icon: Icon(isPlaylistPlaying - ? Icons.stop_rounded - : Icons.play_arrow_rounded), - color: Colors.white, + child: PlatformIconButton( + icon: Icon( + isPlaylistPlaying + ? Icons.stop_rounded + : Icons.play_arrow_rounded, + color: Colors.white, + ), onPressed: () => playPlaylist(topTracks.toList()), ), diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart index 517c4039..1d42c49e 100644 --- a/lib/components/Home/Sidebar.dart +++ b/lib/components/Home/Sidebar.dart @@ -75,7 +75,7 @@ class Sidebar extends HookConsumerWidget { if (layoutMode == LayoutMode.compact || (breakpoints.isSm && layoutMode == LayoutMode.adaptive)) { - return Container(); + return child; } void toggleExtended() => @@ -117,7 +117,7 @@ class Sidebar extends HookConsumerWidget { child: MoveWindow( child: !extended.value ? Center( - child: IconButton( + child: PlatformIconButton( icon: const Icon(Icons.menu_rounded), onPressed: toggleExtended, ), @@ -127,7 +127,7 @@ class Sidebar extends HookConsumerWidget { ), if (!kIsDesktop && !extended.value) Center( - child: IconButton( + child: PlatformIconButton( icon: const Icon(Icons.menu_rounded), onPressed: toggleExtended, ), @@ -143,7 +143,7 @@ class Sidebar extends HookConsumerWidget { "Spotube", style: Theme.of(context).textTheme.headline4, ), - IconButton( + PlatformIconButton( icon: const Icon(Icons.menu_rounded), onPressed: toggleExtended, ), @@ -241,7 +241,7 @@ class SidebarFooter extends HookConsumerWidget { ], ), ), - IconButton( + PlatformIconButton( icon: const Icon(Icons.settings_outlined), onPressed: () => Sidebar.goToSettings(context)), ], diff --git a/lib/components/Library/UserDownloads.dart b/lib/components/Library/UserDownloads.dart index 2b522b1d..4ceaf3d5 100644 --- a/lib/components/Library/UserDownloads.dart +++ b/lib/components/Library/UserDownloads.dart @@ -30,7 +30,7 @@ class UserDownloads extends HookConsumerWidget { ), ), const SizedBox(width: 10), - ElevatedButton( + PlatformFilledButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.red[50], foregroundColor: Colors.red[400], diff --git a/lib/components/Login/LoginTutorial.dart b/lib/components/Login/LoginTutorial.dart index 21a51bbb..6d15578e 100644 --- a/lib/components/Login/LoginTutorial.dart +++ b/lib/components/Login/LoginTutorial.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:introduction_screen/introduction_screen.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Login/TokenLoginForms.dart'; import 'package:spotube/components/Shared/Hyperlink.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; @@ -16,7 +17,7 @@ class LoginTutorial extends ConsumerWidget { return Scaffold( appBar: PageWindowTitleBar( - leading: TextButton( + leading: PlatformTextButton( child: const Text("Exit"), onPressed: () { Navigator.of(context).pop(); @@ -27,7 +28,7 @@ class LoginTutorial extends ConsumerWidget { next: const Text("Next"), back: const Text("Previous"), showBackButton: true, - overrideDone: TextButton( + overrideDone: PlatformTextButton( onPressed: auth.isLoggedIn ? () { ServiceUtils.navigate(context, "/"); diff --git a/lib/components/Login/TokenLogin.dart b/lib/components/Login/TokenLogin.dart index 57b2be9a..e7a81657 100644 --- a/lib/components/Login/TokenLogin.dart +++ b/lib/components/Login/TokenLogin.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Login/TokenLoginForms.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; @@ -45,7 +46,7 @@ class TokenLogin extends HookConsumerWidget { crossAxisAlignment: WrapCrossAlignment.center, children: [ const Text("Don't know how to do this?"), - TextButton( + PlatformTextButton( child: const Text( "Follow along the Step by Step guide", ), diff --git a/lib/components/Login/TokenLoginForms.dart b/lib/components/Login/TokenLoginForms.dart index f50e25db..76ee5a5c 100644 --- a/lib/components/Login/TokenLoginForms.dart +++ b/lib/components/Login/TokenLoginForms.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/provider/Auth.dart'; import 'package:spotube/utils/service_utils.dart'; @@ -42,7 +43,7 @@ class TokenLoginForm extends HookConsumerWidget { keyboardType: TextInputType.visiblePassword, ), const SizedBox(height: 20), - ElevatedButton( + PlatformFilledButton( onPressed: () async { if (keyCodeController.text.isEmpty || directCodeController.text.isEmpty) { diff --git a/lib/components/Lyrics/LyricDelayAdjustDialog.dart b/lib/components/Lyrics/LyricDelayAdjustDialog.dart index 222efa01..f0c5c909 100644 --- a/lib/components/Lyrics/LyricDelayAdjustDialog.dart +++ b/lib/components/Lyrics/LyricDelayAdjustDialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Lyrics/SyncedLyrics.dart'; class LyricDelayAdjustDialog extends HookConsumerWidget { @@ -18,13 +19,13 @@ class LyricDelayAdjustDialog extends HookConsumerWidget { return AlertDialog( title: const Center(child: Text("Adjust Lyrics Delay")), actions: [ - ElevatedButton( + PlatformFilledButton( child: const Text("Cancel"), onPressed: () { Navigator.of(context).pop(); }, ), - ElevatedButton( + PlatformFilledButton( child: const Text("Done"), onPressed: () { Navigator.of(context).pop( @@ -38,7 +39,7 @@ class LyricDelayAdjustDialog extends HookConsumerWidget { content: Row( mainAxisSize: MainAxisSize.min, children: [ - IconButton( + PlatformIconButton( icon: const Icon(Icons.remove_rounded), onPressed: () { controller.text = "${getValue() - 25}ms"; @@ -61,7 +62,7 @@ class LyricDelayAdjustDialog extends HookConsumerWidget { }, ), ), - IconButton( + PlatformIconButton( icon: const Icon(Icons.add_rounded), onPressed: () { controller.text = "${getValue() + 25}ms"; diff --git a/lib/components/Lyrics/SyncedLyrics.dart b/lib/components/Lyrics/SyncedLyrics.dart index 042102a8..ed69c350 100644 --- a/lib/components/Lyrics/SyncedLyrics.dart +++ b/lib/components/Lyrics/SyncedLyrics.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:palette_generator/palette_generator.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerLyrics.dart'; import 'package:spotube/components/Lyrics/LyricDelayAdjustDialog.dart'; @@ -85,7 +86,7 @@ class SyncedLyrics extends HookConsumerWidget { Positioned.fill( child: Align( alignment: Alignment.centerRight, - child: IconButton( + child: PlatformIconButton( tooltip: "Lyrics Delay", icon: const Icon(Icons.av_timer_rounded), onPressed: () async { diff --git a/lib/components/Player/PlayerActions.dart b/lib/components/Player/PlayerActions.dart index 53f93595..9b543d55 100644 --- a/lib/components/Player/PlayerActions.dart +++ b/lib/components/Player/PlayerActions.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Library/UserLocalTracks.dart'; import 'package:spotube/components/Player/PlayerQueue.dart'; @@ -48,7 +49,7 @@ class PlayerActions extends HookConsumerWidget { return Row( mainAxisAlignment: mainAxisAlignment, children: [ - IconButton( + PlatformIconButton( icon: const Icon(Icons.queue_music_rounded), tooltip: 'Queue', onPressed: playback.playlist != null @@ -73,7 +74,7 @@ class PlayerActions extends HookConsumerWidget { } : null, ), - IconButton( + PlatformIconButton( icon: const Icon(Icons.alt_route_rounded), tooltip: "Alternative Track Sources", onPressed: playback.track != null @@ -108,7 +109,7 @@ class PlayerActions extends HookConsumerWidget { ), ) else - IconButton( + PlatformIconButton( tooltip: 'Download track', icon: Icon( isDownloaded diff --git a/lib/components/Player/PlayerControls.dart b/lib/components/Player/PlayerControls.dart index d0a2c413..381efac2 100644 --- a/lib/components/Player/PlayerControls.dart +++ b/lib/components/Player/PlayerControls.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/hooks/playback.dart'; import 'package:spotube/models/Intents.dart'; import 'package:spotube/models/Logger.dart'; @@ -138,7 +139,7 @@ class PlayerControls extends HookConsumerWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - IconButton( + PlatformIconButton( tooltip: playback.isLoop ? "Repeat playlist" : playback.isShuffled @@ -156,14 +157,16 @@ class PlayerControls extends HookConsumerWidget { ? null : playback.cyclePlaybackMode, ), - IconButton( + PlatformIconButton( tooltip: "Previous track", - icon: const Icon(Icons.skip_previous_rounded), - color: iconColor, + icon: Icon( + Icons.skip_previous_rounded, + color: iconColor, + ), onPressed: () { onPrevious(); }), - IconButton( + PlatformIconButton( tooltip: playback.isPlaying ? "Pause playback" : "Resume playback", @@ -177,23 +180,27 @@ class PlayerControls extends HookConsumerWidget { playback.isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded, + color: iconColor, ), - color: iconColor, onPressed: Actions.handler( context, PlayPauseIntent(ref), ), ), - IconButton( + PlatformIconButton( tooltip: "Next track", - icon: const Icon(Icons.skip_next_rounded), + icon: Icon( + Icons.skip_next_rounded, + color: iconColor, + ), onPressed: () => onNext(), - color: iconColor, ), - IconButton( + PlatformIconButton( tooltip: "Stop playback", - icon: const Icon(Icons.stop_rounded), - color: iconColor, + icon: Icon( + Icons.stop_rounded, + color: iconColor, + ), onPressed: playback.track != null ? () async { try { diff --git a/lib/components/Player/PlayerOverlay.dart b/lib/components/Player/PlayerOverlay.dart index f5104722..a36eb9b8 100644 --- a/lib/components/Player/PlayerOverlay.dart +++ b/lib/components/Player/PlayerOverlay.dart @@ -3,6 +3,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Player/PlayerTrackDetails.dart'; import 'package:spotube/hooks/playback.dart'; import 'package:spotube/hooks/usePaletteColor.dart'; @@ -79,21 +80,23 @@ class PlayerOverlay extends HookConsumerWidget { ), Row( children: [ - IconButton( - icon: const Icon(Icons.skip_previous_rounded), - color: paletteColor.bodyTextColor, + PlatformIconButton( + icon: Icon( + Icons.skip_previous_rounded, + color: paletteColor.bodyTextColor, + ), onPressed: () { onPrevious(); }), Consumer( builder: (context, ref, _) { - return IconButton( + return PlatformIconButton( icon: Icon( ref.read(playbackProvider).isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded, + color: paletteColor.bodyTextColor, ), - color: paletteColor.bodyTextColor, onPressed: Actions.handler( context, PlayPauseIntent(ref), @@ -101,10 +104,12 @@ class PlayerOverlay extends HookConsumerWidget { ); }, ), - IconButton( - icon: const Icon(Icons.skip_next_rounded), + PlatformIconButton( + icon: Icon( + Icons.skip_next_rounded, + color: paletteColor.bodyTextColor, + ), onPressed: () => onNext(), - color: paletteColor.bodyTextColor, ), ], ), diff --git a/lib/components/Player/PlayerView.dart b/lib/components/Player/PlayerView.dart index 2622de4b..96edfcca 100644 --- a/lib/components/Player/PlayerView.dart +++ b/lib/components/Player/PlayerView.dart @@ -5,6 +5,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:palette_generator/palette_generator.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Player/PlayerActions.dart'; import 'package:spotube/components/Player/PlayerControls.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; @@ -162,7 +163,7 @@ class PlayerView extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, floatingQueue: false, extraActions: [ - IconButton( + PlatformIconButton( tooltip: "Open Lyrics", icon: const Icon(Icons.lyrics_rounded), onPressed: () { diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index 10ba07f4..d31539a4 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -185,7 +185,7 @@ class Search extends HookConsumerWidget { }), if (searchTrack.hasNextPage && tracks.isNotEmpty) Center( - child: TextButton( + child: PlatformTextButton( onPressed: searchTrack.isFetchingNextPage ? null : () => searchTrack.fetchNextPage(), diff --git a/lib/components/Settings/ColorSchemePickerDialog.dart b/lib/components/Settings/ColorSchemePickerDialog.dart index f96620d2..7c0fbe8a 100644 --- a/lib/components/Settings/ColorSchemePickerDialog.dart +++ b/lib/components/Settings/ColorSchemePickerDialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/provider/UserPreferences.dart'; final highContrast = MaterialColor( @@ -68,13 +69,13 @@ class ColorSchemePickerDialog extends HookConsumerWidget { return AlertDialog( title: Text("Pick ${schemeType.name} color scheme"), actions: [ - TextButton( + PlatformTextButton( child: const Text("Cancel"), onPressed: () { Navigator.pop(context); }, ), - ElevatedButton( + PlatformFilledButton( child: const Text("Save"), onPressed: () { switch (schemeType) { diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index cf2b6dee..bcec8e46 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -466,9 +466,7 @@ class Settings extends HookConsumerWidget { ), ), ), - trailing: (context, update) => ElevatedButton.icon( - icon: const Icon(Icons.favorite_outline_rounded), - label: const Text("Please Sponsor/Donate"), + trailing: (context, update) => PlatformFilledButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.red[100], foregroundColor: Colors.pinkAccent, @@ -480,6 +478,12 @@ class Settings extends HookConsumerWidget { mode: LaunchMode.externalApplication, ); }, + child: Row( + children: const [ + Icon(Icons.favorite_outline_rounded), + Text("Please Sponsor/Donate"), + ], + ), ), ), PlatformListTile( diff --git a/lib/components/Shared/AdaptivePopupMenuButton.dart b/lib/components/Shared/AdaptivePopupMenuButton.dart index dfa8b3dc..fc1d0a98 100644 --- a/lib/components/Shared/AdaptivePopupMenuButton.dart +++ b/lib/components/Shared/AdaptivePopupMenuButton.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:popover/popover.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; @@ -24,23 +25,25 @@ class Action extends StatelessWidget { "\"", "", ), - child: IconButton( + child: PlatformIconButton( icon: icon, onPressed: onPressed, ), ); } - return TextButton.icon( + return PlatformTextButton( style: TextButton.styleFrom( foregroundColor: Theme.of(context).textTheme.bodyMedium?.color, padding: const EdgeInsets.all(20), ), - icon: icon, - label: Align( - alignment: Alignment.centerLeft, - child: text, - ), onPressed: onPressed, + child: Row( + children: [ + icon, + const SizedBox(width: 10), + text, + ], + ), ); } } @@ -59,7 +62,7 @@ class AdaptiveActions extends HookWidget { final breakpoint = useBreakpoints(); if (breakpoint.isLessThan(breakOn)) { - return IconButton( + return PlatformIconButton( icon: const Icon(Icons.more_horiz), onPressed: () { showPopover( diff --git a/lib/components/Shared/AnonymousFallback.dart b/lib/components/Shared/AnonymousFallback.dart index 3c8e1931..252d1106 100644 --- a/lib/components/Shared/AnonymousFallback.dart +++ b/lib/components/Shared/AnonymousFallback.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/provider/Auth.dart'; import 'package:spotube/utils/service_utils.dart'; @@ -21,7 +22,7 @@ class AnonymousFallback extends ConsumerWidget { children: [ const Text("You're not logged in"), const SizedBox(height: 10), - ElevatedButton( + PlatformFilledButton( child: const Text("Login with Spotify"), onPressed: () => ServiceUtils.navigate(context, "/settings"), ) diff --git a/lib/components/Shared/DownloadConfirmationDialog.dart b/lib/components/Shared/DownloadConfirmationDialog.dart index 48c96a32..73c04a59 100644 --- a/lib/components/Shared/DownloadConfirmationDialog.dart +++ b/lib/components/Shared/DownloadConfirmationDialog.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; class DownloadConfirmationDialog extends StatelessWidget { @@ -57,11 +58,11 @@ class DownloadConfirmationDialog extends StatelessWidget { ), ), actions: [ - ElevatedButton( + PlatformFilledButton( child: const Text("Decline"), onPressed: () => Navigator.of(context).pop(false), ), - ElevatedButton( + PlatformFilledButton( onPressed: () => Navigator.of(context).pop(true), style: ElevatedButton.styleFrom( foregroundColor: Colors.white, diff --git a/lib/components/Shared/HeartButton.dart b/lib/components/Shared/HeartButton.dart index 24954b34..b119cb9c 100644 --- a/lib/components/Shared/HeartButton.dart +++ b/lib/components/Shared/HeartButton.dart @@ -3,6 +3,7 @@ import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/hooks/usePaletteColor.dart'; import 'package:spotube/provider/Auth.dart'; @@ -32,7 +33,7 @@ class HeartButton extends ConsumerWidget { if (!auth.isLoggedIn) return Container(); - return IconButton( + return PlatformIconButton( tooltip: tooltip, icon: Icon( icon ?? diff --git a/lib/components/Shared/PageWindowTitleBar.dart b/lib/components/Shared/PageWindowTitleBar.dart index cc39f499..3ef24250 100644 --- a/lib/components/Shared/PageWindowTitleBar.dart +++ b/lib/components/Shared/PageWindowTitleBar.dart @@ -1,5 +1,6 @@ import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:flutter/material.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/utils/platform.dart'; class TitleBarActionButtons extends StatelessWidget { @@ -26,7 +27,7 @@ class TitleBarActionButtons extends StatelessWidget { data: const IconThemeData(size: 16), child: Row( children: [ - TextButton( + PlatformTextButton( onPressed: () { appWindow.minimize(); }, @@ -38,7 +39,7 @@ class TitleBarActionButtons extends StatelessWidget { Icons.minimize_rounded, color: color, )), - TextButton( + PlatformTextButton( onPressed: () async { appWindow.maximizeOrRestore(); }, @@ -50,7 +51,7 @@ class TitleBarActionButtons extends StatelessWidget { Icons.crop_square_rounded, color: color, )), - TextButton( + PlatformTextButton( onPressed: () { appWindow.close(); }, diff --git a/lib/components/Shared/ReplaceDownloadedFileDialog.dart b/lib/components/Shared/ReplaceDownloadedFileDialog.dart index 6b6591b7..ec472b8d 100644 --- a/lib/components/Shared/ReplaceDownloadedFileDialog.dart +++ b/lib/components/Shared/ReplaceDownloadedFileDialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; final replaceDownloadedFileState = StateProvider((ref) => null); @@ -48,13 +49,13 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget { ], ), actions: [ - TextButton( + PlatformTextButton( child: const Text("No"), onPressed: () { Navigator.pop(context, false); }, ), - TextButton( + PlatformTextButton( child: const Text("Yes"), onPressed: () { Navigator.pop(context, true); diff --git a/lib/components/Shared/TrackCollectionView.dart b/lib/components/Shared/TrackCollectionView.dart index 85e75266..f89d750a 100644 --- a/lib/components/Shared/TrackCollectionView.dart +++ b/lib/components/Shared/TrackCollectionView.dart @@ -2,6 +2,7 @@ import 'package:fl_query/fl_query.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerTrackTile.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/components/Shared/TracksTableView.dart'; @@ -59,7 +60,7 @@ class TrackCollectionView extends HookConsumerWidget { final List buttons = [ if (showShare) - IconButton( + PlatformIconButton( icon: Icon( Icons.share_rounded, color: color?.titleTextColor, @@ -71,13 +72,9 @@ class TrackCollectionView extends HookConsumerWidget { // play playlist Container( margin: const EdgeInsets.symmetric(vertical: 10), - child: ElevatedButton( + child: PlatformFilledButton( style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all(Theme.of(context).primaryColor), - shape: MaterialStateProperty.all( - const CircleBorder(), - ), + shape: MaterialStateProperty.all(const CircleBorder()), ), onPressed: tracksSnapshot.data != null ? onPlay : null, child: Icon( @@ -112,7 +109,7 @@ class TrackCollectionView extends HookConsumerWidget { }, [collapsed.value]); return SafeArea( - child: Scaffold( + child: PlatformScaffold( appBar: kIsDesktop ? PageWindowTitleBar( backgroundColor: color?.color, diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index 88f6bd80..b3c18542 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart' hide Action; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart' hide Image; import 'package:spotube/components/Shared/AdaptivePopupMenuButton.dart'; import 'package:spotube/components/Shared/HeartButton.dart'; @@ -105,11 +106,11 @@ class TrackTile extends HookConsumerWidget { fontWeight: FontWeight.bold, ), actions: [ - TextButton( + PlatformTextButton( child: const Text("Cancel"), onPressed: () => Navigator.pop(context), ), - ElevatedButton( + PlatformFilledButton( child: const Text("Add"), onPressed: () async { final selectedPlaylists = playlistsCheck @@ -214,7 +215,7 @@ class TrackTile extends HookConsumerWidget { ), ), ), - IconButton( + PlatformIconButton( icon: Icon( playback.track?.id != null && playback.track?.id == track.value.id diff --git a/lib/components/Shared/TracksTableView.dart b/lib/components/Shared/TracksTableView.dart index 60bbd55a..21210440 100644 --- a/lib/components/Shared/TracksTableView.dart +++ b/lib/components/Shared/TracksTableView.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Library/UserLocalTracks.dart'; import 'package:spotube/components/Shared/DownloadConfirmationDialog.dart'; @@ -126,23 +127,21 @@ class TracksTableView extends HookConsumerWidget { .state = value; }, ), - PopupMenuButton( - itemBuilder: (context) { - return [ - PopupMenuItem( - enabled: selected.value.isNotEmpty, - value: "download", - child: Row( - children: [ - const Icon(Icons.file_download_outlined), - Text( - "Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}", - ), - ], - ), + PlatformPopupMenuButton( + items: [ + PlatformPopupMenuItem( + enabled: selected.value.isNotEmpty, + value: "download", + child: Row( + children: [ + const Icon(Icons.file_download_outlined), + Text( + "Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}", + ), + ], ), - ]; - }, + ), + ], onSelected: (action) async { switch (action) { case "download": @@ -163,6 +162,7 @@ class TracksTableView extends HookConsumerWidget { default: } }, + child: const Icon(Icons.more_vert), ), ], ), diff --git a/lib/hooks/useUpdateChecker.dart b/lib/hooks/useUpdateChecker.dart index 672bcb4b..f0648a55 100644 --- a/lib/hooks/useUpdateChecker.dart +++ b/lib/hooks/useUpdateChecker.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/AnchorButton.dart'; import 'package:spotube/hooks/usePackageInfo.dart'; import 'package:spotube/provider/UserPreferences.dart'; @@ -59,7 +60,7 @@ void useUpdateChecker(WidgetRef ref) { return AlertDialog( title: const Text("Spotube has an update"), actions: [ - ElevatedButton( + PlatformFilledButton( child: const Text("Download Now"), onPressed: () => download(url), ), diff --git a/lib/main.dart b/lib/main.dart index c26eff73..e4124d48 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -199,7 +199,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.macOS; + platform = TargetPlatform.windows; return PlatformApp.router( routeInformationParser: router.routeInformationParser, From 512446dcab72aa1d7bce18e5a5793f6be8f30fcb Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 29 Oct 2022 19:36:04 +0600 Subject: [PATCH 03/23] feat: Player and Playbutton theme respect to platform --- lib/components/Home/Genres.dart | 4 +- lib/components/Player/Player.dart | 44 ++++- lib/components/Settings/Settings.dart | 14 +- lib/components/Shared/PageWindowTitleBar.dart | 6 +- lib/components/Shared/PlaybuttonCard.dart | 178 +++++++++++++----- lib/hooks/usePlatformProperty.dart | 10 + lib/main.dart | 9 +- lib/themes/light-theme.dart | 27 +++ pubspec.lock | 4 +- pubspec.yaml | 2 + 10 files changed, 240 insertions(+), 58 deletions(-) create mode 100644 lib/hooks/usePlatformProperty.dart diff --git a/lib/components/Home/Genres.dart b/lib/components/Home/Genres.dart index 4b85d4d9..0f614f1b 100644 --- a/lib/components/Home/Genres.dart +++ b/lib/components/Home/Genres.dart @@ -2,6 +2,7 @@ import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Category/CategoryCard.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerCategories.dart'; @@ -40,7 +41,8 @@ class Genres extends HookConsumerWidget { .toList() ]; - return Scaffold( + return PlatformScaffold( + backgroundColor: PlatformProperty.all(Colors.transparent), body: ListView.builder( itemCount: categories.length, itemBuilder: (context, index) { diff --git a/lib/components/Player/Player.dart b/lib/components/Player/Player.dart index f012d9de..1f09f57b 100644 --- a/lib/components/Player/Player.dart +++ b/lib/components/Player/Player.dart @@ -1,11 +1,16 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:macos_ui/macos_ui.dart'; +import 'package:fluent_ui/fluent_ui.dart' as FluentUI; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Player/PlayerActions.dart'; import 'package:spotube/components/Player/PlayerOverlay.dart'; import 'package:spotube/components/Player/PlayerTrackDetails.dart'; import 'package:spotube/components/Player/PlayerControls.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; +import 'package:spotube/hooks/usePlatformProperty.dart'; import 'package:spotube/models/Logger.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:flutter/material.dart'; @@ -46,8 +51,43 @@ class Player extends HookConsumerWidget { ); } - return Container( - color: Theme.of(context).backgroundColor, + final backgroundColor = usePlatformProperty( + (context) => PlatformProperty( + android: Theme.of(context).backgroundColor, + ios: CupertinoTheme.of(context).scaffoldBackgroundColor, + macos: MacosTheme.of(context).brightness == Brightness.dark + ? Colors.grey[800] + : Colors.blueGrey[50], + linux: Theme.of(context).backgroundColor, + windows: FluentUI.FluentTheme.maybeOf(context)?.micaBackgroundColor, + ), + ); + + final border = usePlatformProperty( + (context) => PlatformProperty( + android: null, + ios: Border( + top: BorderSide( + color: CupertinoTheme.of(context).barBackgroundColor, + width: 1, + ), + ), + macos: Border( + top: BorderSide( + color: MacosTheme.of(context).dividerColor, + width: 1, + ), + ), + linux: null, + windows: null, + ), + ); + + return DecoratedBox( + decoration: BoxDecoration( + color: backgroundColor, + border: border, + ), child: Material( type: MaterialType.transparency, child: Row( diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index bcec8e46..d8fde026 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:macos_ui/macos_ui.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Settings/About.dart'; import 'package:spotube/components/Settings/ColorSchemePickerDialog.dart'; @@ -467,10 +468,13 @@ class Settings extends HookConsumerWidget { ), ), trailing: (context, update) => PlatformFilledButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red[100], - foregroundColor: Colors.pinkAccent, - padding: const EdgeInsets.all(15), + style: ButtonStyle( + backgroundColor: + MaterialStatePropertyAll(Colors.red[100]), + foregroundColor: + const MaterialStatePropertyAll(Colors.pinkAccent), + padding: const MaterialStatePropertyAll( + EdgeInsets.all(15)), ), onPressed: () { launchUrlString( @@ -479,8 +483,10 @@ class Settings extends HookConsumerWidget { ); }, child: Row( + mainAxisSize: MainAxisSize.min, children: const [ Icon(Icons.favorite_outline_rounded), + SizedBox(width: 5), Text("Please Sponsor/Donate"), ], ), diff --git a/lib/components/Shared/PageWindowTitleBar.dart b/lib/components/Shared/PageWindowTitleBar.dart index 3ef24250..1d3afe4a 100644 --- a/lib/components/Shared/PageWindowTitleBar.dart +++ b/lib/components/Shared/PageWindowTitleBar.dart @@ -27,7 +27,7 @@ class TitleBarActionButtons extends StatelessWidget { data: const IconThemeData(size: 16), child: Row( children: [ - PlatformTextButton( + TextButton( onPressed: () { appWindow.minimize(); }, @@ -39,7 +39,7 @@ class TitleBarActionButtons extends StatelessWidget { Icons.minimize_rounded, color: color, )), - PlatformTextButton( + TextButton( onPressed: () async { appWindow.maximizeOrRestore(); }, @@ -51,7 +51,7 @@ class TitleBarActionButtons extends StatelessWidget { Icons.crop_square_rounded, color: color, )), - PlatformTextButton( + TextButton( onPressed: () { appWindow.close(); }, diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index c523e246..3c21c581 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -1,10 +1,15 @@ +import 'package:fluent_ui/fluent_ui.dart' as FluentUI; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:macos_ui/macos_ui.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/HoverBuilder.dart'; import 'package:spotube/components/Shared/SpotubeMarqueeText.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; +import 'package:spotube/hooks/usePlatformProperty.dart'; -class PlaybuttonCard extends StatelessWidget { +class PlaybuttonCard extends HookWidget { final void Function()? onTap; final void Function()? onPlaybuttonPressed; final String? description; @@ -27,26 +32,109 @@ class PlaybuttonCard extends StatelessWidget { @override Widget build(BuildContext context) { + final backgroundColor = usePlatformProperty( + (context) => PlatformProperty( + android: Theme.of(context).backgroundColor, + ios: CupertinoTheme.of(context).scaffoldBackgroundColor, + macos: MacosTheme.of(context).brightness == Brightness.dark + ? Colors.grey[800] + : Colors.blueGrey[50], + linux: Theme.of(context).backgroundColor, + windows: FluentUI.FluentTheme.maybeOf(context)?.scaffoldBackgroundColor, + ), + ); + + final boxShadow = usePlatformProperty( + (context) => PlatformProperty( + android: BoxShadow( + blurRadius: 10, + offset: const Offset(0, 3), + spreadRadius: 5, + color: Theme.of(context).shadowColor, + ), + ios: null, + macos: null, + linux: BoxShadow( + blurRadius: 10, + offset: const Offset(0, 3), + spreadRadius: 5, + color: Theme.of(context).shadowColor, + ), + windows: null, + ), + ); + + final titleStyle = usePlatformProperty( + (context) => PlatformProperty( + android: Theme.of(context).textTheme.bodyMedium, + ios: CupertinoTheme.of(context).textTheme.textStyle, + macos: MacosTheme.of(context).typography.body, + linux: Theme.of(context).textTheme.bodyMedium, + windows: FluentUI.FluentTheme.maybeOf(context)?.typography.body, + ), + ); + + final descriptionStyle = usePlatformProperty( + (context) => PlatformProperty( + android: Theme.of(context).textTheme.caption, + ios: CupertinoTheme.of(context) + .textTheme + .textStyle + .copyWith(fontSize: 13), + macos: MacosTheme.of(context).typography.caption1, + linux: Theme.of(context).textTheme.caption, + windows: FluentUI.FluentTheme.maybeOf(context)?.typography.caption, + ), + ); + + final splash = usePlatformProperty( + (context) => PlatformProperty.multiPlatformGroup({ + InkRipple.splashFactory: {TargetPlatform.android, TargetPlatform.linux}, + NoSplash.splashFactory: { + TargetPlatform.windows, + TargetPlatform.macOS, + TargetPlatform.iOS, + } + }), + ); + + final iconBgColor = usePlatformProperty( + (context) => PlatformProperty( + android: Theme.of(context).primaryColor, + ios: CupertinoTheme.of(context).primaryColor, + macos: MacosTheme.of(context).primaryColor, + linux: Theme.of(context).primaryColor, + windows: FluentUI.FluentTheme.maybeOf(context)?.accentColor, + ), + ); + return Container( margin: margin, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), + splashFactory: splash, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 200), child: HoverBuilder(builder: (context, isHovering) { return Ink( decoration: BoxDecoration( - color: Theme.of(context).backgroundColor, - borderRadius: BorderRadius.circular(8), + color: backgroundColor, + borderRadius: BorderRadius.circular( + platform == TargetPlatform.windows ? 5 : 8, + ), boxShadow: [ - BoxShadow( - blurRadius: 10, - offset: const Offset(0, 3), - spreadRadius: 5, - color: Theme.of(context).shadowColor, - ) + if (boxShadow != null) boxShadow, ], + border: platform == TargetPlatform.windows + ? Border.all( + color: FluentUI.FluentTheme.maybeOf(context) + ?.micaBackgroundColor + .withOpacity(.7) ?? + Colors.transparent, + width: 1, + ) + : null, ), child: Column( crossAxisAlignment: CrossAxisAlignment.center, @@ -54,13 +142,20 @@ class PlaybuttonCard extends StatelessWidget { // thumbnail of the playlist Stack( children: [ - ClipRRect( - borderRadius: BorderRadius.circular(8), - child: UniversalImage( - path: imageUrl, - width: 200, - placeholder: (context, url) => - Image.asset("assets/placeholder.png"), + Padding( + padding: EdgeInsets.all( + platform == TargetPlatform.windows ? 5 : 0, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular( + platform == TargetPlatform.windows ? 5 : 8, + ), + child: UniversalImage( + path: imageUrl, + width: 200, + placeholder: (context, url) => + Image.asset("assets/placeholder.png"), + ), ), ), Positioned.directional( @@ -68,27 +163,26 @@ class PlaybuttonCard extends StatelessWidget { bottom: 10, end: 5, child: Builder(builder: (context) { - return PlatformFilledButton( - onPressed: onPlaybuttonPressed, - style: ButtonStyle( - shape: MaterialStateProperty.all( - const CircleBorder(), - ), - padding: MaterialStateProperty.all( - const EdgeInsets.all(16), - ), + return Container( + decoration: BoxDecoration( + color: iconBgColor, + shape: BoxShape.circle, + ), + child: PlatformIconButton( + onPressed: onPlaybuttonPressed, + icon: isLoading + ? const SizedBox( + height: 23, + width: 23, + child: CircularProgressIndicator(), + ) + : Icon( + isPlaying + ? Icons.pause_rounded + : Icons.play_arrow_rounded, + color: backgroundColor, + ), ), - child: isLoading - ? const SizedBox( - height: 23, - width: 23, - child: CircularProgressIndicator(), - ) - : Icon( - isPlaying - ? Icons.pause_rounded - : Icons.play_arrow_rounded, - ), ); }), ) @@ -106,8 +200,8 @@ class PlaybuttonCard extends StatelessWidget { height: 20, child: SpotubeMarqueeText( text: title, - style: - const TextStyle(fontWeight: FontWeight.bold), + style: titleStyle?.copyWith( + fontWeight: FontWeight.bold), isHovering: isHovering, ), ), @@ -118,13 +212,7 @@ class PlaybuttonCard extends StatelessWidget { height: 30, child: SpotubeMarqueeText( text: description!, - style: TextStyle( - fontSize: 13, - color: Theme.of(context) - .textTheme - .headline4 - ?.color, - ), + style: descriptionStyle, isHovering: isHovering, ), ), diff --git a/lib/hooks/usePlatformProperty.dart b/lib/hooks/usePlatformProperty.dart new file mode 100644 index 00000000..d074d574 --- /dev/null +++ b/lib/hooks/usePlatformProperty.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:platform_ui/platform_ui.dart'; + +T usePlatformProperty( + PlatformProperty Function(BuildContext context) getProperties) { + final context = useContext(); + + return getProperties(context).resolve(platform ?? Theme.of(context).platform); +} diff --git a/lib/main.dart b/lib/main.dart index e4124d48..cdc1260e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,11 +3,13 @@ import 'dart:convert'; import 'package:audio_service/audio_service.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:fl_query/fl_query.dart'; +import 'package:fluent_ui/fluent_ui.dart' as FluentUI; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:macos_ui/macos_ui.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotube/components/Shared/ReplaceDownloadedFileDialog.dart'; @@ -199,7 +201,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.windows; + platform = TargetPlatform.macOS; return PlatformApp.router( routeInformationParser: router.routeInformationParser, @@ -215,6 +217,11 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { accentMaterialColor: accentMaterialColor, backgroundMaterialColor: backgroundMaterialColor, ), + iosTheme: iosTheme, + windowsTheme: windowsTheme, + windowsDarkTheme: windowsDarkTheme, + macosTheme: macosTheme, + macosDarkTheme: macosDarkTheme, themeMode: themeMode, shortcuts: PlatformProperty.all({ ...WidgetsApp.defaultShortcuts.map((key, value) { diff --git a/lib/themes/light-theme.dart b/lib/themes/light-theme.dart index 114a99ca..030fafd8 100644 --- a/lib/themes/light-theme.dart +++ b/lib/themes/light-theme.dart @@ -1,5 +1,8 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:macos_ui/macos_ui.dart'; import 'package:spotube/extensions/ShimmerColorTheme.dart'; +import 'package:fluent_ui/fluent_ui.dart' as FluentUI; final materialWhite = MaterialColor(Colors.white.value, { 50: Colors.white, @@ -115,3 +118,27 @@ ThemeData lightTheme({ ), ); } + +final windowsTheme = FluentUI.ThemeData.light().copyWith( + buttonTheme: FluentUI.ButtonThemeData( + iconButtonStyle: FluentUI.ButtonStyle( + iconSize: FluentUI.ButtonState.all(20), + ), + ), +); +final windowsDarkTheme = FluentUI.ThemeData.dark().copyWith( + buttonTheme: FluentUI.ButtonThemeData( + iconButtonStyle: FluentUI.ButtonStyle( + iconSize: FluentUI.ButtonState.all(20), + ), + ), +); +final macosTheme = MacosThemeData.light().copyWith( + pushButtonTheme: PushButtonThemeData( + secondaryColor: Colors.white, + ), + iconTheme: MacosIconThemeData(size: 16), + iconButtonTheme: MacosIconButtonThemeData(), +); +final macosDarkTheme = MacosThemeData.dark(); +final iosTheme = CupertinoThemeData(); diff --git a/pubspec.lock b/pubspec.lock index 280f5c49..b9dfa5e2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -494,7 +494,7 @@ packages: source: hosted version: "0.3.1" fluent_ui: - dependency: transitive + dependency: "direct main" description: name: fluent_ui url: "https://pub.dartlang.org" @@ -759,7 +759,7 @@ packages: source: hosted version: "1.0.2" macos_ui: - dependency: transitive + dependency: "direct main" description: name: macos_ui url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index db52cc89..98cb2795 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,8 @@ dependencies: uuid: ^3.0.6 platform_ui: path: ../platform_ui + fluent_ui: ^4.0.3 + macos_ui: ^1.7.5 dev_dependencies: flutter_test: From 46b00bafdf71a4add61cf50168a32198c3293181 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 31 Oct 2022 23:28:10 +0600 Subject: [PATCH 04/23] feat: platform slider and progress indicator integration --- lib/components/Artist/ArtistProfile.dart | 7 +-- lib/components/Home/Sidebar.dart | 11 +++-- lib/components/Library/UserDownloads.dart | 2 +- lib/components/Player/Player.dart | 6 +-- lib/components/Player/PlayerControls.dart | 4 +- .../Playlist/PlaylistGenreView.dart | 3 +- lib/components/Search/Search.dart | 10 ++-- lib/components/Shared/HeartButton.dart | 8 ++-- lib/components/Shared/PlaybuttonCard.dart | 48 +++---------------- lib/components/Shared/TrackTile.dart | 2 +- lib/main.dart | 2 +- lib/themes/light-theme.dart | 5 +- 12 files changed, 40 insertions(+), 68 deletions(-) diff --git a/lib/components/Artist/ArtistProfile.dart b/lib/components/Artist/ArtistProfile.dart index 5d1e288f..556ca6fa 100644 --- a/lib/components/Artist/ArtistProfile.dart +++ b/lib/components/Artist/ArtistProfile.dart @@ -139,7 +139,8 @@ class ArtistProfile extends HookConsumerWidget { return const SizedBox( height: 20, width: 20, - child: CircularProgressIndicator(), + child: + PlatformCircularProgressIndicator(), ); } @@ -214,7 +215,7 @@ class ArtistProfile extends HookConsumerWidget { ); if (topTracksQuery.isLoading || !topTracksQuery.hasData) { - return const CircularProgressIndicator.adaptive(); + return const PlatformCircularProgressIndicator(); } else if (topTracksQuery.hasError) { return Center( child: Text(topTracksQuery.error.toString()), @@ -313,7 +314,7 @@ class ArtistProfile extends HookConsumerWidget { ); if (relatedArtists.isLoading || !relatedArtists.hasData) { - return const CircularProgressIndicator.adaptive(); + return const PlatformCircularProgressIndicator(); } else if (relatedArtists.hasError) { return Center( child: Text(relatedArtists.error.toString()), diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart index 1d42c49e..f31acde4 100644 --- a/lib/components/Home/Sidebar.dart +++ b/lib/components/Home/Sidebar.dart @@ -141,7 +141,7 @@ class Sidebar extends HookConsumerWidget { ), Text( "Spotube", - style: Theme.of(context).textTheme.headline4, + style: PlatformTheme.of(context).textTheme?.headline, ), PlatformIconButton( icon: const Icon(Icons.menu_rounded), @@ -207,7 +207,7 @@ class SidebarFooter extends HookConsumerWidget { children: [ if (auth.isLoggedIn && data == null) const Center( - child: CircularProgressIndicator(), + child: PlatformCircularProgressIndicator(), ) else if (data != null) Flexible( @@ -233,9 +233,10 @@ class SidebarFooter extends HookConsumerWidget { maxLines: 1, softWrap: false, overflow: TextOverflow.fade, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), + style: PlatformTheme.of(context) + .textTheme + ?.body + ?.copyWith(fontWeight: FontWeight.bold), ), ), ], diff --git a/lib/components/Library/UserDownloads.dart b/lib/components/Library/UserDownloads.dart index 4ceaf3d5..340d8071 100644 --- a/lib/components/Library/UserDownloads.dart +++ b/lib/components/Library/UserDownloads.dart @@ -67,7 +67,7 @@ class UserDownloads extends HookConsumerWidget { trailing: const SizedBox( width: 30, height: 30, - child: CircularProgressIndicator.adaptive(), + child: PlatformCircularProgressIndicator(), ), subtitle: Text( TypeConversionUtils.artists_X_String( diff --git a/lib/components/Player/Player.dart b/lib/components/Player/Player.dart index 1f09f57b..37e35c24 100644 --- a/lib/components/Player/Player.dart +++ b/lib/components/Player/Player.dart @@ -68,13 +68,13 @@ class Player extends HookConsumerWidget { android: null, ios: Border( top: BorderSide( - color: CupertinoTheme.of(context).barBackgroundColor, + color: PlatformTheme.of(context).borderColor ?? Colors.transparent, width: 1, ), ), macos: Border( top: BorderSide( - color: MacosTheme.of(context).dividerColor, + color: PlatformTheme.of(context).borderColor ?? Colors.transparent, width: 1, ), ), @@ -130,7 +130,7 @@ class Player extends HookConsumerWidget { } } }, - child: Slider.adaptive( + child: PlatformSlider( min: 0, max: 1, value: volume.value, diff --git a/lib/components/Player/PlayerControls.dart b/lib/components/Player/PlayerControls.dart index 381efac2..0bfc8818 100644 --- a/lib/components/Player/PlayerControls.dart +++ b/lib/components/Player/PlayerControls.dart @@ -97,7 +97,7 @@ class PlayerControls extends HookConsumerWidget { children: [ Tooltip( message: "Slide to seek forward or backward", - child: Slider.adaptive( + child: PlatformSlider( focusNode: FocusNode(), // cannot divide by zero // there's an edge case for value being bigger @@ -174,7 +174,7 @@ class PlayerControls extends HookConsumerWidget { ? const SizedBox( height: 20, width: 20, - child: CircularProgressIndicator(), + child: PlatformCircularProgressIndicator(), ) : Icon( playback.isPlaying diff --git a/lib/components/Playlist/PlaylistGenreView.dart b/lib/components/Playlist/PlaylistGenreView.dart index aa519f2c..a9191b6a 100644 --- a/lib/components/Playlist/PlaylistGenreView.dart +++ b/lib/components/Playlist/PlaylistGenreView.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Playlist/PlaylistCard.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; @@ -47,7 +48,7 @@ class PlaylistGenreView extends ConsumerWidget { return const Center(child: Text("Error occurred")); } if (!snapshot.hasData) { - return const CircularProgressIndicator.adaptive(); + return const PlatformCircularProgressIndicator(); } return Center( child: Wrap( diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index d31539a4..39982108 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -143,7 +143,7 @@ class Search extends HookConsumerWidget { ), if (searchTrack.isLoading && !searchTrack.isFetchingNextPage) - const CircularProgressIndicator() + const PlatformCircularProgressIndicator() else if (searchTrack.hasError) Text( searchTrack.error?[searchTrack.pageParams.last]) @@ -190,7 +190,7 @@ class Search extends HookConsumerWidget { ? null : () => searchTrack.fetchNextPage(), child: searchTrack.isFetchingNextPage - ? const CircularProgressIndicator() + ? const PlatformCircularProgressIndicator() : const Text("Load more"), ), ), @@ -202,7 +202,7 @@ class Search extends HookConsumerWidget { const SizedBox(height: 10), if (searchPlaylist.isLoading && !searchPlaylist.isFetchingNextPage) - const CircularProgressIndicator() + const PlatformCircularProgressIndicator() else if (searchPlaylist.hasError) Text(searchPlaylist .error?[searchPlaylist.pageParams.last]) @@ -256,7 +256,7 @@ class Search extends HookConsumerWidget { const SizedBox(height: 10), if (searchArtist.isLoading && !searchArtist.isFetchingNextPage) - const CircularProgressIndicator() + const PlatformCircularProgressIndicator() else if (searchArtist.hasError) Text(searchArtist .error?[searchArtist.pageParams.last]) @@ -310,7 +310,7 @@ class Search extends HookConsumerWidget { const SizedBox(height: 10), if (searchAlbum.isLoading && !searchAlbum.isFetchingNextPage) - const CircularProgressIndicator() + const PlatformCircularProgressIndicator() else if (searchAlbum.hasError) Text( searchAlbum.error?[searchAlbum.pageParams.last]) diff --git a/lib/components/Shared/HeartButton.dart b/lib/components/Shared/HeartButton.dart index b119cb9c..6b87e6d9 100644 --- a/lib/components/Shared/HeartButton.dart +++ b/lib/components/Shared/HeartButton.dart @@ -122,7 +122,7 @@ class TrackHeartButton extends HookConsumerWidget { ); final toggler = useTrackToggleLike(track, ref); if (toggler.item3.isLoading || !toggler.item3.hasData) { - return const CircularProgressIndicator(); + return const PlatformCircularProgressIndicator(); } return HeartButton( @@ -182,7 +182,8 @@ class PlaylistHeartButton extends HookConsumerWidget { titleImage, ).dominantColor; - if (me.isLoading || !me.hasData) return const CircularProgressIndicator(); + if (me.isLoading || !me.hasData) + return const PlatformCircularProgressIndicator(); return HeartButton( isLiked: isLikedQuery.data ?? false, @@ -236,7 +237,8 @@ class AlbumHeartButton extends HookConsumerWidget { }, ); - if (me.isLoading || !me.hasData) return const CircularProgressIndicator(); + if (me.isLoading || !me.hasData) + return const PlatformCircularProgressIndicator(); return HeartButton( isLiked: isLiked, diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index 3c21c581..b86c2a92 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -32,17 +32,7 @@ class PlaybuttonCard extends HookWidget { @override Widget build(BuildContext context) { - final backgroundColor = usePlatformProperty( - (context) => PlatformProperty( - android: Theme.of(context).backgroundColor, - ios: CupertinoTheme.of(context).scaffoldBackgroundColor, - macos: MacosTheme.of(context).brightness == Brightness.dark - ? Colors.grey[800] - : Colors.blueGrey[50], - linux: Theme.of(context).backgroundColor, - windows: FluentUI.FluentTheme.maybeOf(context)?.scaffoldBackgroundColor, - ), - ); + final backgroundColor = PlatformTheme.of(context).secondaryBackgroundColor; final boxShadow = usePlatformProperty( (context) => PlatformProperty( @@ -64,28 +54,9 @@ class PlaybuttonCard extends HookWidget { ), ); - final titleStyle = usePlatformProperty( - (context) => PlatformProperty( - android: Theme.of(context).textTheme.bodyMedium, - ios: CupertinoTheme.of(context).textTheme.textStyle, - macos: MacosTheme.of(context).typography.body, - linux: Theme.of(context).textTheme.bodyMedium, - windows: FluentUI.FluentTheme.maybeOf(context)?.typography.body, - ), - ); + final titleStyle = PlatformTextTheme.of(context).body; - final descriptionStyle = usePlatformProperty( - (context) => PlatformProperty( - android: Theme.of(context).textTheme.caption, - ios: CupertinoTheme.of(context) - .textTheme - .textStyle - .copyWith(fontSize: 13), - macos: MacosTheme.of(context).typography.caption1, - linux: Theme.of(context).textTheme.caption, - windows: FluentUI.FluentTheme.maybeOf(context)?.typography.caption, - ), - ); + final descriptionStyle = PlatformTextTheme.of(context).caption; final splash = usePlatformProperty( (context) => PlatformProperty.multiPlatformGroup({ @@ -98,15 +69,7 @@ class PlaybuttonCard extends HookWidget { }), ); - final iconBgColor = usePlatformProperty( - (context) => PlatformProperty( - android: Theme.of(context).primaryColor, - ios: CupertinoTheme.of(context).primaryColor, - macos: MacosTheme.of(context).primaryColor, - linux: Theme.of(context).primaryColor, - windows: FluentUI.FluentTheme.maybeOf(context)?.accentColor, - ), - ); + final iconBgColor = PlatformTheme.of(context).primaryColor; return Container( margin: margin, @@ -174,7 +137,8 @@ class PlaybuttonCard extends HookWidget { ? const SizedBox( height: 23, width: 23, - child: CircularProgressIndicator(), + child: + PlatformCircularProgressIndicator(), ) : Icon( isPlaying diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index b3c18542..4605fd0b 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -132,7 +132,7 @@ class TrackTile extends HookConsumerWidget { width: 300, child: !snapshot.hasData ? const Center( - child: CircularProgressIndicator.adaptive()) + child: PlatformCircularProgressIndicator()) : ListView.builder( shrinkWrap: true, itemCount: snapshot.data!.length, diff --git a/lib/main.dart b/lib/main.dart index cdc1260e..688cf9c2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -201,7 +201,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.macOS; + platform = TargetPlatform.windows; return PlatformApp.router( routeInformationParser: router.routeInformationParser, diff --git a/lib/themes/light-theme.dart b/lib/themes/light-theme.dart index 030fafd8..dbf5996a 100644 --- a/lib/themes/light-theme.dart +++ b/lib/themes/light-theme.dart @@ -139,6 +139,9 @@ final macosTheme = MacosThemeData.light().copyWith( ), iconTheme: MacosIconThemeData(size: 16), iconButtonTheme: MacosIconButtonThemeData(), + typography: MacosTypography(color: Colors.green), +); +final macosDarkTheme = MacosThemeData.dark().copyWith( + typography: MacosTypography(color: Colors.red), ); -final macosDarkTheme = MacosThemeData.dark(); final iosTheme = CupertinoThemeData(); From e086b520e745e65771136cbfa842ae0693c44872 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 1 Nov 2022 13:08:06 +0600 Subject: [PATCH 05/23] fix(macos): white text color in dark mode, text field white background --- lib/components/Artist/ArtistCard.dart | 74 ++++++++-- lib/components/Artist/ArtistProfile.dart | 54 +++---- lib/components/Category/CategoryCard.dart | 9 +- lib/components/Home/Genres.dart | 1 - lib/components/Library/UserAlbums.dart | 26 ++-- lib/components/Library/UserArtists.dart | 43 +++--- lib/components/Library/UserDownloads.dart | 5 +- lib/components/Library/UserPlaylists.dart | 30 ++-- lib/components/Player/PlayerControls.dart | 6 +- lib/components/Player/PlayerTrackDetails.dart | 15 +- lib/components/Search/Search.dart | 31 ++-- lib/components/Settings/About.dart | 5 +- lib/components/Settings/Settings.dart | 139 +++++++++--------- .../Shared/AdaptivePopupMenuButton.dart | 18 +-- lib/components/Shared/AnchorButton.dart | 3 +- lib/components/Shared/PageWindowTitleBar.dart | 6 +- lib/components/Shared/PlaybuttonCard.dart | 8 +- .../Shared/TrackCollectionView.dart | 36 ++--- lib/components/Shared/TrackTile.dart | 30 ++-- lib/components/Shared/TracksTableView.dart | 10 +- lib/main.dart | 2 +- lib/themes/light-theme.dart | 13 +- 22 files changed, 311 insertions(+), 253 deletions(-) diff --git a/lib/components/Artist/ArtistCard.dart b/lib/components/Artist/ArtistCard.dart index 8619fee1..aec4b3e7 100644 --- a/lib/components/Artist/ArtistCard.dart +++ b/lib/components/Artist/ArtistCard.dart @@ -1,12 +1,16 @@ import 'package:auto_size_text/auto_size_text.dart'; +import 'package:fluent_ui/fluent_ui.dart' hide Colors; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Shared/HoverBuilder.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; +import 'package:spotube/hooks/usePlatformProperty.dart'; import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; -class ArtistCard extends StatelessWidget { +class ArtistCard extends HookWidget { final Artist artist; const ArtistCard(this.artist, {Key? key}) : super(key: key); @@ -18,28 +22,76 @@ class ArtistCard extends StatelessWidget { placeholder: ImagePlaceholder.artist, ), ); + final boxShadow = usePlatformProperty( + (context) => PlatformProperty( + android: BoxShadow( + blurRadius: 10, + offset: const Offset(0, 3), + spreadRadius: 5, + color: Theme.of(context).shadowColor, + ), + ios: null, + macos: null, + linux: BoxShadow( + blurRadius: 10, + offset: const Offset(0, 3), + spreadRadius: 5, + color: Theme.of(context).shadowColor, + ), + windows: null, + ), + ); + + final splash = usePlatformProperty( + (context) => PlatformProperty.multiPlatformGroup({ + InkRipple.splashFactory: {TargetPlatform.android, TargetPlatform.linux}, + NoSplash.splashFactory: { + TargetPlatform.windows, + TargetPlatform.macOS, + TargetPlatform.iOS, + } + }), + ); + return SizedBox( height: 240, width: 200, child: InkWell( + splashFactory: splash, onTap: () { ServiceUtils.navigate(context, "/artist/${artist.id}"); }, - borderRadius: BorderRadius.circular(10), + customBorder: platform == TargetPlatform.windows + ? Border.all( + color: FluentTheme.maybeOf(context) + ?.micaBackgroundColor + .withOpacity(.7) ?? + Colors.transparent, + width: 1, + ) + : null, + borderRadius: BorderRadius.circular( + platform == TargetPlatform.windows ? 5 : 8, + ), child: HoverBuilder(builder: (context, isHovering) { return Ink( width: 200, decoration: BoxDecoration( - color: Theme.of(context).backgroundColor, - borderRadius: BorderRadius.circular(8), + color: PlatformTheme.of(context).secondaryBackgroundColor, + borderRadius: BorderRadius.circular( + platform == TargetPlatform.windows ? 5 : 8, + ), boxShadow: [ - BoxShadow( - blurRadius: 10, - offset: const Offset(0, 3), - spreadRadius: 5, - color: Theme.of(context).shadowColor, - ) + if (boxShadow != null) boxShadow, ], + border: [TargetPlatform.windows, TargetPlatform.macOS] + .contains(platform) + ? Border.all( + color: PlatformTheme.of(context).borderColor ?? + Colors.transparent, + width: 1, + ) + : null, ), child: Padding( padding: const EdgeInsets.all(15), @@ -79,7 +131,7 @@ class ArtistCard extends StatelessWidget { artist.name!, maxLines: 2, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyLarge!.copyWith( + style: PlatformTextTheme.of(context).body?.copyWith( fontWeight: FontWeight.bold, ), ), diff --git a/lib/components/Artist/ArtistProfile.dart b/lib/components/Artist/ArtistProfile.dart index 556ca6fa..b16d011a 100644 --- a/lib/components/Artist/ArtistProfile.dart +++ b/lib/components/Artist/ArtistProfile.dart @@ -31,13 +31,13 @@ class ArtistProfile extends HookConsumerWidget { Widget build(BuildContext context, ref) { SpotifyApi spotify = ref.watch(spotifyProvider); final parentScrollController = useScrollController(); - final textTheme = Theme.of(context).textTheme; + final textTheme = PlatformTheme.of(context).textTheme; final chipTextVariant = useBreakpointValue( - sm: textTheme.bodySmall, - md: textTheme.bodyMedium, - lg: textTheme.headline6, - xl: textTheme.headline6, - xxl: textTheme.headline6, + sm: textTheme!.caption, + md: textTheme.body, + lg: textTheme.subheading, + xl: textTheme.headline, + xxl: textTheme.headline, ); final avatarWidth = useBreakpointValue( @@ -53,7 +53,7 @@ class ArtistProfile extends HookConsumerWidget { final Playback playback = ref.watch(playbackProvider); return SafeArea( - child: Scaffold( + child: PlatformScaffold( appBar: const PageWindowTitleBar( leading: BackButton(), ), @@ -68,7 +68,7 @@ class ArtistProfile extends HookConsumerWidget { return const ShimmerArtistProfile(); } else if (artistsQuery.hasError) { return Center( - child: Text(artistsQuery.error.toString()), + child: PlatformText(artistsQuery.error.toString()), ); } @@ -106,21 +106,22 @@ class ArtistProfile extends HookConsumerWidget { decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(50)), - child: Text(data.type!.toUpperCase(), + child: PlatformText(data.type!.toUpperCase(), style: chipTextVariant?.copyWith( color: Colors.white)), ), - Text( + PlatformText( data.name!, style: breakpoint.isSm - ? textTheme.headline4 - : textTheme.headline2, + ? textTheme.subheading + : textTheme.headline, ), - Text( + PlatformText( "${PrimitiveUtils.toReadableNumber(data.followers!.total!.toDouble())} followers", style: breakpoint.isSm - ? textTheme.bodyText1 - : textTheme.headline5, + ? textTheme.body + : textTheme.body + ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 20), Row( @@ -144,7 +145,7 @@ class ArtistProfile extends HookConsumerWidget { ); } - return OutlinedButton( + return PlatformFilledButton( onPressed: () async { try { isFollowingQuery.data! @@ -170,7 +171,7 @@ class ArtistProfile extends HookConsumerWidget { ]); } }, - child: Text( + child: PlatformText( isFollowingQuery.data! ? "Following" : "Follow", @@ -190,7 +191,7 @@ class ArtistProfile extends HookConsumerWidget { const SnackBar( width: 300, behavior: SnackBarBehavior.floating, - content: Text( + content: PlatformText( "Artist URL copied to clipboard", textAlign: TextAlign.center, ), @@ -218,7 +219,7 @@ class ArtistProfile extends HookConsumerWidget { return const PlatformCircularProgressIndicator(); } else if (topTracksQuery.hasError) { return Center( - child: Text(topTracksQuery.error.toString()), + child: PlatformText(topTracksQuery.error.toString()), ); } @@ -252,9 +253,10 @@ class ArtistProfile extends HookConsumerWidget { return Column(children: [ Row( children: [ - Text( + PlatformText( "Top Tracks", - style: Theme.of(context).textTheme.headline4, + style: + PlatformTheme.of(context).textTheme?.headline, ), Container( margin: const EdgeInsets.symmetric(horizontal: 5), @@ -294,16 +296,16 @@ class ArtistProfile extends HookConsumerWidget { }, ), const SizedBox(height: 50), - Text( + PlatformText( "Albums", - style: Theme.of(context).textTheme.headline4, + style: PlatformTheme.of(context).textTheme?.headline, ), const SizedBox(height: 10), ArtistAlbumList(artistId), const SizedBox(height: 20), - Text( + PlatformText( "Fans also likes", - style: Theme.of(context).textTheme.headline4, + style: PlatformTheme.of(context).textTheme?.headline, ), const SizedBox(height: 10), HookBuilder( @@ -317,7 +319,7 @@ class ArtistProfile extends HookConsumerWidget { return const PlatformCircularProgressIndicator(); } else if (relatedArtists.hasError) { return Center( - child: Text(relatedArtists.error.toString()), + child: PlatformText(relatedArtists.error.toString()), ); } diff --git a/lib/components/Category/CategoryCard.dart b/lib/components/Category/CategoryCard.dart index 55025526..c94b65f8 100644 --- a/lib/components/Category/CategoryCard.dart +++ b/lib/components/Category/CategoryCard.dart @@ -3,6 +3,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart' hide Page; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart'; import 'package:spotube/components/Playlist/PlaylistCard.dart'; @@ -46,15 +47,13 @@ class CategoryCard extends HookConsumerWidget { padding: const EdgeInsets.all(8.0), child: Row( children: [ - Text( - category.name ?? "Unknown", - style: Theme.of(context).textTheme.headline5, - ), + PlatformText.headline(category.name ?? "Unknown"), ], ), ), playlistQuery.hasError - ? Text("Something Went Wrong\n${playlistQuery.errors.first}") + ? PlatformText( + "Something Went Wrong\n${playlistQuery.errors.first}") : SizedBox( height: 245, child: ScrollConfiguration( diff --git a/lib/components/Home/Genres.dart b/lib/components/Home/Genres.dart index 0f614f1b..558a19b0 100644 --- a/lib/components/Home/Genres.dart +++ b/lib/components/Home/Genres.dart @@ -42,7 +42,6 @@ class Genres extends HookConsumerWidget { ]; return PlatformScaffold( - backgroundColor: PlatformProperty.all(Colors.transparent), body: ListView.builder( itemCount: categories.length, itemBuilder: (context, index) { diff --git a/lib/components/Library/UserAlbums.dart b/lib/components/Library/UserAlbums.dart index 520afa0b..866c1ddb 100644 --- a/lib/components/Library/UserAlbums.dart +++ b/lib/components/Library/UserAlbums.dart @@ -1,6 +1,7 @@ import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart' hide Image; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Album/AlbumCard.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart'; import 'package:spotube/provider/SpotifyDI.dart'; @@ -22,16 +23,21 @@ class UserAlbums extends HookConsumerWidget { } return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Wrap( - spacing: 20, // gap between adjacent chips - runSpacing: 20, // gap between lines - alignment: WrapAlignment.center, - children: albumsQuery.data! - .map((album) => - AlbumCard(TypeConversionUtils.simpleAlbum_X_Album(album))) - .toList(), + child: Material( + type: MaterialType.transparency, + color: PlatformTheme.of(context).scaffoldBackgroundColor, + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(8.0), + child: Wrap( + spacing: 20, // gap between adjacent chips + runSpacing: 20, // gap between lines + alignment: WrapAlignment.center, + children: albumsQuery.data! + .map((album) => + AlbumCard(TypeConversionUtils.simpleAlbum_X_Album(album))) + .toList(), + ), ), ), ); diff --git a/lib/components/Library/UserArtists.dart b/lib/components/Library/UserArtists.dart index 1641bcb7..921825de 100644 --- a/lib/components/Library/UserArtists.dart +++ b/lib/components/Library/UserArtists.dart @@ -2,6 +2,7 @@ import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Artist/ArtistCard.dart'; import 'package:spotube/components/Shared/Waypoint.dart'; @@ -28,26 +29,30 @@ class UserArtists extends HookConsumerWidget { ? false : (artistQuery.pages.last?.items?.length ?? 0) == 15; - return GridView.builder( - itemCount: artists.length, - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 200, - mainAxisExtent: 250, - crossAxisSpacing: 20, - mainAxisSpacing: 20, + return Material( + type: MaterialType.transparency, + color: PlatformTheme.of(context).scaffoldBackgroundColor, + child: GridView.builder( + itemCount: artists.length, + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 200, + mainAxisExtent: 250, + crossAxisSpacing: 20, + mainAxisSpacing: 20, + ), + padding: const EdgeInsets.all(10), + itemBuilder: (context, index) { + if (index == artists.length - 1 && hasNextPage) { + return Waypoint( + onEnter: () { + artistQuery.fetchNextPage(); + }, + child: ArtistCard(artists[index]), + ); + } + return ArtistCard(artists[index]); + }, ), - padding: const EdgeInsets.all(10), - itemBuilder: (context, index) { - if (index == artists.length - 1 && hasNextPage) { - return Waypoint( - onEnter: () { - artistQuery.fetchNextPage(); - }, - child: ArtistCard(artists[index]), - ); - } - return ArtistCard(artists[index]); - }, ); } } diff --git a/lib/components/Library/UserDownloads.dart b/lib/components/Library/UserDownloads.dart index 340d8071..bd7fb732 100644 --- a/lib/components/Library/UserDownloads.dart +++ b/lib/components/Library/UserDownloads.dart @@ -26,7 +26,7 @@ class UserDownloads extends HookConsumerWidget { child: AutoSizeText( "Currently downloading (${downloader.currentlyRunning})", maxLines: 1, - style: Theme.of(context).textTheme.headline5, + style: PlatformTextTheme.of(context).headline, ), ), const SizedBox(width: 10), @@ -38,7 +38,8 @@ class UserDownloads extends HookConsumerWidget { onPressed: downloader.currentlyRunning > 0 ? downloader.cancelAll : null, - child: const Text("Cancel All"), + macOSIsSecondary: true, + child: const PlatformText("Cancel All"), ), ], ), diff --git a/lib/components/Library/UserPlaylists.dart b/lib/components/Library/UserPlaylists.dart index 26d2bf91..705a13dd 100644 --- a/lib/components/Library/UserPlaylists.dart +++ b/lib/components/Library/UserPlaylists.dart @@ -34,19 +34,23 @@ class UserPlaylists extends HookConsumerWidget { } return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Wrap( - spacing: 20, // gap between adjacent chips - runSpacing: 20, // gap between lines - alignment: WrapAlignment.center, - children: [ - const PlaylistCreateDialog(), - PlaylistCard(likedTracksPlaylist), - ...playlistsQuery.data! - .map((playlist) => PlaylistCard(playlist)) - .toList(), - ], + child: Material( + type: MaterialType.transparency, + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(8.0), + child: Wrap( + spacing: 20, // gap between adjacent chips + runSpacing: 20, // gap between lines + alignment: WrapAlignment.center, + children: [ + const PlaylistCreateDialog(), + PlaylistCard(likedTracksPlaylist), + ...playlistsQuery.data! + .map((playlist) => PlaylistCard(playlist)) + .toList(), + ], + ), ), ), ); diff --git a/lib/components/Player/PlayerControls.dart b/lib/components/Player/PlayerControls.dart index 0bfc8818..580a346c 100644 --- a/lib/components/Player/PlayerControls.dart +++ b/lib/components/Player/PlayerControls.dart @@ -95,7 +95,7 @@ class PlayerControls extends HookConsumerWidget { return Column( children: [ - Tooltip( + PlatformTooltip( message: "Slide to seek forward or backward", child: PlatformSlider( focusNode: FocusNode(), @@ -123,10 +123,10 @@ class PlayerControls extends HookConsumerWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( + PlatformText( "$currentMinutes:$currentSeconds", ), - Text("$totalMinutes:$totalSeconds"), + PlatformText("$totalMinutes:$totalSeconds"), ], ), ), diff --git a/lib/components/Player/PlayerTrackDetails.dart b/lib/components/Player/PlayerTrackDetails.dart index 4a8a0342..9dfc079b 100644 --- a/lib/components/Player/PlayerTrackDetails.dart +++ b/lib/components/Player/PlayerTrackDetails.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; import 'package:spotube/provider/Playback.dart'; @@ -36,13 +37,10 @@ class PlayerTrackDetails extends HookConsumerWidget { ), if (breakpoint.isLessThanOrEqualTo(Breakpoints.md)) Flexible( - child: Text( + child: PlatformText( playback.track?.name ?? "Not playing", overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .bodyText1 - ?.copyWith(fontWeight: FontWeight.bold, color: color), + style: TextStyle(fontWeight: FontWeight.bold, color: color), ), ), @@ -52,13 +50,10 @@ class PlayerTrackDetails extends HookConsumerWidget { flex: 1, child: Column( children: [ - Text( + PlatformText( playback.track?.name ?? "Not playing", overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .bodyText1 - ?.copyWith(fontWeight: FontWeight.bold, color: color), + style: TextStyle(fontWeight: FontWeight.bold, color: color), ), TypeConversionUtils.artists_X_ClickableArtists( playback.track?.artists ?? [], diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index 39982108..240246a5 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -77,7 +77,7 @@ class Search extends HookConsumerWidget { return SafeArea( child: Material( - color: Theme.of(context).backgroundColor, + color: PlatformTheme.of(context).scaffoldBackgroundColor, child: Column( children: [ Container( @@ -85,7 +85,6 @@ class Search extends HookConsumerWidget { horizontal: 20, vertical: 10, ), - color: Theme.of(context).backgroundColor, child: PlatformTextField( onChanged: (value) { ref.read(searchTermStateProvider.notifier).state = value; @@ -136,16 +135,12 @@ class Search extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (tracks.isNotEmpty) - Text( - "Songs", - style: Theme.of(context).textTheme.headline5, - ), + if (tracks.isNotEmpty) PlatformText.headline("Songs"), if (searchTrack.isLoading && !searchTrack.isFetchingNextPage) const PlatformCircularProgressIndicator() else if (searchTrack.hasError) - Text( + PlatformText( searchTrack.error?[searchTrack.pageParams.last]) else ...tracks.asMap().entries.map((track) { @@ -191,20 +186,17 @@ class Search extends HookConsumerWidget { : () => searchTrack.fetchNextPage(), child: searchTrack.isFetchingNextPage ? const PlatformCircularProgressIndicator() - : const Text("Load more"), + : const PlatformText("Load more"), ), ), if (playlists.isNotEmpty) - Text( - "Playlists", - style: Theme.of(context).textTheme.headline5, - ), + PlatformText.headline("Playlists"), const SizedBox(height: 10), if (searchPlaylist.isLoading && !searchPlaylist.isFetchingNextPage) const PlatformCircularProgressIndicator() else if (searchPlaylist.hasError) - Text(searchPlaylist + PlatformText(searchPlaylist .error?[searchPlaylist.pageParams.last]) else ScrollConfiguration( @@ -249,16 +241,13 @@ class Search extends HookConsumerWidget { ), const SizedBox(height: 20), if (artists.isNotEmpty) - Text( - "Artists", - style: Theme.of(context).textTheme.headline5, - ), + PlatformText.headline("Artists"), const SizedBox(height: 10), if (searchArtist.isLoading && !searchArtist.isFetchingNextPage) const PlatformCircularProgressIndicator() else if (searchArtist.hasError) - Text(searchArtist + PlatformText(searchArtist .error?[searchArtist.pageParams.last]) else ScrollConfiguration( @@ -303,7 +292,7 @@ class Search extends HookConsumerWidget { ), const SizedBox(height: 20), if (albums.isNotEmpty) - Text( + PlatformText( "Albums", style: Theme.of(context).textTheme.headline5, ), @@ -312,7 +301,7 @@ class Search extends HookConsumerWidget { !searchAlbum.isFetchingNextPage) const PlatformCircularProgressIndicator() else if (searchAlbum.hasError) - Text( + PlatformText( searchAlbum.error?[searchAlbum.pageParams.last]) else ScrollConfiguration( diff --git a/lib/components/Settings/About.dart b/lib/components/Settings/About.dart index f243b561..0d44dfb1 100644 --- a/lib/components/Settings/About.dart +++ b/lib/components/Settings/About.dart @@ -32,7 +32,10 @@ class About extends HookWidget { return PlatformListTile( leading: const Icon(Icons.info_outline_rounded), - title: const Text("About Spotube"), + title: Text( + "About Spotube", + style: PlatformTextTheme.of(context).body, + ), onTap: () { showAboutDialog( context: context, diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index d8fde026..28a652e4 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -50,11 +50,11 @@ class Settings extends HookConsumerWidget { ); return SafeArea( - child: Scaffold( + child: PlatformScaffold( appBar: PageWindowTitleBar( - center: Text( + center: PlatformText( "Settings", - style: Theme.of(context).textTheme.headline5, + style: PlatformTheme.of(context).textTheme?.headline, ), ), body: Row( @@ -65,10 +65,11 @@ class Settings extends HookConsumerWidget { constraints: const BoxConstraints(maxWidth: 1366), child: ListView( children: [ - const Text( + PlatformText( " Account", - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + style: PlatformTextTheme.of(context) + .headline + ?.copyWith(fontWeight: FontWeight.bold), ), if (auth.isAnonymous) AdaptiveListTile( @@ -101,7 +102,8 @@ class Settings extends HookConsumerWidget { ), ), ), - child: Text("Connect with Spotify".toUpperCase()), + child: PlatformText( + "Connect with Spotify".toUpperCase()), ), ), if (auth.isLoggedIn) @@ -109,7 +111,7 @@ class Settings extends HookConsumerWidget { Auth auth = ref.watch(authProvider); return PlatformListTile( leading: const Icon(Icons.logout_rounded), - title: const SizedBox( + title: SizedBox( height: 50, width: 180, child: Align( @@ -117,6 +119,7 @@ class Settings extends HookConsumerWidget { child: AutoSizeText( "Log out of this account", maxLines: 1, + style: PlatformTextTheme.of(context).body, ), ), ), @@ -131,19 +134,20 @@ class Settings extends HookConsumerWidget { auth.logout(); GoRouter.of(context).pop(); }, - child: const Text("Logout"), + child: const PlatformText("Logout"), ), ); }), - const Text( + PlatformText( " Appearance", - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + style: PlatformTextTheme.of(context) + .headline + ?.copyWith(fontWeight: FontWeight.bold), ), AdaptiveListTile( leading: const Icon(Icons.dashboard_rounded), - title: const Text("Layout Mode"), - subtitle: const Text( + title: const PlatformText("Layout Mode"), + subtitle: const PlatformText( "Override responsive layout mode settings", ), trailing: (context, update) => @@ -152,19 +156,19 @@ class Settings extends HookConsumerWidget { items: [ PlatformDropDownMenuItem( value: LayoutMode.adaptive, - child: const Text( + child: const PlatformText( "Adaptive", ), ), PlatformDropDownMenuItem( value: LayoutMode.compact, - child: const Text( + child: const PlatformText( "Compact", ), ), PlatformDropDownMenuItem( value: LayoutMode.extended, - child: const Text("Extended"), + child: const PlatformText("Extended"), ), ], onChanged: (value) { @@ -177,26 +181,22 @@ class Settings extends HookConsumerWidget { ), AdaptiveListTile( leading: const Icon(Icons.dark_mode_outlined), - title: const Text("Theme"), + title: const PlatformText("Theme"), trailing: (context, update) => PlatformDropDownMenu( value: preferences.themeMode, items: [ PlatformDropDownMenuItem( value: ThemeMode.dark, - child: const Text( - "Dark", - ), + child: const PlatformText("Dark"), ), PlatformDropDownMenuItem( value: ThemeMode.light, - child: const Text( - "Light", - ), + child: const PlatformText("Light"), ), PlatformDropDownMenuItem( value: ThemeMode.system, - child: const Text("System"), + child: const PlatformText("System"), ), ], onChanged: (value) { @@ -209,7 +209,7 @@ class Settings extends HookConsumerWidget { ), PlatformListTile( leading: const Icon(Icons.palette_outlined), - title: const Text("Accent Color Scheme"), + title: const PlatformText("Accent Color Scheme"), contentPadding: const EdgeInsets.symmetric( horizontal: 15, vertical: 5, @@ -223,7 +223,7 @@ class Settings extends HookConsumerWidget { ), PlatformListTile( leading: const Icon(Icons.format_color_fill_rounded), - title: const Text("Background Color Scheme"), + title: const PlatformText("Background Color Scheme"), contentPadding: const EdgeInsets.symmetric( horizontal: 15, vertical: 5, @@ -237,7 +237,7 @@ class Settings extends HookConsumerWidget { ), PlatformListTile( leading: const Icon(Icons.album_rounded), - title: const Text("Rotating Album Art"), + title: const PlatformText("Rotating Album Art"), trailing: PlatformSwitch( value: preferences.rotatingAlbumArt, onChanged: (state) { @@ -245,27 +245,28 @@ class Settings extends HookConsumerWidget { }, ), ), - const Text( + PlatformText( " Playback", - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + style: PlatformTextTheme.of(context) + .headline + ?.copyWith(fontWeight: FontWeight.bold), ), AdaptiveListTile( leading: const Icon(Icons.multitrack_audio_rounded), - title: const Text("Audio Quality"), + title: const PlatformText("Audio Quality"), trailing: (context, update) => PlatformDropDownMenu( value: preferences.audioQuality, items: [ PlatformDropDownMenuItem( value: AudioQuality.high, - child: const Text( + child: const PlatformText( "High", ), ), PlatformDropDownMenuItem( value: AudioQuality.low, - child: const Text("Low"), + child: const PlatformText("Low"), ), ], onChanged: (value) { @@ -279,10 +280,10 @@ class Settings extends HookConsumerWidget { if (kIsMobile) PlatformListTile( leading: const Icon(Icons.download_for_offline_rounded), - title: const Text( + title: const PlatformText( "Pre download and play", ), - subtitle: const Text( + subtitle: const PlatformText( "Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)", ), trailing: PlatformSwitch( @@ -294,7 +295,7 @@ class Settings extends HookConsumerWidget { ), PlatformListTile( leading: const Icon(Icons.fast_forward_rounded), - title: const Text( + title: const PlatformText( "Skip non-music segments (SponsorBlock)", ), trailing: PlatformSwitch( @@ -304,20 +305,17 @@ class Settings extends HookConsumerWidget { }, ), ), - const Text( + PlatformText( " Search", - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + style: PlatformTextTheme.of(context) + .headline + ?.copyWith(fontWeight: FontWeight.bold), ), AdaptiveListTile( leading: const Icon(Icons.shopping_bag_rounded), - title: Text( - "Market Place", - style: Theme.of(context).textTheme.bodyText1, - ), - subtitle: Text( + title: const PlatformText("Market Place"), + subtitle: PlatformText.caption( "Recommendation Country", - style: Theme.of(context).textTheme.caption, ), trailing: (context, update) => ConstrainedBox( constraints: const BoxConstraints(maxWidth: 250), @@ -327,7 +325,7 @@ class Settings extends HookConsumerWidget { .map( (country) => (PlatformDropDownMenuItem( value: country.first, - child: Text(country.last), + child: PlatformText(country.last), )), ) .toList(), @@ -343,18 +341,21 @@ class Settings extends HookConsumerWidget { ), AdaptiveListTile( leading: const Icon(Icons.screen_search_desktop_rounded), - title: const SizedBox( + title: SizedBox( height: 50, width: 200, child: Align( alignment: Alignment.centerLeft, - child: AutoSizeText( - "Format of the YouTube Search term", - maxLines: 2, + child: DefaultTextStyle( + style: PlatformTextTheme.of(context).body!, + child: const AutoSizeText( + "Format of the YouTube Search term", + maxLines: 2, + ), ), ), ), - subtitle: const Text("(Case sensitive)"), + subtitle: const PlatformText("(Case sensitive)"), breakOn: Breakpoints.lg, trailing: (context, update) => ConstrainedBox( constraints: const BoxConstraints(maxWidth: 450), @@ -377,7 +378,7 @@ class Settings extends HookConsumerWidget { ), AdaptiveListTile( leading: const Icon(Icons.low_priority_rounded), - title: const SizedBox( + title: SizedBox( height: 50, width: 180, child: Align( @@ -385,6 +386,7 @@ class Settings extends HookConsumerWidget { child: AutoSizeText( "Track Match Algorithm", maxLines: 1, + style: PlatformTextTheme.of(context).body, ), ), ), @@ -394,19 +396,19 @@ class Settings extends HookConsumerWidget { items: [ PlatformDropDownMenuItem( value: SpotubeTrackMatchAlgorithm.authenticPopular, - child: const Text( + child: const PlatformText( "Popular from Author", ), ), PlatformDropDownMenuItem( value: SpotubeTrackMatchAlgorithm.popular, - child: const Text( + child: const PlatformText( "Accurately Popular", ), ), PlatformDropDownMenuItem( value: SpotubeTrackMatchAlgorithm.youtube, - child: const Text("YouTube's Top choice"), + child: const PlatformText("YouTube's Top choice"), ), ], onChanged: (value) { @@ -417,15 +419,16 @@ class Settings extends HookConsumerWidget { }, ), ), - const Text( + PlatformText( " Downloads", - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + style: PlatformTextTheme.of(context) + .headline + ?.copyWith(fontWeight: FontWeight.bold), ), PlatformListTile( leading: const Icon(Icons.file_download_outlined), - title: const Text("Download Location"), - subtitle: Text(preferences.downloadLocation), + title: const PlatformText("Download Location"), + subtitle: PlatformText(preferences.downloadLocation), trailing: PlatformFilledButton( onPressed: pickDownloadLocation, child: const Icon(Icons.folder_rounded), @@ -434,7 +437,8 @@ class Settings extends HookConsumerWidget { ), PlatformListTile( leading: const Icon(Icons.lyrics_rounded), - title: const Text("Download lyrics along with the Track"), + title: const PlatformText( + "Download lyrics along with the Track"), trailing: PlatformSwitch( value: preferences.saveTrackLyrics, onChanged: (state) { @@ -442,10 +446,11 @@ class Settings extends HookConsumerWidget { }, ), ), - const Text( + PlatformText( " About", - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + style: PlatformTextTheme.of(context) + .headline + ?.copyWith(fontWeight: FontWeight.bold), ), AdaptiveListTile( leading: const Icon( @@ -487,14 +492,14 @@ class Settings extends HookConsumerWidget { children: const [ Icon(Icons.favorite_outline_rounded), SizedBox(width: 5), - Text("Please Sponsor/Donate"), + PlatformText("Please Sponsor/Donate"), ], ), ), ), PlatformListTile( leading: const Icon(Icons.update_rounded), - title: const Text("Check for Update"), + title: const PlatformText("Check for Update"), trailing: PlatformSwitch( value: preferences.checkUpdate, onChanged: (checked) => diff --git a/lib/components/Shared/AdaptivePopupMenuButton.dart b/lib/components/Shared/AdaptivePopupMenuButton.dart index fc1d0a98..5dd34b3e 100644 --- a/lib/components/Shared/AdaptivePopupMenuButton.dart +++ b/lib/components/Shared/AdaptivePopupMenuButton.dart @@ -20,15 +20,15 @@ class Action extends StatelessWidget { @override Widget build(BuildContext context) { if (isExpanded != true) { - return Tooltip( - message: text.toStringShallow().split(",").last.replaceAll( - "\"", - "", - ), - child: PlatformIconButton( - icon: icon, - onPressed: onPressed, - ), + return PlatformIconButton( + icon: icon, + onPressed: onPressed, + tooltip: text is Text + ? (text as Text).data + : text.toStringShallow().split(",").last.replaceAll( + "\"", + "", + ), ); } return PlatformTextButton( diff --git a/lib/components/Shared/AnchorButton.dart b/lib/components/Shared/AnchorButton.dart index ede984e9..9660cfc1 100644 --- a/lib/components/Shared/AnchorButton.dart +++ b/lib/components/Shared/AnchorButton.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:platform_ui/platform_ui.dart'; class AnchorButton extends HookWidget { final String text; @@ -28,7 +29,7 @@ class AnchorButton extends HookWidget { onTap: onTap, child: MouseRegion( cursor: MaterialStateMouseCursor.clickable, - child: Text( + child: PlatformText( text, style: style.copyWith( decoration: diff --git a/lib/components/Shared/PageWindowTitleBar.dart b/lib/components/Shared/PageWindowTitleBar.dart index 1d3afe4a..06fd4923 100644 --- a/lib/components/Shared/PageWindowTitleBar.dart +++ b/lib/components/Shared/PageWindowTitleBar.dart @@ -33,7 +33,7 @@ class TitleBarActionButtons extends StatelessWidget { }, style: ButtonStyle( foregroundColor: MaterialStateProperty.all( - Theme.of(context).iconTheme.color), + PlatformTheme.of(context).iconTheme?.color), ), child: Icon( Icons.minimize_rounded, @@ -45,7 +45,7 @@ class TitleBarActionButtons extends StatelessWidget { }, style: ButtonStyle( foregroundColor: MaterialStateProperty.all( - Theme.of(context).iconTheme.color), + PlatformTheme.of(context).iconTheme?.color), ), child: Icon( Icons.crop_square_rounded, @@ -57,7 +57,7 @@ class TitleBarActionButtons extends StatelessWidget { }, style: ButtonStyle( foregroundColor: MaterialStateProperty.all( - color ?? Theme.of(context).iconTheme.color), + color ?? PlatformTheme.of(context).iconTheme?.color), overlayColor: MaterialStateProperty.all(Colors.redAccent), ), child: const Icon( diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index b86c2a92..7f88d17e 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -77,6 +77,7 @@ class PlaybuttonCard extends HookWidget { onTap: onTap, borderRadius: BorderRadius.circular(8), splashFactory: splash, + highlightColor: Colors.black12, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 200), child: HoverBuilder(builder: (context, isHovering) { @@ -89,11 +90,10 @@ class PlaybuttonCard extends HookWidget { boxShadow: [ if (boxShadow != null) boxShadow, ], - border: platform == TargetPlatform.windows + border: [TargetPlatform.windows, TargetPlatform.macOS] + .contains(platform) ? Border.all( - color: FluentUI.FluentTheme.maybeOf(context) - ?.micaBackgroundColor - .withOpacity(.7) ?? + color: PlatformTheme.of(context).borderColor ?? Colors.transparent, width: 1, ) diff --git a/lib/components/Shared/TrackCollectionView.dart b/lib/components/Shared/TrackCollectionView.dart index f89d750a..b42ef7cc 100644 --- a/lib/components/Shared/TrackCollectionView.dart +++ b/lib/components/Shared/TrackCollectionView.dart @@ -132,12 +132,12 @@ class TrackCollectionView extends HookConsumerWidget { primary: true, backgroundColor: color?.color, title: collapsed.value - ? Text( + ? PlatformText.headline( title, - style: Theme.of(context).textTheme.headline4?.copyWith( - color: color?.titleTextColor, - fontWeight: FontWeight.w600, - ), + style: TextStyle( + color: color?.titleTextColor, + fontWeight: FontWeight.w600, + ), ) : null, flexibleSpace: LayoutBuilder(builder: (context, constrains) { @@ -188,25 +188,19 @@ class TrackCollectionView extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( + PlatformText.headline( title, - style: Theme.of(context) - .textTheme - .headline4 - ?.copyWith( - color: color?.titleTextColor, - fontWeight: FontWeight.w600, - ), + style: TextStyle( + color: color?.titleTextColor, + fontWeight: FontWeight.w600, + ), ), if (description != null) - Text( + PlatformText( description!, - style: Theme.of(context) - .textTheme - .bodyLarge - ?.copyWith( - color: color?.bodyTextColor, - ), + style: TextStyle( + color: color?.bodyTextColor, + ), maxLines: 2, overflow: TextOverflow.fade, ), @@ -232,7 +226,7 @@ class TrackCollectionView extends HookConsumerWidget { } else if (tracksSnapshot.hasError && tracksSnapshot.isError) { return SliverToBoxAdapter( - child: Text("Error ${tracksSnapshot.error}")); + child: PlatformText("Error ${tracksSnapshot.error}")); } final tracks = tracksSnapshot.data!; diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index 4605fd0b..8250e768 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -74,7 +74,7 @@ class TrackTile extends HookConsumerWidget { SnackBar( width: 300, behavior: SnackBarBehavior.floating, - content: Text( + content: PlatformText( "Copied $data to clipboard", textAlign: TextAlign.center, ), @@ -98,7 +98,7 @@ class TrackTile extends HookConsumerWidget { return HookBuilder(builder: (context) { final playlistsCheck = useState({}); return AlertDialog( - title: Text( + title: PlatformText( "Add `${track.value.name}` to following Playlists"), titleTextStyle: Theme.of(context).textTheme.bodyText1?.copyWith( @@ -107,11 +107,11 @@ class TrackTile extends HookConsumerWidget { ), actions: [ PlatformTextButton( - child: const Text("Cancel"), + child: const PlatformText("Cancel"), onPressed: () => Navigator.pop(context), ), PlatformFilledButton( - child: const Text("Add"), + child: const PlatformText("Add"), onPressed: () async { final selectedPlaylists = playlistsCheck .value.entries @@ -140,7 +140,7 @@ class TrackTile extends HookConsumerWidget { final playlist = snapshot.data!.elementAt(index); return CheckboxListTile( - title: Text(playlist.name!), + title: PlatformText(playlist.name!), controlAffinity: ListTileControlAffinity.leading, value: playlistsCheck.value[playlist.id] ?? @@ -191,7 +191,7 @@ class TrackTile extends HookConsumerWidget { height: 20, width: 25, child: Center( - child: Text((track.key + 1).toString()), + child: PlatformText((track.key + 1).toString()), ), ), Padding( @@ -221,7 +221,7 @@ class TrackTile extends HookConsumerWidget { playback.track?.id == track.value.id ? Icons.pause_circle_rounded : Icons.play_circle_rounded, - color: Theme.of(context).primaryColor, + color: PlatformTheme.of(context).primaryColor, ), onPressed: () => onTrackPlayButtonPressed?.call( track.value, @@ -231,7 +231,7 @@ class TrackTile extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + PlatformText( track.value.name ?? "", style: TextStyle( fontWeight: FontWeight.bold, @@ -240,7 +240,7 @@ class TrackTile extends HookConsumerWidget { overflow: TextOverflow.ellipsis, ), isReallyLocal - ? Text( + ? PlatformText( TypeConversionUtils.artists_X_String( track.value.artists ?? []), ) @@ -256,7 +256,7 @@ class TrackTile extends HookConsumerWidget { if (breakpoint.isMoreThan(Breakpoints.md) && showAlbum) Expanded( child: isReallyLocal - ? Text(track.value.album?.name ?? "") + ? PlatformText(track.value.album?.name ?? "") : LinkText( track.value.album!.name!, "/album/${track.value.album?.id}", @@ -266,7 +266,7 @@ class TrackTile extends HookConsumerWidget { ), if (!breakpoint.isSm) ...[ const SizedBox(width: 10), - Text(duration), + PlatformText(duration), ], const SizedBox(width: 10), if (!isReallyLocal) @@ -280,7 +280,7 @@ class TrackTile extends HookConsumerWidget { color: Colors.pink, ) : const Icon(Icons.favorite_border_rounded), - text: const Text("Save as favorite"), + text: const PlatformText("Save as favorite"), onPressed: () { toggler.item2.mutate(Tuple2(spotify, toggler.item1)); }, @@ -288,18 +288,18 @@ class TrackTile extends HookConsumerWidget { if (auth.isLoggedIn) Action( icon: const Icon(Icons.add_box_rounded), - text: const Text("Add To playlist"), + text: const PlatformText("Add To playlist"), onPressed: actionAddToPlaylist, ), if (userPlaylist && auth.isLoggedIn) Action( icon: const Icon(Icons.remove_circle_outline_rounded), - text: const Text("Remove from playlist"), + text: const PlatformText("Remove from playlist"), onPressed: actionRemoveFromPlaylist, ), Action( icon: const Icon(Icons.share_rounded), - text: const Text("Share"), + text: const PlatformText("Share"), onPressed: () { actionShare(track.value); }, diff --git a/lib/components/Shared/TracksTableView.dart b/lib/components/Shared/TracksTableView.dart index 21210440..05922eb9 100644 --- a/lib/components/Shared/TracksTableView.dart +++ b/lib/components/Shared/TracksTableView.dart @@ -82,7 +82,7 @@ class TracksTableView extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.all(8.0), - child: Text( + child: PlatformText( "#", textAlign: TextAlign.center, style: tableHeadStyle, @@ -91,7 +91,7 @@ class TracksTableView extends HookConsumerWidget { Expanded( child: Row( children: [ - Text( + PlatformText( "Title", style: tableHeadStyle, overflow: TextOverflow.ellipsis, @@ -105,7 +105,7 @@ class TracksTableView extends HookConsumerWidget { Expanded( child: Row( children: [ - Text( + PlatformText( "Album", overflow: TextOverflow.ellipsis, style: tableHeadStyle, @@ -116,7 +116,7 @@ class TracksTableView extends HookConsumerWidget { ], if (!breakpoint.isSm) ...[ const SizedBox(width: 10), - Text("Time", style: tableHeadStyle), + PlatformText("Time", style: tableHeadStyle), const SizedBox(width: 10), ], SortTracksDropdown( @@ -135,7 +135,7 @@ class TracksTableView extends HookConsumerWidget { child: Row( children: [ const Icon(Icons.file_download_outlined), - Text( + PlatformText( "Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}", ), ], diff --git a/lib/main.dart b/lib/main.dart index 688cf9c2..cdc1260e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -201,7 +201,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.windows; + platform = TargetPlatform.macOS; return PlatformApp.router( routeInformationParser: router.routeInformationParser, diff --git a/lib/themes/light-theme.dart b/lib/themes/light-theme.dart index dbf5996a..5a9d5590 100644 --- a/lib/themes/light-theme.dart +++ b/lib/themes/light-theme.dart @@ -134,14 +134,17 @@ final windowsDarkTheme = FluentUI.ThemeData.dark().copyWith( ), ); final macosTheme = MacosThemeData.light().copyWith( - pushButtonTheme: PushButtonThemeData( + pushButtonTheme: const PushButtonThemeData( secondaryColor: Colors.white, ), - iconTheme: MacosIconThemeData(size: 16), - iconButtonTheme: MacosIconButtonThemeData(), - typography: MacosTypography(color: Colors.green), + iconTheme: const MacosIconThemeData(size: 14), + typography: MacosTypography(color: Colors.grey[900]!), ); final macosDarkTheme = MacosThemeData.dark().copyWith( - typography: MacosTypography(color: Colors.red), + pushButtonTheme: const PushButtonThemeData( + secondaryColor: Colors.white, + ), + iconTheme: const MacosIconThemeData(size: 14), + typography: MacosTypography(color: MacosColors.textColor), ); final iosTheme = CupertinoThemeData(); From c201624f996edcbe9110574293c94f96cf38bd38 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 3 Nov 2022 09:12:43 +0600 Subject: [PATCH 06/23] refactor: platform alert dialog for all dialogs --- lib/components/Home/Shell.dart | 5 +- lib/components/Library/UserDownloads.dart | 10 +- .../Lyrics/LyricDelayAdjustDialog.dart | 9 +- lib/components/Lyrics/SyncedLyrics.dart | 4 +- .../Playlist/PlaylistCreateDialog.dart | 19 ++- .../Settings/ColorSchemePickerDialog.dart | 19 ++- lib/components/Settings/Settings.dart | 4 +- lib/components/Shared/AdaptiveListTile.dart | 8 +- .../Shared/DownloadConfirmationDialog.dart | 53 ++++--- .../Shared/ReplaceDownloadedFileDialog.dart | 21 +-- lib/components/Shared/TrackTile.dart | 144 +++++++++--------- lib/components/Shared/TracksTableView.dart | 9 +- lib/hooks/useUpdateChecker.dart | 60 ++++---- lib/main.dart | 8 +- lib/themes/light-theme.dart | 3 +- 15 files changed, 194 insertions(+), 182 deletions(-) diff --git a/lib/components/Home/Shell.dart b/lib/components/Home/Shell.dart index dc51c464..2d24a7f8 100644 --- a/lib/components/Home/Shell.dart +++ b/lib/components/Home/Shell.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/components/Home/SpotubeNavigationBar.dart'; import 'package:spotube/components/Player/Player.dart'; @@ -35,8 +36,8 @@ class Shell extends HookConsumerWidget { useEffect(() { downloader.onFileExists = (track) async { if (!isMounted()) return false; - return await showDialog( - context: context, + return await showPlatformAlertDialog( + context, builder: (context) => ReplaceDownloadedFileDialog( track: track, ), diff --git a/lib/components/Library/UserDownloads.dart b/lib/components/Library/UserDownloads.dart index bd7fb732..33263d6d 100644 --- a/lib/components/Library/UserDownloads.dart +++ b/lib/components/Library/UserDownloads.dart @@ -31,14 +31,14 @@ class UserDownloads extends HookConsumerWidget { ), const SizedBox(width: 10), PlatformFilledButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red[50], - foregroundColor: Colors.red[400], + style: ButtonStyle( + backgroundColor: MaterialStatePropertyAll(Colors.red[50]), + foregroundColor: MaterialStatePropertyAll(Colors.red[400]), ), onPressed: downloader.currentlyRunning > 0 ? downloader.cancelAll : null, - macOSIsSecondary: true, + isSecondary: true, child: const PlatformText("Cancel All"), ), ], @@ -50,7 +50,7 @@ class UserDownloads extends HookConsumerWidget { itemBuilder: (context, index) { final track = downloader.inQueue.elementAt(index); return PlatformListTile( - title: Text(track.name!), + title: Text(track.name ?? ''), leading: Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: ClipRRect( diff --git a/lib/components/Lyrics/LyricDelayAdjustDialog.dart b/lib/components/Lyrics/LyricDelayAdjustDialog.dart index f0c5c909..402fc896 100644 --- a/lib/components/Lyrics/LyricDelayAdjustDialog.dart +++ b/lib/components/Lyrics/LyricDelayAdjustDialog.dart @@ -16,15 +16,18 @@ class LyricDelayAdjustDialog extends HookConsumerWidget { double getValue() => double.tryParse(controller.text.replaceAll("ms", "")) ?? 0; - return AlertDialog( + return PlatformAlertDialog( title: const Center(child: Text("Adjust Lyrics Delay")), - actions: [ + secondaryActions: [ PlatformFilledButton( - child: const Text("Cancel"), + isSecondary: true, onPressed: () { Navigator.of(context).pop(); }, + child: const Text("Cancel"), ), + ], + primaryActions: [ PlatformFilledButton( child: const Text("Done"), onPressed: () { diff --git a/lib/components/Lyrics/SyncedLyrics.dart b/lib/components/Lyrics/SyncedLyrics.dart index ed69c350..1edf4f97 100644 --- a/lib/components/Lyrics/SyncedLyrics.dart +++ b/lib/components/Lyrics/SyncedLyrics.dart @@ -90,8 +90,8 @@ class SyncedLyrics extends HookConsumerWidget { tooltip: "Lyrics Delay", icon: const Icon(Icons.av_timer_rounded), onPressed: () async { - final delay = await showDialog( - context: context, + final delay = await showPlatformAlertDialog( + context, builder: (context) => const LyricDelayAdjustDialog(), ); if (delay != null) { diff --git a/lib/components/Playlist/PlaylistCreateDialog.dart b/lib/components/Playlist/PlaylistCreateDialog.dart index dc085786..97033d5d 100644 --- a/lib/components/Playlist/PlaylistCreateDialog.dart +++ b/lib/components/Playlist/PlaylistCreateDialog.dart @@ -15,8 +15,8 @@ class PlaylistCreateDialog extends HookConsumerWidget { return PlatformTextButton( onPressed: () { - showDialog( - context: context, + showPlatformAlertDialog( + context, builder: (context) { return HookBuilder(builder: (context) { final playlistName = useTextEditingController(); @@ -24,13 +24,9 @@ class PlaylistCreateDialog extends HookConsumerWidget { final public = useState(false); final collaborative = useState(false); - return AlertDialog( + return PlatformAlertDialog( title: const Text("Create a Playlist"), - actions: [ - PlatformTextButton( - child: const Text("Cancel"), - onPressed: () => Navigator.of(context).pop(), - ), + primaryActions: [ PlatformFilledButton( child: const Text("Create"), onPressed: () async { @@ -53,6 +49,13 @@ class PlaylistCreateDialog extends HookConsumerWidget { }, ) ], + secondaryActions: [ + PlatformFilledButton( + isSecondary: true, + onPressed: () => Navigator.of(context).pop(), + child: const Text("Cancel"), + ), + ], content: Container( width: MediaQuery.of(context).size.width, constraints: const BoxConstraints(maxWidth: 500), diff --git a/lib/components/Settings/ColorSchemePickerDialog.dart b/lib/components/Settings/ColorSchemePickerDialog.dart index 7c0fbe8a..8bd14935 100644 --- a/lib/components/Settings/ColorSchemePickerDialog.dart +++ b/lib/components/Settings/ColorSchemePickerDialog.dart @@ -66,15 +66,9 @@ class ColorSchemePickerDialog extends HookConsumerWidget { }, ).key); - return AlertDialog( + return PlatformAlertDialog( title: Text("Pick ${schemeType.name} color scheme"), - actions: [ - PlatformTextButton( - child: const Text("Cancel"), - onPressed: () { - Navigator.pop(context); - }, - ), + primaryActions: [ PlatformFilledButton( child: const Text("Save"), onPressed: () { @@ -91,6 +85,15 @@ class ColorSchemePickerDialog extends HookConsumerWidget { }, ) ], + secondaryActions: [ + PlatformFilledButton( + isSecondary: true, + child: const Text("Cancel"), + onPressed: () { + Navigator.pop(context); + }, + ), + ], content: SizedBox( height: 200, width: 400, diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index 28a652e4..4bf361a0 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -28,9 +28,7 @@ class Settings extends HookConsumerWidget { final Auth auth = ref.watch(authProvider); final pickColorScheme = useCallback((ColorSchemeType schemeType) { - return () => showDialog( - context: context, - builder: (context) { + return () => showPlatformAlertDialog(context, builder: (context) { return ColorSchemePickerDialog( schemeType: schemeType, ); diff --git a/lib/components/Shared/AdaptiveListTile.dart b/lib/components/Shared/AdaptiveListTile.dart index ba81621b..3d219ee8 100644 --- a/lib/components/Shared/AdaptiveListTile.dart +++ b/lib/components/Shared/AdaptiveListTile.dart @@ -34,11 +34,11 @@ class AdaptiveListTile extends HookWidget { onTap: breakpoint.isLessThan(breakOn) ? () { onTap?.call(); - showDialog( - context: context, + showPlatformAlertDialog( + context, builder: (context) { return StatefulBuilder(builder: (context, update) { - return AlertDialog( + return PlatformAlertDialog( title: title != null ? Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -50,7 +50,7 @@ class AdaptiveListTile extends HookWidget { Flexible(child: title!), ], ) - : null, + : Container(), content: trailing?.call(context, update), ); }); diff --git a/lib/components/Shared/DownloadConfirmationDialog.dart b/lib/components/Shared/DownloadConfirmationDialog.dart index 73c04a59..5e770b31 100644 --- a/lib/components/Shared/DownloadConfirmationDialog.dart +++ b/lib/components/Shared/DownloadConfirmationDialog.dart @@ -7,22 +7,24 @@ class DownloadConfirmationDialog extends StatelessWidget { @override Widget build(BuildContext context) { - return AlertDialog( - contentPadding: const EdgeInsets.all(15), - title: Row( - children: const [ - Text("Are you sure?"), - SizedBox(width: 10), - UniversalImage( - path: - "https://c.tenor.com/kHcmsxlKHEAAAAAM/rock-one-eyebrow-raised-rock-staring.gif", - height: 40, - width: 40, - ) - ], + return PlatformAlertDialog( + title: Padding( + padding: const EdgeInsets.all(15), + child: Row( + children: const [ + Text("Are you sure?"), + SizedBox(width: 10), + UniversalImage( + path: + "https://c.tenor.com/kHcmsxlKHEAAAAAM/rock-one-eyebrow-raised-rock-staring.gif", + height: 40, + width: 40, + ) + ], + ), ), - content: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 400), + content: Padding( + padding: const EdgeInsets.all(15), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, @@ -57,19 +59,22 @@ class DownloadConfirmationDialog extends StatelessWidget { ), ), ), - actions: [ + primaryActions: [ PlatformFilledButton( + style: const ButtonStyle( + foregroundColor: MaterialStatePropertyAll(Colors.white), + backgroundColor: MaterialStatePropertyAll(Colors.red), + ), + onPressed: () => Navigator.of(context).pop(true), + child: const Text("Accept"), + ) + ], + secondaryActions: [ + PlatformFilledButton( + isSecondary: true, child: const Text("Decline"), onPressed: () => Navigator.of(context).pop(false), ), - PlatformFilledButton( - onPressed: () => Navigator.of(context).pop(true), - style: ElevatedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: Colors.red, - ), - child: const Text("Accept"), - ) ], ); } diff --git a/lib/components/Shared/ReplaceDownloadedFileDialog.dart b/lib/components/Shared/ReplaceDownloadedFileDialog.dart index ec472b8d..58263746 100644 --- a/lib/components/Shared/ReplaceDownloadedFileDialog.dart +++ b/lib/components/Shared/ReplaceDownloadedFileDialog.dart @@ -14,7 +14,7 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget { Widget build(BuildContext context, ref) { final groupValue = ref.watch(replaceDownloadedFileState); - return AlertDialog( + return PlatformAlertDialog( title: Text("Track ${track.name} Already Exists"), content: Column( mainAxisSize: MainAxisSize.min, @@ -48,20 +48,23 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget { ), ], ), - actions: [ - PlatformTextButton( - child: const Text("No"), - onPressed: () { - Navigator.pop(context, false); - }, - ), - PlatformTextButton( + primaryActions: [ + PlatformFilledButton( child: const Text("Yes"), onPressed: () { Navigator.pop(context, true); }, ) ], + secondaryActions: [ + PlatformFilledButton( + isSecondary: true, + child: const Text("No"), + onPressed: () { + Navigator.pop(context, false); + }, + ), + ], ); } } diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index 8250e768..dedcb1bc 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -84,81 +84,79 @@ class TrackTile extends HookConsumerWidget { } Future actionAddToPlaylist() async { - showDialog( - context: context, - builder: (context) { - return FutureBuilder>( - future: spotify.playlists.me.all().then((playlists) async { - final me = await spotify.me.get(); - return playlists.where((playlist) => - playlist.owner?.id != null && - playlist.owner!.id == me.id); - }), - builder: (context, snapshot) { - return HookBuilder(builder: (context) { - final playlistsCheck = useState({}); - return AlertDialog( - title: PlatformText( - "Add `${track.value.name}` to following Playlists"), - titleTextStyle: - Theme.of(context).textTheme.bodyText1?.copyWith( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - actions: [ - PlatformTextButton( - child: const PlatformText("Cancel"), - onPressed: () => Navigator.pop(context), - ), - PlatformFilledButton( - child: const PlatformText("Add"), - onPressed: () async { - final selectedPlaylists = playlistsCheck - .value.entries - .where((entry) => entry.value) - .map((entry) => entry.key); + showPlatformAlertDialog(context, builder: (context) { + return FutureBuilder>( + future: spotify.playlists.me.all().then((playlists) async { + final me = await spotify.me.get(); + return playlists.where((playlist) => + playlist.owner?.id != null && playlist.owner!.id == me.id); + }), + builder: (context, snapshot) { + return HookBuilder(builder: (context) { + final playlistsCheck = useState({}); + return PlatformAlertDialog( + title: PlatformText( + "Add `${track.value.name}` to following Playlists", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + secondaryActions: [ + PlatformFilledButton( + isSecondary: true, + child: const PlatformText("Cancel"), + onPressed: () => Navigator.pop(context), + ), + ], + primaryActions: [ + PlatformFilledButton( + child: const PlatformText("Add"), + onPressed: () async { + final selectedPlaylists = playlistsCheck.value.entries + .where((entry) => entry.value) + .map((entry) => entry.key); - await Future.wait( - selectedPlaylists.map( - (playlistId) => spotify.playlists - .addTrack(track.value.uri!, playlistId), - ), - ).then((_) => Navigator.pop(context)); - }, - ) - ], - content: SizedBox( - height: 300, - width: 300, - child: !snapshot.hasData - ? const Center( - child: PlatformCircularProgressIndicator()) - : ListView.builder( - shrinkWrap: true, - itemCount: snapshot.data!.length, - itemBuilder: (context, index) { - final playlist = - snapshot.data!.elementAt(index); - return CheckboxListTile( - title: PlatformText(playlist.name!), - controlAffinity: - ListTileControlAffinity.leading, - value: playlistsCheck.value[playlist.id] ?? - false, - onChanged: (val) { - playlistsCheck.value = { - ...playlistsCheck.value, - playlist.id!: val == true - }; - }, - ); + await Future.wait( + selectedPlaylists.map( + (playlistId) => spotify.playlists + .addTrack(track.value.uri!, playlistId), + ), + ).then((_) => Navigator.pop(context)); + }, + ) + ], + content: SizedBox( + height: 300, + width: 300, + child: !snapshot.hasData + ? const Center( + child: PlatformCircularProgressIndicator()) + : ListView.builder( + shrinkWrap: true, + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + final playlist = snapshot.data!.elementAt(index); + return CheckboxListTile( + title: PlatformText(playlist.name!), + controlAffinity: + ListTileControlAffinity.leading, + value: + playlistsCheck.value[playlist.id] ?? false, + onChanged: (val) { + playlistsCheck.value = { + ...playlistsCheck.value, + playlist.id!: val == true + }; }, - ), - ), - ); - }); - }); - }); + ); + }, + ), + ), + ); + }); + }); + }); } final String thumbnailUrl = TypeConversionUtils.image_X_UrlString( diff --git a/lib/components/Shared/TracksTableView.dart b/lib/components/Shared/TracksTableView.dart index 05922eb9..1dca965f 100644 --- a/lib/components/Shared/TracksTableView.dart +++ b/lib/components/Shared/TracksTableView.dart @@ -146,11 +146,10 @@ class TracksTableView extends HookConsumerWidget { switch (action) { case "download": { - final isConfirmed = await showDialog( - context: context, - builder: (context) { - return const DownloadConfirmationDialog(); - }); + final isConfirmed = await showPlatformAlertDialog( + context, builder: (context) { + return const DownloadConfirmationDialog(); + }); if (isConfirmed != true) return; for (final selectedTrack in selectedTracks) { downloader.addToQueue(selectedTrack); diff --git a/lib/hooks/useUpdateChecker.dart b/lib/hooks/useUpdateChecker.dart index f0648a55..8105b119 100644 --- a/lib/hooks/useUpdateChecker.dart +++ b/lib/hooks/useUpdateChecker.dart @@ -52,41 +52,39 @@ void useUpdateChecker(WidgetRef ref) { final latestVersion = value.last; if (currentVersion == null || latestVersion == null) return; if (latestVersion <= currentVersion) return; - showDialog( - context: context, - builder: (context) { - const url = - "https://spotube.netlify.app/other-downloads/stable-downloads"; - return AlertDialog( - title: const Text("Spotube has an update"), - actions: [ - PlatformFilledButton( - child: const Text("Download Now"), - onPressed: () => download(url), - ), - ], - content: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + showPlatformAlertDialog(context, builder: (context) { + const url = + "https://spotube.netlify.app/other-downloads/stable-downloads"; + return PlatformAlertDialog( + title: const PlatformText("Spotube has an update"), + primaryActions: [ + PlatformFilledButton( + child: const Text("Download Now"), + onPressed: () => download(url), + ), + ], + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text("Spotube v${value.last} has been released"), + Row( children: [ - Text("Spotube v${value.last} has been released"), - Row( - children: [ - const Text("Read the latest "), - AnchorButton( - "release notes", - style: const TextStyle(color: Colors.blue), - onTap: () => launchUrlString( - url, - mode: LaunchMode.externalApplication, - ), - ), - ], + const PlatformText("Read the latest "), + AnchorButton( + "release notes", + style: const TextStyle(color: Colors.blue), + onTap: () => launchUrlString( + url, + mode: LaunchMode.externalApplication, + ), ), ], ), - ); - }); + ], + ), + ); + }); }); return null; }, [packageInfo, isCheckUpdateEnabled]); diff --git a/lib/main.dart b/lib/main.dart index cdc1260e..0d7dbbe4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -109,8 +109,8 @@ void main() async { logger.v( "[onFileExists] download confirmation for ${track.name}", ); - return showDialog( - context: context, + return showPlatformAlertDialog( + context, builder: (_) => ReplaceDownloadedFileDialog(track: track), ).then((s) => s ?? false); @@ -201,7 +201,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.macOS; + platform = TargetPlatform.windows; return PlatformApp.router( routeInformationParser: router.routeInformationParser, @@ -217,7 +217,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { accentMaterialColor: accentMaterialColor, backgroundMaterialColor: backgroundMaterialColor, ), - iosTheme: iosTheme, + iosTheme: themeMode == ThemeMode.dark ? iosDarkTheme : iosTheme, windowsTheme: windowsTheme, windowsDarkTheme: windowsDarkTheme, macosTheme: macosTheme, diff --git a/lib/themes/light-theme.dart b/lib/themes/light-theme.dart index 5a9d5590..0b4ab77e 100644 --- a/lib/themes/light-theme.dart +++ b/lib/themes/light-theme.dart @@ -147,4 +147,5 @@ final macosDarkTheme = MacosThemeData.dark().copyWith( iconTheme: const MacosIconThemeData(size: 14), typography: MacosTypography(color: MacosColors.textColor), ); -final iosTheme = CupertinoThemeData(); +const iosTheme = CupertinoThemeData(brightness: Brightness.light); +const iosDarkTheme = CupertinoThemeData(brightness: Brightness.dark); From fb9c0e44be93997fc852bf0260e8a8608000c023 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 3 Nov 2022 10:49:05 +0600 Subject: [PATCH 07/23] fix(macos): black text in dark mode --- .vscode/launch.json | 2 +- lib/components/Library/UserAlbums.dart | 1 + lib/components/Library/UserArtists.dart | 1 + lib/components/Library/UserPlaylists.dart | 2 ++ lib/components/Lyrics/LyricDelayAdjustDialog.dart | 7 ++----- lib/components/Lyrics/SyncedLyrics.dart | 1 + lib/components/Player/Player.dart | 1 + lib/components/Player/PlayerOverlay.dart | 1 + lib/components/Player/PlayerView.dart | 1 + lib/components/Player/SiblingTracksSheet.dart | 4 ++-- lib/components/Playlist/PlaylistCreateDialog.dart | 4 ++-- lib/components/Search/Search.dart | 1 + lib/components/Settings/About.dart | 11 ++++++----- lib/components/Settings/Settings.dart | 12 ++++-------- lib/components/Shared/PlaybuttonCard.dart | 13 +++---------- lib/components/Shared/SpotubeMarqueeText.dart | 4 ++-- lib/components/Shared/TrackCollectionView.dart | 1 + lib/components/Shared/TrackTile.dart | 1 + lib/main.dart | 4 +--- 19 files changed, 34 insertions(+), 38 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index b3769b2b..6c0bb0d8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "name": "Flutter", "type": "dart", "request": "launch", - "program": "lib/main.dart" + "program": "${workspaceFolder}/lib/main.dart" }, ], "compounds": [] diff --git a/lib/components/Library/UserAlbums.dart b/lib/components/Library/UserAlbums.dart index 866c1ddb..2ac63caf 100644 --- a/lib/components/Library/UserAlbums.dart +++ b/lib/components/Library/UserAlbums.dart @@ -25,6 +25,7 @@ class UserAlbums extends HookConsumerWidget { return SingleChildScrollView( child: Material( type: MaterialType.transparency, + textStyle: PlatformTheme.of(context).textTheme!.body!, color: PlatformTheme.of(context).scaffoldBackgroundColor, child: Container( width: double.infinity, diff --git a/lib/components/Library/UserArtists.dart b/lib/components/Library/UserArtists.dart index 921825de..98102769 100644 --- a/lib/components/Library/UserArtists.dart +++ b/lib/components/Library/UserArtists.dart @@ -31,6 +31,7 @@ class UserArtists extends HookConsumerWidget { return Material( type: MaterialType.transparency, + textStyle: PlatformTheme.of(context).textTheme!.body!, color: PlatformTheme.of(context).scaffoldBackgroundColor, child: GridView.builder( itemCount: artists.length, diff --git a/lib/components/Library/UserPlaylists.dart b/lib/components/Library/UserPlaylists.dart index 705a13dd..ecfb639b 100644 --- a/lib/components/Library/UserPlaylists.dart +++ b/lib/components/Library/UserPlaylists.dart @@ -1,6 +1,7 @@ import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:flutter/material.dart' hide Image; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart'; import 'package:spotube/components/Playlist/PlaylistCard.dart'; @@ -36,6 +37,7 @@ class UserPlaylists extends HookConsumerWidget { return SingleChildScrollView( child: Material( type: MaterialType.transparency, + textStyle: PlatformTheme.of(context).textTheme!.body!, child: Container( width: double.infinity, padding: const EdgeInsets.all(8.0), diff --git a/lib/components/Lyrics/LyricDelayAdjustDialog.dart b/lib/components/Lyrics/LyricDelayAdjustDialog.dart index 402fc896..4a6dbcee 100644 --- a/lib/components/Lyrics/LyricDelayAdjustDialog.dart +++ b/lib/components/Lyrics/LyricDelayAdjustDialog.dart @@ -49,13 +49,10 @@ class LyricDelayAdjustDialog extends HookConsumerWidget { }, ), Flexible( - child: TextField( + child: PlatformTextField( keyboardType: TextInputType.number, controller: controller, - decoration: const InputDecoration( - isDense: true, - hintText: "Delay in milliseconds", - ), + placeholder: "Delay in milliseconds", onSubmitted: (_) { Navigator.of(context).pop( Duration( diff --git a/lib/components/Lyrics/SyncedLyrics.dart b/lib/components/Lyrics/SyncedLyrics.dart index 1edf4f97..2b5c197c 100644 --- a/lib/components/Lyrics/SyncedLyrics.dart +++ b/lib/components/Lyrics/SyncedLyrics.dart @@ -74,6 +74,7 @@ class SyncedLyrics extends HookConsumerWidget { height: breakpoint >= Breakpoints.md ? 50 : 30, child: Material( type: MaterialType.transparency, + textStyle: PlatformTheme.of(context).textTheme!.body!, child: Stack( children: [ Center( diff --git a/lib/components/Player/Player.dart b/lib/components/Player/Player.dart index 37e35c24..c76909ec 100644 --- a/lib/components/Player/Player.dart +++ b/lib/components/Player/Player.dart @@ -90,6 +90,7 @@ class Player extends HookConsumerWidget { ), child: Material( type: MaterialType.transparency, + textStyle: PlatformTheme.of(context).textTheme!.body!, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ diff --git a/lib/components/Player/PlayerOverlay.dart b/lib/components/Player/PlayerOverlay.dart index a36eb9b8..aaa3d67f 100644 --- a/lib/components/Player/PlayerOverlay.dart +++ b/lib/components/Player/PlayerOverlay.dart @@ -62,6 +62,7 @@ class PlayerOverlay extends HookConsumerWidget { duration: const Duration(milliseconds: 250), opacity: canShow ? 1 : 0, child: Material( + textStyle: PlatformTheme.of(context).textTheme!.body!, type: MaterialType.transparency, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/components/Player/PlayerView.dart b/lib/components/Player/PlayerView.dart index 96edfcca..15bcb071 100644 --- a/lib/components/Player/PlayerView.dart +++ b/lib/components/Player/PlayerView.dart @@ -69,6 +69,7 @@ class PlayerView extends HookConsumerWidget { child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), child: Material( + textStyle: PlatformTheme.of(context).textTheme!.body!, color: paletteColor.color.withOpacity(.5), child: SafeArea( child: Column( diff --git a/lib/components/Player/SiblingTracksSheet.dart b/lib/components/Player/SiblingTracksSheet.dart index 605bf1bd..ced57a97 100644 --- a/lib/components/Player/SiblingTracksSheet.dart +++ b/lib/components/Player/SiblingTracksSheet.dart @@ -3,6 +3,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/utils/primitive_utils.dart'; @@ -59,7 +60,7 @@ class SiblingTracksSheet extends HookConsumerWidget { itemCount: playback.siblingYtVideos.length, itemBuilder: (context, index) { final video = playback.siblingYtVideos[index]; - return ListTile( + return PlatformListTile( title: Text(video.title), leading: UniversalImage( path: video.thumbnails.lowResUrl, @@ -69,7 +70,6 @@ class SiblingTracksSheet extends HookConsumerWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), - horizontalTitleGap: 10, trailing: Text( PrimitiveUtils.toReadableDuration( video.duration ?? Duration.zero, diff --git a/lib/components/Playlist/PlaylistCreateDialog.dart b/lib/components/Playlist/PlaylistCreateDialog.dart index 97033d5d..30b52c68 100644 --- a/lib/components/Playlist/PlaylistCreateDialog.dart +++ b/lib/components/Playlist/PlaylistCreateDialog.dart @@ -77,13 +77,13 @@ class PlaylistCreateDialog extends HookConsumerWidget { const SizedBox(height: 10), CheckboxListTile( value: public.value, - title: const Text("Public"), + title: const PlatformText("Public"), onChanged: (val) => public.value = val ?? false, ), const SizedBox(height: 10), CheckboxListTile( value: collaborative.value, - title: const Text("Collaborative"), + title: const PlatformText("Collaborative"), onChanged: (val) => collaborative.value = val ?? false, ), ], diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index 240246a5..681a27e3 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -78,6 +78,7 @@ class Search extends HookConsumerWidget { return SafeArea( child: Material( color: PlatformTheme.of(context).scaffoldBackgroundColor, + textStyle: PlatformTheme.of(context).textTheme!.body!, child: Column( children: [ Container( diff --git a/lib/components/Settings/About.dart b/lib/components/Settings/About.dart index 0d44dfb1..789c413a 100644 --- a/lib/components/Settings/About.dart +++ b/lib/components/Settings/About.dart @@ -32,7 +32,7 @@ class About extends HookWidget { return PlatformListTile( leading: const Icon(Icons.info_outline_rounded), - title: Text( + title: PlatformText( "About Spotube", style: PlatformTextTheme.of(context).body, ), @@ -48,7 +48,7 @@ class About extends HookWidget { Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ - Text("Author: "), + PlatformText("Author: "), Hyperlink( "Kingkor Roy Tirtho", "https://github.com/KRTirtho", @@ -63,12 +63,12 @@ class About extends HookWidget { "💚 Sponsor/Donate 💚", "https://opencollective.com/spotube", ), - Text(" • "), + PlatformText(" • "), Hyperlink( "BSD-4-Clause LICENSE", "https://github.com/KRTirtho/spotube/blob/master/LICENSE", ), - Text(" • "), + PlatformText(" • "), Hyperlink( "Bug Report", "https://github.com/KRTirtho/spotube/issues/new?assignees=&labels=bug&template=bug_report.md&title=", @@ -76,7 +76,8 @@ class About extends HookWidget { ], ), const SizedBox(height: 10), - const Center(child: Text("© Spotube 2022. All rights reserved")) + const Center( + child: PlatformText("© Spotube 2022. All rights reserved")) ]); }, ); diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index 4bf361a0..8b982342 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:macos_ui/macos_ui.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Settings/About.dart'; import 'package:spotube/components/Settings/ColorSchemePickerDialog.dart'; @@ -339,17 +338,14 @@ class Settings extends HookConsumerWidget { ), AdaptiveListTile( leading: const Icon(Icons.screen_search_desktop_rounded), - title: SizedBox( + title: const SizedBox( height: 50, width: 200, child: Align( alignment: Alignment.centerLeft, - child: DefaultTextStyle( - style: PlatformTextTheme.of(context).body!, - child: const AutoSizeText( - "Format of the YouTube Search term", - maxLines: 2, - ), + child: AutoSizeText( + "Format of the YouTube Search term", + maxLines: 2, ), ), ), diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index 7f88d17e..3ecb8eb2 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -1,8 +1,5 @@ -import 'package:fluent_ui/fluent_ui.dart' as FluentUI; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:macos_ui/macos_ui.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Shared/HoverBuilder.dart'; import 'package:spotube/components/Shared/SpotubeMarqueeText.dart'; @@ -54,10 +51,6 @@ class PlaybuttonCard extends HookWidget { ), ); - final titleStyle = PlatformTextTheme.of(context).body; - - final descriptionStyle = PlatformTextTheme.of(context).caption; - final splash = usePlatformProperty( (context) => PlatformProperty.multiPlatformGroup({ InkRipple.splashFactory: {TargetPlatform.android, TargetPlatform.linux}, @@ -164,8 +157,8 @@ class PlaybuttonCard extends HookWidget { height: 20, child: SpotubeMarqueeText( text: title, - style: titleStyle?.copyWith( - fontWeight: FontWeight.bold), + style: + const TextStyle(fontWeight: FontWeight.bold), isHovering: isHovering, ), ), @@ -176,7 +169,7 @@ class PlaybuttonCard extends HookWidget { height: 30, child: SpotubeMarqueeText( text: description!, - style: descriptionStyle, + style: PlatformTextTheme.of(context).caption, isHovering: isHovering, ), ), diff --git a/lib/components/Shared/SpotubeMarqueeText.dart b/lib/components/Shared/SpotubeMarqueeText.dart index 692a0027..65583b32 100644 --- a/lib/components/Shared/SpotubeMarqueeText.dart +++ b/lib/components/Shared/SpotubeMarqueeText.dart @@ -26,12 +26,12 @@ class SpotubeMarqueeText extends HookWidget { return AutoSizeText( text, minFontSize: 13, - style: style, + style: DefaultTextStyle.of(context).style.merge(style), maxLines: 1, overflowReplacement: Marquee( key: uKey.value, text: text, - style: style, + style: DefaultTextStyle.of(context).style.merge(style), scrollAxis: Axis.horizontal, crossAxisAlignment: CrossAxisAlignment.start, blankSpace: 40.0, diff --git a/lib/components/Shared/TrackCollectionView.dart b/lib/components/Shared/TrackCollectionView.dart index b42ef7cc..b29f1281 100644 --- a/lib/components/Shared/TrackCollectionView.dart +++ b/lib/components/Shared/TrackCollectionView.dart @@ -155,6 +155,7 @@ class TrackCollectionView extends HookConsumerWidget { ), ), child: Material( + textStyle: PlatformTheme.of(context).textTheme!.body!, type: MaterialType.transparency, child: Padding( padding: const EdgeInsets.symmetric( diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index dedcb1bc..e9ab605a 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -177,6 +177,7 @@ class TrackTile extends HookConsumerWidget { ), child: Material( type: MaterialType.transparency, + textStyle: PlatformTheme.of(context).textTheme!.body!, child: Row( children: [ if (showCheck) diff --git a/lib/main.dart b/lib/main.dart index 0d7dbbe4..73df9599 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,13 +3,11 @@ import 'dart:convert'; import 'package:audio_service/audio_service.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:fl_query/fl_query.dart'; -import 'package:fluent_ui/fluent_ui.dart' as FluentUI; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:macos_ui/macos_ui.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotube/components/Shared/ReplaceDownloadedFileDialog.dart'; @@ -201,7 +199,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.windows; + platform = TargetPlatform.macOS; return PlatformApp.router( routeInformationParser: router.routeInformationParser, From 2211505d713cef752d07cafb9791a49e8095eee2 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 3 Nov 2022 20:11:20 +0600 Subject: [PATCH 08/23] feat: use platform checkbox --- lib/components/Playlist/PlaylistCreateDialog.dart | 8 ++++---- lib/components/Shared/TrackTile.dart | 8 +++----- lib/components/Shared/TracksTableView.dart | 2 +- lib/main.dart | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/components/Playlist/PlaylistCreateDialog.dart b/lib/components/Playlist/PlaylistCreateDialog.dart index 30b52c68..15503f17 100644 --- a/lib/components/Playlist/PlaylistCreateDialog.dart +++ b/lib/components/Playlist/PlaylistCreateDialog.dart @@ -75,15 +75,15 @@ class PlaylistCreateDialog extends HookConsumerWidget { maxLines: 5, ), const SizedBox(height: 10), - CheckboxListTile( + PlatformCheckbox( value: public.value, - title: const PlatformText("Public"), + label: const PlatformText("Public"), onChanged: (val) => public.value = val ?? false, ), const SizedBox(height: 10), - CheckboxListTile( + PlatformCheckbox( value: collaborative.value, - title: const PlatformText("Collaborative"), + label: const PlatformText("Collaborative"), onChanged: (val) => collaborative.value = val ?? false, ), ], diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index e9ab605a..8ecdadd9 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -137,10 +137,8 @@ class TrackTile extends HookConsumerWidget { itemCount: snapshot.data!.length, itemBuilder: (context, index) { final playlist = snapshot.data!.elementAt(index); - return CheckboxListTile( - title: PlatformText(playlist.name!), - controlAffinity: - ListTileControlAffinity.leading, + return PlatformCheckbox( + label: PlatformText(playlist.name!), value: playlistsCheck.value[playlist.id] ?? false, onChanged: (val) { @@ -181,7 +179,7 @@ class TrackTile extends HookConsumerWidget { child: Row( children: [ if (showCheck) - Checkbox( + PlatformCheckbox( value: isChecked, onChanged: (s) => onCheckChange?.call(s), ) diff --git a/lib/components/Shared/TracksTableView.dart b/lib/components/Shared/TracksTableView.dart index 1dca965f..4dea4e04 100644 --- a/lib/components/Shared/TracksTableView.dart +++ b/lib/components/Shared/TracksTableView.dart @@ -68,7 +68,7 @@ class TracksTableView extends HookConsumerWidget { if (heading != null) heading!, Row( children: [ - Checkbox( + PlatformCheckbox( value: selected.value.length == sortedTracks.length, onChanged: (checked) { if (!showCheck.value) showCheck.value = true; diff --git a/lib/main.dart b/lib/main.dart index 73df9599..c64ced90 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -199,7 +199,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.macOS; + platform = TargetPlatform.android; return PlatformApp.router( routeInformationParser: router.routeInformationParser, From 5e96913ba34230e7a15d62060d5dae28d80e3630 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 4 Nov 2022 11:36:33 +0600 Subject: [PATCH 09/23] feat: dialog logo for macos, settings more width for country picker --- lib/components/Home/Sidebar.dart | 6 +++--- lib/components/Library/UserLibrary.dart | 1 + lib/components/Lyrics/LyricDelayAdjustDialog.dart | 2 ++ lib/components/Player/PlayerControls.dart | 1 - lib/components/Playlist/PlaylistCreateDialog.dart | 2 ++ lib/components/Settings/ColorSchemePickerDialog.dart | 2 ++ lib/components/Settings/Settings.dart | 2 +- lib/components/Shared/AdaptiveListTile.dart | 2 ++ lib/components/Shared/DownloadConfirmationDialog.dart | 2 ++ lib/components/Shared/PageWindowTitleBar.dart | 4 +++- lib/components/Shared/ReplaceDownloadedFileDialog.dart | 2 ++ lib/components/Shared/TrackTile.dart | 2 ++ lib/hooks/useUpdateChecker.dart | 2 ++ lib/main.dart | 2 +- lib/themes/dark-theme.dart | 2 +- 15 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart index f31acde4..483ba397 100644 --- a/lib/components/Home/Sidebar.dart +++ b/lib/components/Home/Sidebar.dart @@ -32,7 +32,7 @@ class Sidebar extends HookConsumerWidget { Key? key, }) : super(key: key); - Widget _buildSmallLogo() { + static Widget brandLogo() { return Image.asset( "assets/spotube-logo.png", height: 50, @@ -135,7 +135,7 @@ class Sidebar extends HookConsumerWidget { (extended.value) ? Row( children: [ - _buildSmallLogo(), + brandLogo(), const SizedBox( width: 10, ), @@ -149,7 +149,7 @@ class Sidebar extends HookConsumerWidget { ), ], ) - : _buildSmallLogo(), + : brandLogo(), ], ), windowsFooterItems: [ diff --git a/lib/components/Library/UserLibrary.dart b/lib/components/Library/UserLibrary.dart index ef0134d0..24718674 100644 --- a/lib/components/Library/UserLibrary.dart +++ b/lib/components/Library/UserLibrary.dart @@ -16,6 +16,7 @@ class UserLibrary extends ConsumerWidget { length: 5, child: SafeArea( child: PlatformTabView( + androidIsScrollable: true, placement: PlatformProperty.all(PlatformTabbarPlacement.top), body: { PlatformTab( diff --git a/lib/components/Lyrics/LyricDelayAdjustDialog.dart b/lib/components/Lyrics/LyricDelayAdjustDialog.dart index 4a6dbcee..65ead135 100644 --- a/lib/components/Lyrics/LyricDelayAdjustDialog.dart +++ b/lib/components/Lyrics/LyricDelayAdjustDialog.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:platform_ui/platform_ui.dart'; +import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/components/Lyrics/SyncedLyrics.dart'; class LyricDelayAdjustDialog extends HookConsumerWidget { @@ -17,6 +18,7 @@ class LyricDelayAdjustDialog extends HookConsumerWidget { double.tryParse(controller.text.replaceAll("ms", "")) ?? 0; return PlatformAlertDialog( + icon: Sidebar.brandLogo(), title: const Center(child: Text("Adjust Lyrics Delay")), secondaryActions: [ PlatformFilledButton( diff --git a/lib/components/Player/PlayerControls.dart b/lib/components/Player/PlayerControls.dart index 580a346c..71779116 100644 --- a/lib/components/Player/PlayerControls.dart +++ b/lib/components/Player/PlayerControls.dart @@ -98,7 +98,6 @@ class PlayerControls extends HookConsumerWidget { PlatformTooltip( message: "Slide to seek forward or backward", child: PlatformSlider( - focusNode: FocusNode(), // cannot divide by zero // there's an edge case for value being bigger // than total duration. Keeping it resolved diff --git a/lib/components/Playlist/PlaylistCreateDialog.dart b/lib/components/Playlist/PlaylistCreateDialog.dart index 15503f17..ab96fc77 100644 --- a/lib/components/Playlist/PlaylistCreateDialog.dart +++ b/lib/components/Playlist/PlaylistCreateDialog.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:platform_ui/platform_ui.dart'; +import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; @@ -25,6 +26,7 @@ class PlaylistCreateDialog extends HookConsumerWidget { final collaborative = useState(false); return PlatformAlertDialog( + icon: Sidebar.brandLogo(), title: const Text("Create a Playlist"), primaryActions: [ PlatformFilledButton( diff --git a/lib/components/Settings/ColorSchemePickerDialog.dart b/lib/components/Settings/ColorSchemePickerDialog.dart index 8bd14935..84a515d4 100644 --- a/lib/components/Settings/ColorSchemePickerDialog.dart +++ b/lib/components/Settings/ColorSchemePickerDialog.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:platform_ui/platform_ui.dart'; +import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/provider/UserPreferences.dart'; final highContrast = MaterialColor( @@ -67,6 +68,7 @@ class ColorSchemePickerDialog extends HookConsumerWidget { ).key); return PlatformAlertDialog( + icon: Sidebar.brandLogo(), title: Text("Pick ${schemeType.name} color scheme"), primaryActions: [ PlatformFilledButton( diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index 8b982342..908c1330 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -315,7 +315,7 @@ class Settings extends HookConsumerWidget { "Recommendation Country", ), trailing: (context, update) => ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 250), + constraints: const BoxConstraints(maxWidth: 350), child: PlatformDropDownMenu( value: preferences.recommendationMarket, items: spotifyMarkets diff --git a/lib/components/Shared/AdaptiveListTile.dart b/lib/components/Shared/AdaptiveListTile.dart index 3d219ee8..e2a0eeae 100644 --- a/lib/components/Shared/AdaptiveListTile.dart +++ b/lib/components/Shared/AdaptiveListTile.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:platform_ui/platform_ui.dart'; +import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; class AdaptiveListTile extends HookWidget { @@ -39,6 +40,7 @@ class AdaptiveListTile extends HookWidget { builder: (context) { return StatefulBuilder(builder: (context, update) { return PlatformAlertDialog( + icon: Sidebar.brandLogo(), title: title != null ? Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/components/Shared/DownloadConfirmationDialog.dart b/lib/components/Shared/DownloadConfirmationDialog.dart index 5e770b31..d2f6d68c 100644 --- a/lib/components/Shared/DownloadConfirmationDialog.dart +++ b/lib/components/Shared/DownloadConfirmationDialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:platform_ui/platform_ui.dart'; +import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; class DownloadConfirmationDialog extends StatelessWidget { @@ -8,6 +9,7 @@ class DownloadConfirmationDialog extends StatelessWidget { @override Widget build(BuildContext context) { return PlatformAlertDialog( + icon: Sidebar.brandLogo(), title: Padding( padding: const EdgeInsets.all(15), child: Row( diff --git a/lib/components/Shared/PageWindowTitleBar.dart b/lib/components/Shared/PageWindowTitleBar.dart index 06fd4923..9f139b8b 100644 --- a/lib/components/Shared/PageWindowTitleBar.dart +++ b/lib/components/Shared/PageWindowTitleBar.dart @@ -1,4 +1,5 @@ import 'package:bitsdojo_window/bitsdojo_window.dart'; +import 'package:fluent_ui/fluent_ui.dart' show FluentTheme; import 'package:flutter/material.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/utils/platform.dart'; @@ -112,7 +113,8 @@ class PageWindowTitleBar extends StatelessWidget } return WindowTitleBarBox( child: Container( - color: backgroundColor, + color: backgroundColor ?? + FluentTheme.maybeOf(context)?.micaBackgroundColor, child: Row( children: [ if (kIsMacOS) diff --git a/lib/components/Shared/ReplaceDownloadedFileDialog.dart b/lib/components/Shared/ReplaceDownloadedFileDialog.dart index 58263746..1c3b16f0 100644 --- a/lib/components/Shared/ReplaceDownloadedFileDialog.dart +++ b/lib/components/Shared/ReplaceDownloadedFileDialog.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; +import 'package:spotube/components/Home/Sidebar.dart'; final replaceDownloadedFileState = StateProvider((ref) => null); @@ -15,6 +16,7 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget { final groupValue = ref.watch(replaceDownloadedFileState); return PlatformAlertDialog( + icon: Sidebar.brandLogo(), title: Text("Track ${track.name} Already Exists"), content: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index 8ecdadd9..dc29f82b 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart' hide Image; +import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/components/Shared/AdaptivePopupMenuButton.dart'; import 'package:spotube/components/Shared/HeartButton.dart'; import 'package:spotube/components/Shared/LinkText.dart'; @@ -95,6 +96,7 @@ class TrackTile extends HookConsumerWidget { return HookBuilder(builder: (context) { final playlistsCheck = useState({}); return PlatformAlertDialog( + icon: Sidebar.brandLogo(), title: PlatformText( "Add `${track.value.name}` to following Playlists", style: const TextStyle( diff --git a/lib/hooks/useUpdateChecker.dart b/lib/hooks/useUpdateChecker.dart index 8105b119..797e6dc3 100644 --- a/lib/hooks/useUpdateChecker.dart +++ b/lib/hooks/useUpdateChecker.dart @@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:platform_ui/platform_ui.dart'; +import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/components/Shared/AnchorButton.dart'; import 'package:spotube/hooks/usePackageInfo.dart'; import 'package:spotube/provider/UserPreferences.dart'; @@ -56,6 +57,7 @@ void useUpdateChecker(WidgetRef ref) { const url = "https://spotube.netlify.app/other-downloads/stable-downloads"; return PlatformAlertDialog( + icon: Sidebar.brandLogo(), title: const PlatformText("Spotube has an update"), primaryActions: [ PlatformFilledButton( diff --git a/lib/main.dart b/lib/main.dart index c64ced90..73df9599 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -199,7 +199,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.android; + platform = TargetPlatform.macOS; return PlatformApp.router( routeInformationParser: router.routeInformationParser, diff --git a/lib/themes/dark-theme.dart b/lib/themes/dark-theme.dart index 057b380d..c47abeac 100644 --- a/lib/themes/dark-theme.dart +++ b/lib/themes/dark-theme.dart @@ -72,7 +72,7 @@ ThemeData darkTheme({ ), dialogTheme: DialogTheme(backgroundColor: backgroundMaterialColor[900]), cardColor: backgroundMaterialColor[800], - canvasColor: backgroundMaterialColor[900], + canvasColor: backgroundMaterialColor[800], listTileTheme: const ListTileThemeData(horizontalTitleGap: 0), checkboxTheme: CheckboxThemeData( fillColor: MaterialStateProperty.resolveWith((states) { From 36c5e02f18374100f61cc3f2957c27bfc0d8511f Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 4 Nov 2022 12:09:27 +0600 Subject: [PATCH 10/23] feat: change default platform option and platform specific back button --- lib/components/Artist/ArtistProfile.dart | 2 +- lib/components/Login/TokenLogin.dart | 2 +- lib/components/Player/PlayerView.dart | 2 +- .../Playlist/PlaylistGenreView.dart | 2 +- lib/components/Settings/Settings.dart | 37 +++++++++++++++++++ lib/components/Shared/AdaptiveListTile.dart | 1 + .../Shared/TrackCollectionView.dart | 4 +- lib/main.dart | 14 ++++++- 8 files changed, 58 insertions(+), 6 deletions(-) diff --git a/lib/components/Artist/ArtistProfile.dart b/lib/components/Artist/ArtistProfile.dart index b16d011a..bf9150c1 100644 --- a/lib/components/Artist/ArtistProfile.dart +++ b/lib/components/Artist/ArtistProfile.dart @@ -55,7 +55,7 @@ class ArtistProfile extends HookConsumerWidget { return SafeArea( child: PlatformScaffold( appBar: const PageWindowTitleBar( - leading: BackButton(), + leading: PlatformBackButton(), ), body: HookBuilder( builder: (context) { diff --git a/lib/components/Login/TokenLogin.dart b/lib/components/Login/TokenLogin.dart index e7a81657..ad5b51d7 100644 --- a/lib/components/Login/TokenLogin.dart +++ b/lib/components/Login/TokenLogin.dart @@ -16,7 +16,7 @@ class TokenLogin extends HookConsumerWidget { return SafeArea( child: Scaffold( - appBar: const PageWindowTitleBar(leading: BackButton()), + appBar: const PageWindowTitleBar(leading: PlatformBackButton()), body: SingleChildScrollView( child: Center( child: Container( diff --git a/lib/components/Player/PlayerView.dart b/lib/components/Player/PlayerView.dart index 15bcb071..0878a296 100644 --- a/lib/components/Player/PlayerView.dart +++ b/lib/components/Player/PlayerView.dart @@ -75,7 +75,7 @@ class PlayerView extends HookConsumerWidget { child: Column( children: [ PageWindowTitleBar( - leading: const BackButton(), + leading: const PlatformBackButton(), backgroundColor: Colors.transparent, foregroundColor: paletteColor.titleTextColor, ), diff --git a/lib/components/Playlist/PlaylistGenreView.dart b/lib/components/Playlist/PlaylistGenreView.dart index a9191b6a..94fb89c5 100644 --- a/lib/components/Playlist/PlaylistGenreView.dart +++ b/lib/components/Playlist/PlaylistGenreView.dart @@ -21,7 +21,7 @@ class PlaylistGenreView extends ConsumerWidget { Widget build(BuildContext context, ref) { return Scaffold( appBar: const PageWindowTitleBar( - leading: BackButton(), + leading: PlatformBackButton(), ), body: Column( children: [ diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index 908c1330..1049365f 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -10,6 +10,7 @@ import 'package:spotube/components/Settings/ColorSchemePickerDialog.dart'; import 'package:spotube/components/Shared/AdaptiveListTile.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; +import 'package:spotube/main.dart'; import 'package:spotube/models/SpotifyMarkets.dart'; import 'package:spotube/models/SpotubeTrack.dart'; import 'package:spotube/provider/Auth.dart'; @@ -204,6 +205,42 @@ class Settings extends HookConsumerWidget { }, ), ), + AdaptiveListTile( + leading: const Icon(Icons.ad_units_rounded), + title: const PlatformText("Mimic Platform"), + trailing: (context, update) => + PlatformDropDownMenu( + value: Spotube.of(context).appPlatform, + items: [ + PlatformDropDownMenuItem( + value: TargetPlatform.android, + child: const PlatformText("Android (Material You)"), + ), + PlatformDropDownMenuItem( + value: TargetPlatform.iOS, + child: const PlatformText("iOS (Cupertino)"), + ), + PlatformDropDownMenuItem( + value: TargetPlatform.macOS, + child: const PlatformText("macOS (Aqua)"), + ), + PlatformDropDownMenuItem( + value: TargetPlatform.linux, + child: const PlatformText("Linux (GTK+Libadwaita)"), + ), + PlatformDropDownMenuItem( + value: TargetPlatform.windows, + child: const PlatformText("Windows 11 (Fluent UI)"), + ), + ], + onChanged: (value) { + if (value != null) { + Spotube.of(context).changePlatform(value); + update?.call(() {}); + } + }, + ), + ), PlatformListTile( leading: const Icon(Icons.palette_outlined), title: const PlatformText("Accent Color Scheme"), diff --git a/lib/components/Shared/AdaptiveListTile.dart b/lib/components/Shared/AdaptiveListTile.dart index e2a0eeae..9bb07bcd 100644 --- a/lib/components/Shared/AdaptiveListTile.dart +++ b/lib/components/Shared/AdaptiveListTile.dart @@ -37,6 +37,7 @@ class AdaptiveListTile extends HookWidget { onTap?.call(); showPlatformAlertDialog( context, + barrierDismissible: true, builder: (context) { return StatefulBuilder(builder: (context, update) { return PlatformAlertDialog( diff --git a/lib/components/Shared/TrackCollectionView.dart b/lib/components/Shared/TrackCollectionView.dart index b29f1281..c4bbb608 100644 --- a/lib/components/Shared/TrackCollectionView.dart +++ b/lib/components/Shared/TrackCollectionView.dart @@ -115,7 +115,9 @@ class TrackCollectionView extends HookConsumerWidget { backgroundColor: color?.color, foregroundColor: color?.titleTextColor, leading: Row( - children: [BackButton(color: color?.titleTextColor)], + children: [ + PlatformBackButton(color: color?.titleTextColor) + ], ), ) : null, diff --git a/lib/main.dart b/lib/main.dart index 73df9599..e3ef5f70 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -141,6 +141,11 @@ class Spotube extends StatefulHookConsumerWidget { @override SpotubeState createState() => SpotubeState(); + + /// ↓↓ ADDED + /// InheritedWidget style accessor to our State object. + static SpotubeState of(BuildContext context) => + context.findAncestorStateOfType()!; } class SpotubeState extends ConsumerState with WidgetsBindingObserver { @@ -181,6 +186,13 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { prevSize = appWindow.size; } + TargetPlatform appPlatform = TargetPlatform.android; + + void changePlatform(TargetPlatform targetPlatform) { + appPlatform = targetPlatform; + setState(() {}); + } + @override Widget build(BuildContext context) { final themeMode = @@ -199,7 +211,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { }; }, []); - platform = TargetPlatform.macOS; + platform = appPlatform; return PlatformApp.router( routeInformationParser: router.routeInformationParser, From e795e23e42e5f1f832963b2a4506df89b7df5baa Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sun, 6 Nov 2022 14:48:13 +0600 Subject: [PATCH 11/23] feat: libadwaita theming, track tile and PlayButtonCard play button icon fix --- lib/components/Home/Sidebar.dart | 5 +- lib/components/Player/Player.dart | 7 ++- lib/components/Search/Search.dart | 5 +- lib/components/Shared/PlaybuttonCard.dart | 23 ++++++--- lib/components/Shared/TrackTile.dart | 26 ++++++---- lib/main.dart | 2 + lib/themes/light-theme.dart | 14 ++++++ pubspec.lock | 58 ++++++++++++++++++++++- pubspec.yaml | 2 + 9 files changed, 115 insertions(+), 27 deletions(-) diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart index 483ba397..4ee0de2c 100644 --- a/lib/components/Home/Sidebar.dart +++ b/lib/components/Home/Sidebar.dart @@ -139,10 +139,7 @@ class Sidebar extends HookConsumerWidget { const SizedBox( width: 10, ), - Text( - "Spotube", - style: PlatformTheme.of(context).textTheme?.headline, - ), + PlatformText.headline("Spotube"), PlatformIconButton( icon: const Icon(Icons.menu_rounded), onPressed: toggleExtended, diff --git a/lib/components/Player/Player.dart b/lib/components/Player/Player.dart index c76909ec..616f787f 100644 --- a/lib/components/Player/Player.dart +++ b/lib/components/Player/Player.dart @@ -78,7 +78,12 @@ class Player extends HookConsumerWidget { width: 1, ), ), - linux: null, + linux: Border( + top: BorderSide( + color: PlatformTheme.of(context).borderColor ?? Colors.transparent, + width: 1, + ), + ), windows: null, ), ); diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index 681a27e3..aad3dfc0 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -90,10 +90,7 @@ class Search extends HookConsumerWidget { onChanged: (value) { ref.read(searchTermStateProvider.notifier).state = value; }, - suffix: PlatformFilledButton( - onPressed: onSearch, - child: const Icon(Icons.search_rounded), - ), + prefixIcon: Icons.search_rounded, placeholder: "Search...", onSubmitted: (value) { onSearch(); diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index 3ecb8eb2..33d3558d 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -42,10 +42,8 @@ class PlaybuttonCard extends HookWidget { ios: null, macos: null, linux: BoxShadow( - blurRadius: 10, - offset: const Offset(0, 3), - spreadRadius: 5, - color: Theme.of(context).shadowColor, + blurRadius: 6, + color: Theme.of(context).shadowColor.withOpacity(0.3), ), windows: null, ), @@ -78,7 +76,10 @@ class PlaybuttonCard extends HookWidget { decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular( - platform == TargetPlatform.windows ? 5 : 8, + [TargetPlatform.windows, TargetPlatform.linux] + .contains(platform) + ? 5 + : 8, ), boxShadow: [ if (boxShadow != null) boxShadow, @@ -104,7 +105,10 @@ class PlaybuttonCard extends HookWidget { ), child: ClipRRect( borderRadius: BorderRadius.circular( - platform == TargetPlatform.windows ? 5 : 8, + [TargetPlatform.windows, TargetPlatform.linux] + .contains(platform) + ? 5 + : 8, ), child: UniversalImage( path: imageUrl, @@ -126,6 +130,11 @@ class PlaybuttonCard extends HookWidget { ), child: PlatformIconButton( onPressed: onPlaybuttonPressed, + backgroundColor: + PlatformTheme.of(context).primaryColor, + hoverColor: PlatformTheme.of(context) + .primaryColor + ?.withOpacity(0.5), icon: isLoading ? const SizedBox( height: 23, @@ -137,7 +146,7 @@ class PlaybuttonCard extends HookWidget { isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded, - color: backgroundColor, + color: Colors.white, ), ), ); diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index dc29f82b..d7984c67 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -214,16 +214,22 @@ class TrackTile extends HookConsumerWidget { ), ), ), - PlatformIconButton( - icon: Icon( - playback.track?.id != null && - playback.track?.id == track.value.id - ? Icons.pause_circle_rounded - : Icons.play_circle_rounded, - color: PlatformTheme.of(context).primaryColor, - ), - onPressed: () => onTrackPlayButtonPressed?.call( - track.value, + Padding( + padding: const EdgeInsets.all(8.0).copyWith(left: 0), + child: PlatformIconButton( + icon: Icon( + playback.track?.id != null && + playback.track?.id == track.value.id + ? Icons.pause_rounded + : Icons.play_arrow_rounded, + color: Colors.white, + ), + backgroundColor: PlatformTheme.of(context).primaryColor, + hoverColor: + PlatformTheme.of(context).primaryColor?.withOpacity(0.5), + onPressed: () => onTrackPlayButtonPressed?.call( + track.value, + ), ), ), Expanded( diff --git a/lib/main.dart b/lib/main.dart index e3ef5f70..08fb65e5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -227,6 +227,8 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { accentMaterialColor: accentMaterialColor, backgroundMaterialColor: backgroundMaterialColor, ), + linuxTheme: linuxTheme, + linuxDarkTheme: linuxDarkTheme, iosTheme: themeMode == ThemeMode.dark ? iosDarkTheme : iosTheme, windowsTheme: windowsTheme, windowsDarkTheme: windowsDarkTheme, diff --git a/lib/themes/light-theme.dart b/lib/themes/light-theme.dart index 0b4ab77e..8d19881e 100644 --- a/lib/themes/light-theme.dart +++ b/lib/themes/light-theme.dart @@ -1,3 +1,4 @@ +import 'package:adwaita/adwaita.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:macos_ui/macos_ui.dart'; @@ -149,3 +150,16 @@ final macosDarkTheme = MacosThemeData.dark().copyWith( ); const iosTheme = CupertinoThemeData(brightness: Brightness.light); const iosDarkTheme = CupertinoThemeData(brightness: Brightness.dark); + +final linuxTheme = AdwaitaThemeData.light().copyWith( + listTileTheme: ListTileThemeData( + iconColor: Colors.grey[900], + horizontalTitleGap: 0, + ), +); +final linuxDarkTheme = AdwaitaThemeData.dark().copyWith( + listTileTheme: ListTileThemeData( + iconColor: Colors.grey[50], + horizontalTitleGap: 0, + ), +); diff --git a/pubspec.lock b/pubspec.lock index b9dfa5e2..35514ea4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "47.0.0" + adwaita: + dependency: "direct main" + description: + name: adwaita + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.2" analyzer: dependency: transitive description: @@ -594,6 +601,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.42.0" + flutter_svg: + dependency: transitive + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" flutter_test: dependency: "direct dev" description: flutter @@ -639,6 +653,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + gsettings: + dependency: transitive + description: + name: gsettings + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.5" hive: dependency: "direct main" description: @@ -737,6 +758,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.5.0" + libadwaita: + dependency: "direct main" + description: + name: libadwaita + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.5" + libadwaita_core: + dependency: transitive + description: + name: libadwaita_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.4" lints: dependency: transitive description: @@ -893,6 +928,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.2" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" path_provider: dependency: "direct main" description: @@ -1004,7 +1053,7 @@ packages: path: "../platform_ui" relative: true source: path - version: "0.0.1" + version: "0.1.0" plugin_platform_interface: dependency: transitive description: @@ -1026,6 +1075,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.6+3" + popover_gtk: + dependency: transitive + description: + name: popover_gtk + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.6+3" process: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 98cb2795..a5e549c1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -66,6 +66,8 @@ dependencies: path: ../platform_ui fluent_ui: ^4.0.3 macos_ui: ^1.7.5 + libadwaita: ^1.2.5 + adwaita: ^0.5.2 dev_dependencies: flutter_test: From ff1446982f0260c6fe231970aa6ed61c273fdf07 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 7 Nov 2022 11:08:06 +0600 Subject: [PATCH 12/23] feat: platform bottom navigation bar add --- lib/components/Home/Sidebar.dart | 2 +- lib/components/Home/SpotubeNavigationBar.dart | 30 +++++-------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart index 4ee0de2c..6cda41d6 100644 --- a/lib/components/Home/Sidebar.dart +++ b/lib/components/Home/Sidebar.dart @@ -75,7 +75,7 @@ class Sidebar extends HookConsumerWidget { if (layoutMode == LayoutMode.compact || (breakpoints.isSm && layoutMode == LayoutMode.adaptive)) { - return child; + return PlatformScaffold(body: child); } void toggleExtended() => diff --git a/lib/components/Home/SpotubeNavigationBar.dart b/lib/components/Home/SpotubeNavigationBar.dart index fbac8a4a..e7bc268a 100644 --- a/lib/components/Home/SpotubeNavigationBar.dart +++ b/lib/components/Home/SpotubeNavigationBar.dart @@ -1,7 +1,7 @@ -import 'package:badges/badges.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Home/Sidebar.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; import 'package:spotube/models/sideBarTiles.dart'; @@ -37,37 +37,23 @@ class SpotubeNavigationBar extends HookConsumerWidget { if (layoutMode == LayoutMode.extended || (breakpoint.isMoreThan(Breakpoints.sm) && layoutMode == LayoutMode.adaptive)) return const SizedBox(); - return NavigationBar( - destinations: [ + return PlatformBottomNavigationBar( + items: [ ...sidebarTileList.map( (e) { - final icon = Icon(e.icon); - return NavigationDestination( - icon: e.title == "Library" && downloadCount > 0 - ? Badge( - badgeColor: Colors.red[100]!, - badgeContent: Text( - downloadCount.toString(), - style: const TextStyle( - color: Colors.red, - fontWeight: FontWeight.bold, - ), - ), - animationType: BadgeAnimationType.fade, - child: icon, - ) - : icon, + return PlatformBottomNavigationBarItem( + icon: e.icon, label: e.title, ); }, ), - const NavigationDestination( - icon: Icon(Icons.settings_rounded), + const PlatformBottomNavigationBarItem( + icon: Icons.settings_rounded, label: "Settings", ) ], selectedIndex: insideSelectedIndex.value, - onDestinationSelected: (i) { + onSelectedIndexChanged: (i) { if (i == 4) { insideSelectedIndex.value = 4; Sidebar.goToSettings(context); From 54048cbfc37d9a40c020eb81ab67b9dd5428f0d7 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 11 Nov 2022 11:20:43 +0600 Subject: [PATCH 13/23] feat: platform title bar buttons add --- lib/components/Shared/PageWindowTitleBar.dart | 3 +-- lib/main.dart | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/components/Shared/PageWindowTitleBar.dart b/lib/components/Shared/PageWindowTitleBar.dart index 9f139b8b..df4a0350 100644 --- a/lib/components/Shared/PageWindowTitleBar.dart +++ b/lib/components/Shared/PageWindowTitleBar.dart @@ -123,8 +123,7 @@ class PageWindowTitleBar extends StatelessWidget ), if (leading != null) leading!, Expanded(child: MoveWindow(child: Center(child: center))), - if (!kIsMacOS && !kIsMobile) - TitleBarActionButtons(color: foregroundColor) + if (!kIsMacOS && !kIsMobile) const PlatformWindowButtons() ], ), ), diff --git a/lib/main.dart b/lib/main.dart index 08fb65e5..4939a7b5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -235,6 +235,13 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { macosTheme: macosTheme, macosDarkTheme: macosDarkTheme, themeMode: themeMode, + windowButtonConfig: PlatformWindowButtonConfig( + isMaximized: appWindow.isMaximized, + onClose: appWindow.close, + onRestore: appWindow.restore, + onMaximize: appWindow.maximize, + onMinimize: appWindow.minimize, + ), shortcuts: PlatformProperty.all({ ...WidgetsApp.defaultShortcuts.map((key, value) { return MapEntry( From e659e3c56fb02ad8a1c5114bf80074f0324cc4f1 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 11 Nov 2022 13:01:23 +0600 Subject: [PATCH 14/23] feat(title_bar): platform specific title bar --- lib/components/Artist/ArtistProfile.dart | 4 +- lib/components/Home/Shell.dart | 52 +++--- lib/components/Home/Sidebar.dart | 2 +- lib/components/Login/TokenLogin.dart | 2 +- .../Playlist/PlaylistGenreView.dart | 4 +- lib/components/Shared/PageWindowTitleBar.dart | 149 +++--------------- lib/main.dart | 2 +- 7 files changed, 59 insertions(+), 156 deletions(-) diff --git a/lib/components/Artist/ArtistProfile.dart b/lib/components/Artist/ArtistProfile.dart index bf9150c1..e5924e02 100644 --- a/lib/components/Artist/ArtistProfile.dart +++ b/lib/components/Artist/ArtistProfile.dart @@ -54,8 +54,8 @@ class ArtistProfile extends HookConsumerWidget { return SafeArea( child: PlatformScaffold( - appBar: const PageWindowTitleBar( - leading: PlatformBackButton(), + appBar: PageWindowTitleBar( + leading: const PlatformBackButton(), ), body: HookBuilder( builder: (context) { diff --git a/lib/components/Home/Shell.dart b/lib/components/Home/Shell.dart index 2d24a7f8..7518cddc 100644 --- a/lib/components/Home/Shell.dart +++ b/lib/components/Home/Shell.dart @@ -13,7 +13,7 @@ import 'package:spotube/hooks/useUpdateChecker.dart'; import 'package:spotube/provider/Downloader.dart'; import 'package:spotube/utils/platform.dart'; -const _path = { +const rootPaths = { 0: "/", 1: "/search", 2: "/library", @@ -64,34 +64,40 @@ class Shell extends HookConsumerWidget { return null; }, [backgroundColor]); - final allowedPath = _path.values.contains(GoRouter.of(context).location); - final preferredSize = - allowedPath ? PageWindowTitleBar.staticPreferredSize : Size.zero; - return Scaffold( - appBar: kIsDesktop - ? PreferredSize( - preferredSize: preferredSize, - child: AnimatedContainer( + final allowedPath = + rootPaths.values.contains(GoRouter.of(context).location); + final titleBar = PageWindowTitleBar( + backgroundColor: + platform == TargetPlatform.android ? Colors.transparent : null, + ); + final preferredSize = allowedPath ? titleBar.preferredSize : Size.zero; + var appBar = kIsDesktop + ? PreferredSize( + preferredSize: preferredSize, + child: AnimatedContainer( + duration: const Duration(milliseconds: 250), + height: allowedPath ? titleBar.preferredSize.height : 0, + child: AnimatedOpacity( duration: const Duration(milliseconds: 250), - height: allowedPath - ? PageWindowTitleBar.staticPreferredSize.height - : 0, - child: AnimatedOpacity( - duration: const Duration(milliseconds: 250), - opacity: allowedPath ? 1 : 0, - child: PageWindowTitleBar(preferredSize: preferredSize), - ), + opacity: allowedPath ? 1 : 0, + child: titleBar, ), - ) - : null, - extendBodyBehindAppBar: true, + ), + ) + : null; + return PlatformScaffold( + appBar: platform == TargetPlatform.windows ? appBar : null, + extendBodyBehindAppBar: false, body: Sidebar( selectedIndex: index.value, onSelectedIndexChanged: (i) { index.value = i; - GoRouter.of(context).go(_path[index.value]!); + GoRouter.of(context).go(rootPaths[index.value]!); }, - child: child, + child: PlatformScaffold( + appBar: platform != TargetPlatform.windows ? appBar : null, + body: child, + ), ), extendBody: true, bottomNavigationBar: Column( @@ -102,7 +108,7 @@ class Shell extends HookConsumerWidget { selectedIndex: index.value, onSelectedIndexChanged: (selectedIndex) { index.value = selectedIndex; - GoRouter.of(context).go(_path[selectedIndex]!); + GoRouter.of(context).go(rootPaths[selectedIndex]!); }, ), ], diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart index 6cda41d6..673fe4f9 100644 --- a/lib/components/Home/Sidebar.dart +++ b/lib/components/Home/Sidebar.dart @@ -110,7 +110,7 @@ class Sidebar extends HookConsumerWidget { expanded: extended.value, header: Column( children: [ - if (kIsDesktop) + if (kIsMacOS) SizedBox( height: appWindow.titleBarHeight, width: extended.value ? 256 : 80, diff --git a/lib/components/Login/TokenLogin.dart b/lib/components/Login/TokenLogin.dart index ad5b51d7..8528d3d0 100644 --- a/lib/components/Login/TokenLogin.dart +++ b/lib/components/Login/TokenLogin.dart @@ -16,7 +16,7 @@ class TokenLogin extends HookConsumerWidget { return SafeArea( child: Scaffold( - appBar: const PageWindowTitleBar(leading: PlatformBackButton()), + appBar: PageWindowTitleBar(leading: PlatformBackButton()), body: SingleChildScrollView( child: Center( child: Container( diff --git a/lib/components/Playlist/PlaylistGenreView.dart b/lib/components/Playlist/PlaylistGenreView.dart index 94fb89c5..892bca9b 100644 --- a/lib/components/Playlist/PlaylistGenreView.dart +++ b/lib/components/Playlist/PlaylistGenreView.dart @@ -20,8 +20,8 @@ class PlaylistGenreView extends ConsumerWidget { @override Widget build(BuildContext context, ref) { return Scaffold( - appBar: const PageWindowTitleBar( - leading: PlatformBackButton(), + appBar: PageWindowTitleBar( + leading: const PlatformBackButton(), ), body: Column( children: [ diff --git a/lib/components/Shared/PageWindowTitleBar.dart b/lib/components/Shared/PageWindowTitleBar.dart index df4a0350..88e857b4 100644 --- a/lib/components/Shared/PageWindowTitleBar.dart +++ b/lib/components/Shared/PageWindowTitleBar.dart @@ -1,132 +1,29 @@ -import 'package:bitsdojo_window/bitsdojo_window.dart'; -import 'package:fluent_ui/fluent_ui.dart' show FluentTheme; import 'package:flutter/material.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/utils/platform.dart'; -class TitleBarActionButtons extends StatelessWidget { - final Color? color; - const TitleBarActionButtons({ - Key? key, - this.color, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return TextButtonTheme( - data: TextButtonThemeData( - style: ButtonStyle( - splashFactory: NoSplash.splashFactory, - shape: MaterialStateProperty.all(const RoundedRectangleBorder()), - overlayColor: MaterialStateProperty.all(Colors.black12), - padding: MaterialStateProperty.all(EdgeInsets.zero), - minimumSize: MaterialStateProperty.all(const Size(50, 40)), - maximumSize: MaterialStateProperty.all(const Size(50, 40)), - ), - ), - child: IconTheme( - data: const IconThemeData(size: 16), - child: Row( - children: [ - TextButton( - onPressed: () { - appWindow.minimize(); - }, - style: ButtonStyle( - foregroundColor: MaterialStateProperty.all( - PlatformTheme.of(context).iconTheme?.color), - ), - child: Icon( - Icons.minimize_rounded, - color: color, - )), - TextButton( - onPressed: () async { - appWindow.maximizeOrRestore(); - }, - style: ButtonStyle( - foregroundColor: MaterialStateProperty.all( - PlatformTheme.of(context).iconTheme?.color), - ), - child: Icon( - Icons.crop_square_rounded, - color: color, - )), - TextButton( - onPressed: () { - appWindow.close(); - }, - style: ButtonStyle( - foregroundColor: MaterialStateProperty.all( - color ?? PlatformTheme.of(context).iconTheme?.color), - overlayColor: MaterialStateProperty.all(Colors.redAccent), - ), - child: const Icon( - Icons.close_rounded, - )), +class PageWindowTitleBar extends PlatformAppBar { + PageWindowTitleBar({ + super.backgroundColor, + List? actions, + super.actionsIconTheme, + super.automaticallyImplyLeading = false, + super.centerTitle, + super.foregroundColor, + super.key, + super.leading, + super.leadingWidth, + Widget? center, + super.titleSpacing, + super.titleTextStyle, + super.titleWidth, + super.toolbarOpacity, + super.toolbarTextStyle, + }) : super( + actions: [ + ...?actions, + if (!kIsMacOS && !kIsMobile) const PlatformWindowButtons(), ], - ), - ), - ); - } -} - -class PageWindowTitleBar extends StatelessWidget - implements PreferredSizeWidget { - final Widget? leading; - final Widget? center; - final Color? backgroundColor; - final Color? foregroundColor; - final Size? _preferredSize; - const PageWindowTitleBar({ - Key? key, - Size? preferredSize, - this.leading, - this.center, - this.backgroundColor, - this.foregroundColor, - }) : _preferredSize = preferredSize, - super(key: key); - - static Size get staticPreferredSize => Size.fromHeight( - (kIsDesktop ? appWindow.titleBarHeight : 35), - ); - - @override - Size get preferredSize => _preferredSize ?? staticPreferredSize; - - @override - Widget build(BuildContext context) { - if (kIsMobile) { - return PreferredSize( - preferredSize: const Size.fromHeight(300), - child: Container( - color: backgroundColor, - child: Row( - children: [ - if (leading != null) leading!, - Expanded(child: Center(child: center)), - ], - ), - ), - ); - } - return WindowTitleBarBox( - child: Container( - color: backgroundColor ?? - FluentTheme.maybeOf(context)?.micaBackgroundColor, - child: Row( - children: [ - if (kIsMacOS) - SizedBox( - width: MediaQuery.of(context).size.width * 0.045, - ), - if (leading != null) leading!, - Expanded(child: MoveWindow(child: Center(child: center))), - if (!kIsMacOS && !kIsMobile) const PlatformWindowButtons() - ], - ), - ), - ); - } + title: center, + ); } diff --git a/lib/main.dart b/lib/main.dart index 4939a7b5..9a421cd9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -236,7 +236,7 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { macosDarkTheme: macosDarkTheme, themeMode: themeMode, windowButtonConfig: PlatformWindowButtonConfig( - isMaximized: appWindow.isMaximized, + isMaximized: () => appWindow.isMaximized, onClose: appWindow.close, onRestore: appWindow.restore, onMaximize: appWindow.maximize, From b3c27d1fca233ea079803fe49134abc528376df3 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 12 Nov 2022 13:30:21 +0600 Subject: [PATCH 15/23] feat: titlebar complete compatibility, platform specific login, library tabbar in titlebar --- lib/components/Home/Genres.dart | 2 + lib/components/Home/Shell.dart | 28 +- lib/components/Home/Sidebar.dart | 27 +- lib/components/Library/UserAlbums.dart | 6 + lib/components/Library/UserArtists.dart | 6 + lib/components/Library/UserLibrary.dart | 61 +- lib/components/Library/UserPlaylists.dart | 7 + lib/components/Login/LoginTutorial.dart | 49 +- lib/components/Login/TokenLogin.dart | 19 +- lib/components/Login/TokenLoginForms.dart | 20 +- lib/components/Login/WebViewLogin.dart | 3 +- lib/components/Lyrics/Lyrics.dart | 101 ++-- lib/components/Player/PlayerView.dart | 14 +- lib/components/Search/Search.dart | 551 +++++++++--------- lib/components/Shared/AnonymousFallback.dart | 4 +- lib/components/Shared/PageWindowTitleBar.dart | 6 + 16 files changed, 449 insertions(+), 455 deletions(-) diff --git a/lib/components/Home/Genres.dart b/lib/components/Home/Genres.dart index 558a19b0..0b0677f0 100644 --- a/lib/components/Home/Genres.dart +++ b/lib/components/Home/Genres.dart @@ -6,6 +6,7 @@ import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Category/CategoryCard.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerCategories.dart'; +import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/components/Shared/Waypoint.dart'; import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; @@ -42,6 +43,7 @@ class Genres extends HookConsumerWidget { ]; return PlatformScaffold( + appBar: PageWindowTitleBar(), body: ListView.builder( itemCount: categories.length, itemBuilder: (context, index) { diff --git a/lib/components/Home/Shell.dart b/lib/components/Home/Shell.dart index 7518cddc..b9e85605 100644 --- a/lib/components/Home/Shell.dart +++ b/lib/components/Home/Shell.dart @@ -64,40 +64,14 @@ class Shell extends HookConsumerWidget { return null; }, [backgroundColor]); - final allowedPath = - rootPaths.values.contains(GoRouter.of(context).location); - final titleBar = PageWindowTitleBar( - backgroundColor: - platform == TargetPlatform.android ? Colors.transparent : null, - ); - final preferredSize = allowedPath ? titleBar.preferredSize : Size.zero; - var appBar = kIsDesktop - ? PreferredSize( - preferredSize: preferredSize, - child: AnimatedContainer( - duration: const Duration(milliseconds: 250), - height: allowedPath ? titleBar.preferredSize.height : 0, - child: AnimatedOpacity( - duration: const Duration(milliseconds: 250), - opacity: allowedPath ? 1 : 0, - child: titleBar, - ), - ), - ) - : null; return PlatformScaffold( - appBar: platform == TargetPlatform.windows ? appBar : null, - extendBodyBehindAppBar: false, body: Sidebar( selectedIndex: index.value, onSelectedIndexChanged: (i) { index.value = i; GoRouter.of(context).go(rootPaths[index.value]!); }, - child: PlatformScaffold( - appBar: platform != TargetPlatform.windows ? appBar : null, - body: child, - ), + child: child, ), extendBody: true, bottomNavigationBar: Column( diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart index 673fe4f9..c7557392 100644 --- a/lib/components/Home/Sidebar.dart +++ b/lib/components/Home/Sidebar.dart @@ -133,18 +133,21 @@ class Sidebar extends HookConsumerWidget { ), ), (extended.value) - ? Row( - children: [ - brandLogo(), - const SizedBox( - width: 10, - ), - PlatformText.headline("Spotube"), - PlatformIconButton( - icon: const Icon(Icons.menu_rounded), - onPressed: toggleExtended, - ), - ], + ? Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + brandLogo(), + const SizedBox( + width: 10, + ), + PlatformText.headline("Spotube"), + PlatformIconButton( + icon: const Icon(Icons.menu_rounded), + onPressed: toggleExtended, + ), + ], + ), ) : brandLogo(), ], diff --git a/lib/components/Library/UserAlbums.dart b/lib/components/Library/UserAlbums.dart index 2ac63caf..30c7f4ca 100644 --- a/lib/components/Library/UserAlbums.dart +++ b/lib/components/Library/UserAlbums.dart @@ -4,6 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Album/AlbumCard.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart'; +import 'package:spotube/components/Shared/AnonymousFallback.dart'; +import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; @@ -13,6 +15,10 @@ class UserAlbums extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final auth = ref.watch(authProvider); + if (auth.isAnonymous) { + return const AnonymousFallback(); + } final albumsQuery = useQuery( job: currentUserAlbumsQueryJob, externalData: ref.watch(spotifyProvider), diff --git a/lib/components/Library/UserArtists.dart b/lib/components/Library/UserArtists.dart index 98102769..757193a1 100644 --- a/lib/components/Library/UserArtists.dart +++ b/lib/components/Library/UserArtists.dart @@ -5,7 +5,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Artist/ArtistCard.dart'; +import 'package:spotube/components/Shared/AnonymousFallback.dart'; import 'package:spotube/components/Shared/Waypoint.dart'; +import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; @@ -14,6 +16,10 @@ class UserArtists extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final auth = ref.watch(authProvider); + if (auth.isAnonymous) { + return const AnonymousFallback(); + } final artistQuery = useInfiniteQuery( job: currentUserFollowingArtistsQueryJob, externalData: ref.watch(spotifyProvider), diff --git a/lib/components/Library/UserLibrary.dart b/lib/components/Library/UserLibrary.dart index 24718674..98da2d1a 100644 --- a/lib/components/Library/UserLibrary.dart +++ b/lib/components/Library/UserLibrary.dart @@ -1,47 +1,48 @@ import 'package:flutter/material.dart' hide Image; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Library/UserAlbums.dart'; import 'package:spotube/components/Library/UserArtists.dart'; import 'package:spotube/components/Library/UserDownloads.dart'; import 'package:spotube/components/Library/UserLocalTracks.dart'; import 'package:spotube/components/Library/UserPlaylists.dart'; -import 'package:spotube/components/Shared/AnonymousFallback.dart'; +import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; -class UserLibrary extends ConsumerWidget { +class UserLibrary extends HookConsumerWidget { const UserLibrary({Key? key}) : super(key: key); @override Widget build(BuildContext context, ref) { - return DefaultTabController( - length: 5, - child: SafeArea( - child: PlatformTabView( + final index = useState(0); + + final body = [ + const UserPlaylists(), + const UserLocalTracks(), + const UserDownloads(), + const UserArtists(), + const UserAlbums(), + ][index.value]; + + return PlatformScaffold( + appBar: PageWindowTitleBar( + titleWidth: 415, + centerTitle: true, + center: PlatformTabBar( androidIsScrollable: true, - placement: PlatformProperty.all(PlatformTabbarPlacement.top), - body: { - PlatformTab( - label: "Playlist", - icon: Container(), - ): const AnonymousFallback(child: UserPlaylists()), - PlatformTab( - label: "Downloads", - icon: Container(), - ): const UserDownloads(), - PlatformTab( - label: "Local", - icon: Container(), - ): const UserLocalTracks(), - PlatformTab( - label: "Artists", - icon: Container(), - ): const AnonymousFallback(child: UserArtists()), - PlatformTab( - label: "Album", - icon: Container(), - ): const AnonymousFallback(child: UserAlbums()), - }, + selectedIndex: index.value, + onSelectedIndexChanged: (value) => index.value = value, + isNavigational: + PlatformProperty.byPlatformGroup(mobile: false, desktop: true), + tabs: [ + PlatformTab(label: 'Playlists', icon: const SizedBox.shrink()), + PlatformTab(label: 'Tracks', icon: const SizedBox.shrink()), + PlatformTab(label: 'Downloads', icon: const SizedBox.shrink()), + PlatformTab(label: 'Artists', icon: const SizedBox.shrink()), + PlatformTab(label: 'Albums', icon: const SizedBox.shrink()), + ], ), ), + body: body, ); } } diff --git a/lib/components/Library/UserPlaylists.dart b/lib/components/Library/UserPlaylists.dart index ecfb639b..8fdfcefa 100644 --- a/lib/components/Library/UserPlaylists.dart +++ b/lib/components/Library/UserPlaylists.dart @@ -6,6 +6,8 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart'; import 'package:spotube/components/Playlist/PlaylistCard.dart'; import 'package:spotube/components/Playlist/PlaylistCreateDialog.dart'; +import 'package:spotube/components/Shared/AnonymousFallback.dart'; +import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; @@ -14,6 +16,11 @@ class UserPlaylists extends HookConsumerWidget { @override Widget build(BuildContext context, ref) { + final auth = ref.watch(authProvider); + if (auth.isAnonymous) { + return const AnonymousFallback(); + } + final playlistsQuery = useQuery( job: currentUserPlaylistsQueryJob, externalData: ref.watch(spotifyProvider), diff --git a/lib/components/Login/LoginTutorial.dart b/lib/components/Login/LoginTutorial.dart index 6d15578e..4a0ec3a3 100644 --- a/lib/components/Login/LoginTutorial.dart +++ b/lib/components/Login/LoginTutorial.dart @@ -14,46 +14,56 @@ class LoginTutorial extends ConsumerWidget { @override Widget build(BuildContext context, ref) { final auth = ref.watch(authProvider); + final key = GlobalKey>(); - return Scaffold( + return PlatformScaffold( appBar: PageWindowTitleBar( leading: PlatformTextButton( - child: const Text("Exit"), + child: const PlatformText("Exit"), onPressed: () { Navigator.of(context).pop(); }, ), ), body: IntroductionScreen( - next: const Text("Next"), - back: const Text("Previous"), + key: key, + overrideBack: PlatformFilledButton( + isSecondary: true, + child: const Center(child: PlatformText("Previous")), + onPressed: () { + (key.currentState as IntroductionScreenState).previous(); + }, + ), + overrideNext: PlatformFilledButton( + child: const Center(child: PlatformText("Next")), + onPressed: () { + (key.currentState as IntroductionScreenState).next(); + }, + ), showBackButton: true, - overrideDone: PlatformTextButton( + overrideDone: PlatformFilledButton( onPressed: auth.isLoggedIn ? () { ServiceUtils.navigate(context, "/"); } : null, - child: const Text("Done"), + child: const Center(child: PlatformText("Done")), ), pages: [ PageViewModel( title: "Step 1", image: Image.asset("assets/tutorial/step-1.png"), bodyWidget: Wrap( - children: [ - Text( + children: const [ + PlatformText( "First, Go to ", - style: Theme.of(context).textTheme.bodyText1, ), Hyperlink( "accounts.spotify.com ", "https://accounts.spotify.com", - style: Theme.of(context).textTheme.bodyText1!, ), - Text( + PlatformText( "and Login/Sign up if you're not logged in", - style: Theme.of(context).textTheme.bodyText1, ), ], ), @@ -61,10 +71,9 @@ class LoginTutorial extends ConsumerWidget { PageViewModel( title: "Step 2", image: Image.asset("assets/tutorial/step-2.png"), - bodyWidget: Text( + bodyWidget: const PlatformText( "1. Once you're logged in, press F12 or Mouse Right Click > Inspect to Open the Browser devtools.\n2. Then go the \"Application\" Tab (Chrome, Edge, Brave etc..) or \"Storage\" Tab (Firefox, Palemoon etc..)\n3. Go to the \"Cookies\" section then the \"https://accounts.spotify.com\" subsection", textAlign: TextAlign.left, - style: Theme.of(context).textTheme.bodyText1, ), ), PageViewModel( @@ -72,10 +81,9 @@ class LoginTutorial extends ConsumerWidget { image: Image.asset( "assets/tutorial/step-3.png", ), - bodyWidget: Text( + bodyWidget: const PlatformText( "Copy the values of \"sp_dc\" and \"sp_key\" Cookies", textAlign: TextAlign.left, - style: Theme.of(context).textTheme.bodyText1, ), ), if (auth.isLoggedIn) @@ -92,13 +100,12 @@ class LoginTutorial extends ConsumerWidget { PageViewModel( title: "Step 5", bodyWidget: Column( - children: [ - Text( + children: const [ + PlatformText( "Paste the copied \"sp_dc\" and \"sp_key\" values in the respective fields", - style: Theme.of(context).textTheme.bodyText1, ), - const SizedBox(height: 10), - const TokenLoginForm(), + SizedBox(height: 10), + TokenLoginForm(), ], ), ), diff --git a/lib/components/Login/TokenLogin.dart b/lib/components/Login/TokenLogin.dart index 8528d3d0..681818f5 100644 --- a/lib/components/Login/TokenLogin.dart +++ b/lib/components/Login/TokenLogin.dart @@ -15,12 +15,17 @@ class TokenLogin extends HookConsumerWidget { final textTheme = Theme.of(context).textTheme; return SafeArea( - child: Scaffold( - appBar: PageWindowTitleBar(leading: PlatformBackButton()), + child: PlatformScaffold( + appBar: PageWindowTitleBar(leading: const PlatformBackButton()), body: SingleChildScrollView( child: Center( child: Container( - margin: const EdgeInsets.symmetric(horizontal: 10), + margin: const EdgeInsets.all(10), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: PlatformTheme.of(context).secondaryBackgroundColor, + borderRadius: BorderRadius.circular(10), + ), child: Column( children: [ Image.asset( @@ -28,11 +33,11 @@ class TokenLogin extends HookConsumerWidget { width: MediaQuery.of(context).size.width * (breakpoint <= Breakpoints.md ? .5 : .3), ), - Text("Add your spotify credentials to get started", + PlatformText("Add your spotify credentials to get started", style: breakpoint <= Breakpoints.md ? textTheme.headline5 : textTheme.headline4), - Text( + PlatformText( "Don't worry, any of your credentials won't be collected or shared with anyone", style: Theme.of(context).textTheme.caption, ), @@ -45,9 +50,9 @@ class TokenLogin extends HookConsumerWidget { alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, children: [ - const Text("Don't know how to do this?"), + const PlatformText("Don't know how to do this?"), PlatformTextButton( - child: const Text( + child: const PlatformText( "Follow along the Step by Step guide", ), onPressed: () => GoRouter.of(context).push( diff --git a/lib/components/Login/TokenLoginForms.dart b/lib/components/Login/TokenLoginForms.dart index 76ee5a5c..85f7c77d 100644 --- a/lib/components/Login/TokenLoginForms.dart +++ b/lib/components/Login/TokenLoginForms.dart @@ -25,21 +25,17 @@ class TokenLoginForm extends HookConsumerWidget { ), child: Column( children: [ - TextField( + PlatformTextField( controller: directCodeController, - decoration: const InputDecoration( - hintText: "Spotify \"sp_dc\" Cookie", - label: Text("sp_dc Cookie"), - ), + placeholder: "Spotify \"sp_dc\" Cookie", + label: "sp_dc Cookie", keyboardType: TextInputType.visiblePassword, ), const SizedBox(height: 10), - TextField( + PlatformTextField( controller: keyCodeController, - decoration: const InputDecoration( - hintText: "Spotify \"sp_key\" Cookie", - label: Text("sp_key Cookie"), - ), + placeholder: "Spotify \"sp_key\" Cookie", + label: "sp_key Cookie", keyboardType: TextInputType.visiblePassword, ), const SizedBox(height: 20), @@ -49,7 +45,7 @@ class TokenLoginForm extends HookConsumerWidget { directCodeController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text("Please fill in all fields"), + content: PlatformText("Please fill in all fields"), behavior: SnackBarBehavior.floating, ), ); @@ -68,7 +64,7 @@ class TokenLoginForm extends HookConsumerWidget { onDone?.call(); } }, - child: const Text("Submit"), + child: const PlatformText("Submit"), ) ], ), diff --git a/lib/components/Login/WebViewLogin.dart b/lib/components/Login/WebViewLogin.dart index 4ab84016..32f29a35 100644 --- a/lib/components/Login/WebViewLogin.dart +++ b/lib/components/Login/WebViewLogin.dart @@ -3,6 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/provider/Auth.dart'; import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/service_utils.dart'; @@ -23,7 +24,7 @@ class WebViewLogin extends HookConsumerWidget { ); } - return Scaffold( + return PlatformScaffold( body: SafeArea( child: InAppWebView( initialOptions: InAppWebViewGroupOptions( diff --git a/lib/components/Lyrics/Lyrics.dart b/lib/components/Lyrics/Lyrics.dart index 225e13be..99b1509a 100644 --- a/lib/components/Lyrics/Lyrics.dart +++ b/lib/components/Lyrics/Lyrics.dart @@ -3,38 +3,20 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:palette_generator/palette_generator.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/components/Lyrics/GeniusLyrics.dart'; import 'package:spotube/components/Lyrics/SyncedLyrics.dart'; +import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/components/Shared/UniversalImage.dart'; import 'package:spotube/hooks/useCustomStatusBarColor.dart'; import 'package:spotube/hooks/usePaletteColor.dart'; import 'package:spotube/provider/Playback.dart'; +import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; class Lyrics extends HookConsumerWidget { const Lyrics({Key? key}) : super(key: key); - Widget buildContainer(Widget child, String albumArt, PaletteColor palette) { - return Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - image: DecorationImage( - image: UniversalImage.imageProvider(albumArt), - fit: BoxFit.cover, - ), - ), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), - child: Container( - color: palette.color.withOpacity(.7), - child: child, - ), - ), - ); - } - @override Widget build(BuildContext context, ref) { Playback playback = ref.watch(playbackProvider); @@ -47,6 +29,7 @@ class Lyrics extends HookConsumerWidget { [playback.track?.album?.images], ); final palette = usePaletteColor(albumArt, ref); + final index = useState(0); useCustomStatusBarColor( palette.color, @@ -54,52 +37,42 @@ class Lyrics extends HookConsumerWidget { noSetBGColor: true, ); - return SafeArea( - child: PlatformTabView( - body: { - PlatformTab( - label: "Synced Lyrics", - icon: Container(), - ): buildContainer(SyncedLyrics(palette: palette), albumArt, palette), - PlatformTab( - label: "Lyrics (genius.com)", - icon: Container(), - ): buildContainer(GeniusLyrics(palette: palette), albumArt, palette), - }, - ), - ); + final body = [ + SyncedLyrics(palette: palette), + GeniusLyrics(palette: palette), + ][index.value]; - return SafeArea( - child: Scaffold( - extendBodyBehindAppBar: true, - appBar: const TabBar( - isScrollable: true, - tabs: [ - Tab(text: "Synced Lyrics"), - Tab(text: "Lyrics (genius.com)"), - ], - ), - body: Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - image: DecorationImage( - image: UniversalImage.imageProvider(albumArt), - fit: BoxFit.cover, - ), - ), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), - child: Container( - color: palette.color.withOpacity(.7), - child: SafeArea( - child: TabBarView( - children: [ - SyncedLyrics(palette: palette), - GeniusLyrics(palette: palette), - ], - ), + return PlatformScaffold( + extendBodyBehindAppBar: true, + appBar: !kIsMacOS + ? PageWindowTitleBar( + backgroundColor: Colors.transparent, + toolbarOpacity: 0, + center: PlatformTabBar( + isNavigational: + PlatformProperty.only(linux: true, other: false), + selectedIndex: index.value, + onSelectedIndexChanged: (value) => index.value = value, + tabs: [ + PlatformTab(label: "Synced", icon: const SizedBox.shrink()), + PlatformTab(label: "Genius", icon: const SizedBox.shrink()), + ], ), - ), + ) + : null, + body: Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + image: DecorationImage( + image: UniversalImage.imageProvider(albumArt), + fit: BoxFit.cover, + ), + ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), + child: Container( + color: palette.color.withOpacity(.7), + child: SafeArea(child: body), ), ), ), diff --git a/lib/components/Player/PlayerView.dart b/lib/components/Player/PlayerView.dart index 0878a296..624296ea 100644 --- a/lib/components/Player/PlayerView.dart +++ b/lib/components/Player/PlayerView.dart @@ -58,7 +58,14 @@ class PlayerView extends HookConsumerWidget { noSetBGColor: true, ); - return Scaffold( + return PlatformScaffold( + appBar: PageWindowTitleBar( + backgroundColor: Colors.transparent, + foregroundColor: paletteColor.titleTextColor, + toolbarOpacity: 0, + automaticallyImplyLeading: true, + ), + extendBodyBehindAppBar: true, body: Container( decoration: BoxDecoration( image: DecorationImage( @@ -74,11 +81,6 @@ class PlayerView extends HookConsumerWidget { child: SafeArea( child: Column( children: [ - PageWindowTitleBar( - leading: const PlatformBackButton(), - backgroundColor: Colors.transparent, - foregroundColor: paletteColor.titleTextColor, - ), Padding( padding: const EdgeInsets.all(10), child: Column( diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index aad3dfc0..81be822b 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -10,6 +10,7 @@ import 'package:spotube/components/Artist/ArtistCard.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart'; import 'package:spotube/components/Playlist/PlaylistCard.dart'; import 'package:spotube/components/Shared/AnonymousFallback.dart'; +import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/components/Shared/TrackTile.dart'; import 'package:spotube/components/Shared/Waypoint.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; @@ -18,6 +19,7 @@ import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; +import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/primitive_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:tuple/tuple.dart'; @@ -57,10 +59,6 @@ class Search extends HookConsumerWidget { job: searchQueryJob(SearchType.artist.key), externalData: getVariables()); - if (auth.isAnonymous) { - return const AnonymousFallback(); - } - void onSearch() { for (final query in [ searchTrack, @@ -75,281 +73,288 @@ class Search extends HookConsumerWidget { } } - return SafeArea( - child: Material( - color: PlatformTheme.of(context).scaffoldBackgroundColor, - textStyle: PlatformTheme.of(context).textTheme!.body!, - child: Column( - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 10, - ), - child: PlatformTextField( - onChanged: (value) { - ref.read(searchTermStateProvider.notifier).state = value; - }, - prefixIcon: Icons.search_rounded, - placeholder: "Search...", - onSubmitted: (value) { - onSearch(); - }, - ), - ), - HookBuilder( - builder: (context) { - Playback playback = ref.watch(playbackProvider); - List albums = []; - List artists = []; - List tracks = []; - List playlists = []; - final pages = [ - ...searchTrack.pages, - ...searchAlbum.pages, - ...searchPlaylist.pages, - ...searchArtist.pages, - ].expand((page) => page ?? []).toList(); - for (MapEntry page in pages.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 PlatformScaffold( + appBar: !kIsMacOS ? PageWindowTitleBar() : null, + body: auth.isAnonymous + ? const AnonymousFallback() + : Column( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 10, + ), + child: PlatformTextField( + onChanged: (value) { + ref.read(searchTermStateProvider.notifier).state = value; + }, + prefixIcon: Icons.search_rounded, + placeholder: "Search...", + onSubmitted: (value) { + onSearch(); + }, + ), + ), + HookBuilder( + builder: (context) { + Playback playback = ref.watch(playbackProvider); + List albums = []; + List artists = []; + List tracks = []; + List playlists = []; + final pages = [ + ...searchTrack.pages, + ...searchAlbum.pages, + ...searchPlaylist.pages, + ...searchArtist.pages, + ].expand((page) => page ?? []).toList(); + for (MapEntry page in pages.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) PlatformText.headline("Songs"), - if (searchTrack.isLoading && - !searchTrack.isFetchingNextPage) - const PlatformCircularProgressIndicator() - else if (searchTrack.hasError) - PlatformText( - searchTrack.error?[searchTrack.pageParams.last]) - else - ...tracks.asMap().entries.map((track) { - String duration = - "${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; - return TrackTile( - playback, - track: track, - duration: duration, - 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: TypeConversionUtils - .image_X_UrlString( - currentTrack.album?.images, - placeholder: - ImagePlaceholder.albumArt, - ), - ), - ); - } else if (isPlaylistPlaying && - currentTrack.id != null && - currentTrack.id != playback.track?.id) { - playback.play(currentTrack); - } - }, - ); - }), - if (searchTrack.hasNextPage && tracks.isNotEmpty) - Center( - child: PlatformTextButton( - onPressed: searchTrack.isFetchingNextPage - ? null - : () => searchTrack.fetchNextPage(), - child: searchTrack.isFetchingNextPage - ? const PlatformCircularProgressIndicator() - : const PlatformText("Load more"), - ), - ), - if (playlists.isNotEmpty) - PlatformText.headline("Playlists"), - const SizedBox(height: 10), - if (searchPlaylist.isLoading && - !searchPlaylist.isFetchingNextPage) - const PlatformCircularProgressIndicator() - else if (searchPlaylist.hasError) - PlatformText(searchPlaylist - .error?[searchPlaylist.pageParams.last]) - else - ScrollConfiguration( - behavior: - ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }, - ), - child: Scrollbar( - scrollbarOrientation: - breakpoint > Breakpoints.md - ? ScrollbarOrientation.bottom - : ScrollbarOrientation.top, - controller: playlistController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: playlistController, - child: Row( - children: [ - ...playlists.mapIndexed( - (i, playlist) { - if (i == playlists.length - 1 && - searchPlaylist.hasNextPage) { - return Waypoint( - onEnter: () { - searchPlaylist.fetchNextPage(); - }, - child: - const ShimmerPlaybuttonCard( - count: 1), - ); - } - return PlaylistCard(playlist); - }, - ), - ], - ), - ), - ), - ), - const SizedBox(height: 20), - if (artists.isNotEmpty) - PlatformText.headline("Artists"), - const SizedBox(height: 10), - if (searchArtist.isLoading && - !searchArtist.isFetchingNextPage) - const PlatformCircularProgressIndicator() - else if (searchArtist.hasError) - PlatformText(searchArtist - .error?[searchArtist.pageParams.last]) - else - ScrollConfiguration( - behavior: - ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }, - ), - child: Scrollbar( - controller: artistController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: artistController, - child: Row( - children: [ - ...artists.mapIndexed( - (i, artist) { - if (i == artists.length - 1 && - searchArtist.hasNextPage) { - return Waypoint( - onEnter: () { - searchArtist.fetchNextPage(); - }, - child: - const ShimmerPlaybuttonCard( - count: 1), - ); - } - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 15), - child: ArtistCard(artist), - ); - }, - ), - ], - ), - ), - ), - ), - const SizedBox(height: 20), - if (albums.isNotEmpty) - PlatformText( - "Albums", - style: Theme.of(context).textTheme.headline5, - ), - const SizedBox(height: 10), - if (searchAlbum.isLoading && - !searchAlbum.isFetchingNextPage) - const PlatformCircularProgressIndicator() - else if (searchAlbum.hasError) - PlatformText( - searchAlbum.error?[searchAlbum.pageParams.last]) - else - ScrollConfiguration( - behavior: - ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }, - ), - child: Scrollbar( - controller: albumController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: albumController, - child: Row( - children: [ - ...albums.mapIndexed((i, album) { - if (i == albums.length - 1 && - searchAlbum.hasNextPage) { - return Waypoint( - onEnter: () { - searchAlbum.fetchNextPage(); - }, - child: const ShimmerPlaybuttonCard( - count: 1), - ); - } - return AlbumCard( - TypeConversionUtils - .simpleAlbum_X_Album( - album, + return Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 20, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (tracks.isNotEmpty) + PlatformText.headline("Songs"), + if (searchTrack.isLoading && + !searchTrack.isFetchingNextPage) + const PlatformCircularProgressIndicator() + else if (searchTrack.hasError) + PlatformText(searchTrack + .error?[searchTrack.pageParams.last]) + else + ...tracks.asMap().entries.map((track) { + String duration = + "${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; + return TrackTile( + playback, + track: track, + duration: duration, + 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: TypeConversionUtils + .image_X_UrlString( + currentTrack.album?.images, + placeholder: + ImagePlaceholder.albumArt, + ), ), ); - }), - ], + } else if (isPlaylistPlaying && + currentTrack.id != null && + currentTrack.id != + playback.track?.id) { + playback.play(currentTrack); + } + }, + ); + }), + if (searchTrack.hasNextPage && tracks.isNotEmpty) + Center( + child: PlatformTextButton( + onPressed: searchTrack.isFetchingNextPage + ? null + : () => searchTrack.fetchNextPage(), + child: searchTrack.isFetchingNextPage + ? const PlatformCircularProgressIndicator() + : const PlatformText("Load more"), ), ), - ), - ), - ], + if (playlists.isNotEmpty) + PlatformText.headline("Playlists"), + const SizedBox(height: 10), + if (searchPlaylist.isLoading && + !searchPlaylist.isFetchingNextPage) + const PlatformCircularProgressIndicator() + else if (searchPlaylist.hasError) + PlatformText(searchPlaylist + .error?[searchPlaylist.pageParams.last]) + else + ScrollConfiguration( + behavior: + ScrollConfiguration.of(context).copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }, + ), + child: Scrollbar( + scrollbarOrientation: + breakpoint > Breakpoints.md + ? ScrollbarOrientation.bottom + : ScrollbarOrientation.top, + controller: playlistController, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: playlistController, + child: Row( + children: [ + ...playlists.mapIndexed( + (i, playlist) { + if (i == playlists.length - 1 && + searchPlaylist.hasNextPage) { + return Waypoint( + onEnter: () { + searchPlaylist + .fetchNextPage(); + }, + child: + const ShimmerPlaybuttonCard( + count: 1), + ); + } + return PlaylistCard(playlist); + }, + ), + ], + ), + ), + ), + ), + const SizedBox(height: 20), + if (artists.isNotEmpty) + PlatformText.headline("Artists"), + const SizedBox(height: 10), + if (searchArtist.isLoading && + !searchArtist.isFetchingNextPage) + const PlatformCircularProgressIndicator() + else if (searchArtist.hasError) + PlatformText(searchArtist + .error?[searchArtist.pageParams.last]) + else + ScrollConfiguration( + behavior: + ScrollConfiguration.of(context).copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }, + ), + child: Scrollbar( + controller: artistController, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: artistController, + child: Row( + children: [ + ...artists.mapIndexed( + (i, artist) { + if (i == artists.length - 1 && + searchArtist.hasNextPage) { + return Waypoint( + onEnter: () { + searchArtist + .fetchNextPage(); + }, + child: + const ShimmerPlaybuttonCard( + count: 1), + ); + } + return Container( + margin: + const EdgeInsets.symmetric( + horizontal: 15), + child: ArtistCard(artist), + ); + }, + ), + ], + ), + ), + ), + ), + const SizedBox(height: 20), + if (albums.isNotEmpty) + PlatformText( + "Albums", + style: Theme.of(context).textTheme.headline5, + ), + const SizedBox(height: 10), + if (searchAlbum.isLoading && + !searchAlbum.isFetchingNextPage) + const PlatformCircularProgressIndicator() + else if (searchAlbum.hasError) + PlatformText(searchAlbum + .error?[searchAlbum.pageParams.last]) + else + ScrollConfiguration( + behavior: + ScrollConfiguration.of(context).copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }, + ), + child: Scrollbar( + controller: albumController, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: albumController, + child: Row( + children: [ + ...albums.mapIndexed((i, album) { + if (i == albums.length - 1 && + searchAlbum.hasNextPage) { + return Waypoint( + onEnter: () { + searchAlbum.fetchNextPage(); + }, + child: + const ShimmerPlaybuttonCard( + count: 1), + ); + } + return AlbumCard( + TypeConversionUtils + .simpleAlbum_X_Album( + album, + ), + ); + }), + ], + ), + ), + ), + ), + ], + ), + ), ), - ), - ), - ); - }, - ) - ], - ), - ), + ); + }, + ) + ], + ), ); } } diff --git a/lib/components/Shared/AnonymousFallback.dart b/lib/components/Shared/AnonymousFallback.dart index 252d1106..85aa9712 100644 --- a/lib/components/Shared/AnonymousFallback.dart +++ b/lib/components/Shared/AnonymousFallback.dart @@ -20,10 +20,10 @@ class AnonymousFallback extends ConsumerWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text("You're not logged in"), + const PlatformText("You're not logged in"), const SizedBox(height: 10), PlatformFilledButton( - child: const Text("Login with Spotify"), + child: const PlatformText("Login with Spotify"), onPressed: () => ServiceUtils.navigate(context, "/settings"), ) ], diff --git a/lib/components/Shared/PageWindowTitleBar.dart b/lib/components/Shared/PageWindowTitleBar.dart index 88e857b4..58235126 100644 --- a/lib/components/Shared/PageWindowTitleBar.dart +++ b/lib/components/Shared/PageWindowTitleBar.dart @@ -1,3 +1,4 @@ +import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:flutter/material.dart'; import 'package:platform_ui/platform_ui.dart'; import 'package:spotube/utils/platform.dart'; @@ -26,4 +27,9 @@ class PageWindowTitleBar extends PlatformAppBar { ], title: center, ); + + @override + Widget build(BuildContext context) { + return MoveWindow(child: super.build(context)); + } } From c1868817e5abb8a4152646f00a0395933fee7823 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 12 Nov 2022 13:35:37 +0600 Subject: [PATCH 16/23] fix(ArtistCard): linux shadow --- lib/components/Artist/ArtistCard.dart | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/components/Artist/ArtistCard.dart b/lib/components/Artist/ArtistCard.dart index aec4b3e7..bf8784b3 100644 --- a/lib/components/Artist/ArtistCard.dart +++ b/lib/components/Artist/ArtistCard.dart @@ -33,24 +33,18 @@ class ArtistCard extends HookWidget { ios: null, macos: null, linux: BoxShadow( - blurRadius: 10, - offset: const Offset(0, 3), - spreadRadius: 5, - color: Theme.of(context).shadowColor, + blurRadius: 6, + color: Theme.of(context).shadowColor.withOpacity(0.3), ), windows: null, ), ); final splash = usePlatformProperty( - (context) => PlatformProperty.multiPlatformGroup({ - InkRipple.splashFactory: {TargetPlatform.android, TargetPlatform.linux}, - NoSplash.splashFactory: { - TargetPlatform.windows, - TargetPlatform.macOS, - TargetPlatform.iOS, - } - }), + (context) => PlatformProperty.only( + android: InkRipple.splashFactory, + other: NoSplash.splashFactory, + ), ); return SizedBox( From 39a77945d132d90ff323b0a22edc2a12a4749888 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sat, 12 Nov 2022 14:01:40 +0600 Subject: [PATCH 17/23] feat: player queue and sibling tracks platform decoration --- lib/components/Library/UserLibrary.dart | 2 +- lib/components/Player/PlayerQueue.dart | 18 ++++------ lib/components/Player/SiblingTracksSheet.dart | 33 +++++++++++-------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/lib/components/Library/UserLibrary.dart b/lib/components/Library/UserLibrary.dart index 98da2d1a..2a5bffa5 100644 --- a/lib/components/Library/UserLibrary.dart +++ b/lib/components/Library/UserLibrary.dart @@ -25,7 +25,7 @@ class UserLibrary extends HookConsumerWidget { return PlatformScaffold( appBar: PageWindowTitleBar( - titleWidth: 415, + titleWidth: 347, centerTitle: true, center: PlatformTabBar( androidIsScrollable: true, diff --git a/lib/components/Player/PlayerQueue.dart b/lib/components/Player/PlayerQueue.dart index 307b918e..d7a04849 100644 --- a/lib/components/Player/PlayerQueue.dart +++ b/lib/components/Player/PlayerQueue.dart @@ -3,6 +3,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:platform_ui/platform_ui.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:spotube/components/Shared/NotFound.dart'; import 'package:spotube/components/Shared/TrackTile.dart'; @@ -47,9 +48,6 @@ class PlayerQueue extends HookConsumerWidget { return null; }, []); - var titleStyle = Theme.of(context).textTheme.headline4?.copyWith( - fontWeight: FontWeight.bold, - ); return BackdropFilter( filter: ImageFilter.blur( sigmaX: 12.0, @@ -61,9 +59,8 @@ class PlayerQueue extends HookConsumerWidget { top: 5.0, ), decoration: BoxDecoration( - color: Theme.of(context) - .navigationRailTheme - .backgroundColor + color: PlatformTheme.of(context) + .scaffoldBackgroundColor ?.withOpacity(0.5), borderRadius: borderRadius, ), @@ -78,16 +75,13 @@ class PlayerQueue extends HookConsumerWidget { borderRadius: BorderRadius.circular(20), ), ), - Text("Queue", style: titleStyle), + PlatformText.subheading("Queue"), Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( + child: PlatformText( playback.playlist?.name ?? "", overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .bodyText1 - ?.copyWith(fontWeight: FontWeight.bold), + style: PlatformTextTheme.of(context).body, ), ), const SizedBox(height: 10), diff --git a/lib/components/Player/SiblingTracksSheet.dart b/lib/components/Player/SiblingTracksSheet.dart index ced57a97..172aedfc 100644 --- a/lib/components/Player/SiblingTracksSheet.dart +++ b/lib/components/Player/SiblingTracksSheet.dart @@ -41,41 +41,46 @@ class SiblingTracksSheet extends HookConsumerWidget { margin: const EdgeInsets.all(8.0), decoration: BoxDecoration( borderRadius: borderRadius, - color: Theme.of(context) - .navigationRailTheme - .backgroundColor - ?.withOpacity(0.5), + color: PlatformTheme.of(context) + .scaffoldBackgroundColor! + .withOpacity(.3), ), child: Scaffold( backgroundColor: Colors.transparent, - appBar: AppBar( + appBar: PlatformAppBar( centerTitle: true, - title: const Text('Alternative Tracks Sources'), + title: PlatformText.subheading( + 'Alternative Tracks Sources', + ), automaticallyImplyLeading: false, backgroundColor: Colors.transparent, + toolbarOpacity: 0, ), - body: Padding( + body: Container( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: ListView.builder( itemCount: playback.siblingYtVideos.length, itemBuilder: (context, index) { final video = playback.siblingYtVideos[index]; return PlatformListTile( - title: Text(video.title), - leading: UniversalImage( - path: video.thumbnails.lowResUrl, - height: 60, - width: 60, + title: PlatformText(video.title), + leading: Padding( + padding: const EdgeInsets.all(8.0), + child: UniversalImage( + path: video.thumbnails.lowResUrl, + height: 60, + width: 60, + ), ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), - trailing: Text( + trailing: PlatformText( PrimitiveUtils.toReadableDuration( video.duration ?? Duration.zero, ), ), - subtitle: Text(video.author), + subtitle: PlatformText(video.author), enabled: playback.status != PlaybackStatus.loading, selected: video.id == playback.track!.ytTrack.id, selectedTileColor: Theme.of(context).popupMenuTheme.color, From be56ad44773ebcd14777d80b61e26875698dc18a Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 24 Nov 2022 10:53:35 +0600 Subject: [PATCH 18/23] fix: search field ios dark icon , lyrics tabbar ios background color --- lib/components/Lyrics/Lyrics.dart | 16 +++++++++++++--- lib/components/Player/PlayerView.dart | 7 ++++++- lib/components/Search/Search.dart | 4 ++++ lib/themes/dark-theme.dart | 3 +++ lib/themes/light-theme.dart | 4 +++- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/components/Lyrics/Lyrics.dart b/lib/components/Lyrics/Lyrics.dart index 99b1509a..0a344a98 100644 --- a/lib/components/Lyrics/Lyrics.dart +++ b/lib/components/Lyrics/Lyrics.dart @@ -46,16 +46,26 @@ class Lyrics extends HookConsumerWidget { extendBodyBehindAppBar: true, appBar: !kIsMacOS ? PageWindowTitleBar( - backgroundColor: Colors.transparent, toolbarOpacity: 0, + backgroundColor: Colors.transparent, center: PlatformTabBar( isNavigational: PlatformProperty.only(linux: true, other: false), selectedIndex: index.value, onSelectedIndexChanged: (value) => index.value = value, + backgroundColor: + PlatformTheme.of(context).scaffoldBackgroundColor, tabs: [ - PlatformTab(label: "Synced", icon: const SizedBox.shrink()), - PlatformTab(label: "Genius", icon: const SizedBox.shrink()), + PlatformTab( + label: "Synced", + icon: const SizedBox.shrink(), + color: PlatformTextTheme.of(context).caption?.color, + ), + PlatformTab( + label: "Genius", + icon: const SizedBox.shrink(), + color: PlatformTextTheme.of(context).caption?.color, + ), ], ), ) diff --git a/lib/components/Player/PlayerView.dart b/lib/components/Player/PlayerView.dart index 624296ea..6075f8f7 100644 --- a/lib/components/Player/PlayerView.dart +++ b/lib/components/Player/PlayerView.dart @@ -63,7 +63,12 @@ class PlayerView extends HookConsumerWidget { backgroundColor: Colors.transparent, foregroundColor: paletteColor.titleTextColor, toolbarOpacity: 0, - automaticallyImplyLeading: true, + leading: PlatformBackButton( + color: PlatformProperty.only( + macos: Colors.transparent, + other: paletteColor.titleTextColor, + ).resolve(platform!), + ), ), extendBodyBehindAppBar: true, body: Container( diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index 81be822b..e5e12db2 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -89,6 +89,10 @@ class Search extends HookConsumerWidget { ref.read(searchTermStateProvider.notifier).state = value; }, prefixIcon: Icons.search_rounded, + prefixIconColor: PlatformProperty.only( + ios: PlatformTheme.of(context).textTheme?.caption?.color, + other: null, + ).resolve(platform!), placeholder: "Search...", onSubmitted: (value) { onSearch(); diff --git a/lib/themes/dark-theme.dart b/lib/themes/dark-theme.dart index c47abeac..42acc42e 100644 --- a/lib/themes/dark-theme.dart +++ b/lib/themes/dark-theme.dart @@ -89,5 +89,8 @@ ThemeData darkTheme({ unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16), ), + appBarTheme: AppBarTheme( + backgroundColor: backgroundMaterialColor[900], + ), ); } diff --git a/lib/themes/light-theme.dart b/lib/themes/light-theme.dart index 8d19881e..5503d5b4 100644 --- a/lib/themes/light-theme.dart +++ b/lib/themes/light-theme.dart @@ -149,7 +149,9 @@ final macosDarkTheme = MacosThemeData.dark().copyWith( typography: MacosTypography(color: MacosColors.textColor), ); const iosTheme = CupertinoThemeData(brightness: Brightness.light); -const iosDarkTheme = CupertinoThemeData(brightness: Brightness.dark); +const iosDarkTheme = CupertinoThemeData( + brightness: Brightness.dark, +); final linuxTheme = AdwaitaThemeData.light().copyWith( listTileTheme: ListTileThemeData( From 3c6803bb3fac8eee9166764089724194a48509c6 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 24 Nov 2022 11:06:14 +0600 Subject: [PATCH 19/23] fix: dialog logo in android, lyrics visible timer adjust button --- .../Lyrics/LyricDelayAdjustDialog.dart | 61 ++++++++++--------- lib/components/Lyrics/SyncedLyrics.dart | 30 +++++---- .../Playlist/PlaylistCreateDialog.dart | 2 +- .../Settings/ColorSchemePickerDialog.dart | 2 +- lib/components/Shared/AdaptiveListTile.dart | 2 +- .../Shared/DownloadConfirmationDialog.dart | 2 +- .../Shared/ReplaceDownloadedFileDialog.dart | 2 +- lib/components/Shared/TrackTile.dart | 2 +- lib/hooks/useUpdateChecker.dart | 2 +- 9 files changed, 57 insertions(+), 48 deletions(-) diff --git a/lib/components/Lyrics/LyricDelayAdjustDialog.dart b/lib/components/Lyrics/LyricDelayAdjustDialog.dart index 65ead135..19dacec7 100644 --- a/lib/components/Lyrics/LyricDelayAdjustDialog.dart +++ b/lib/components/Lyrics/LyricDelayAdjustDialog.dart @@ -18,7 +18,7 @@ class LyricDelayAdjustDialog extends HookConsumerWidget { double.tryParse(controller.text.replaceAll("ms", "")) ?? 0; return PlatformAlertDialog( - icon: Sidebar.brandLogo(), + macosAppIcon: Sidebar.brandLogo(), title: const Center(child: Text("Adjust Lyrics Delay")), secondaryActions: [ PlatformFilledButton( @@ -41,36 +41,39 @@ class LyricDelayAdjustDialog extends HookConsumerWidget { }, ) ], - content: Row( - mainAxisSize: MainAxisSize.min, - children: [ - PlatformIconButton( - icon: const Icon(Icons.remove_rounded), - onPressed: () { - controller.text = "${getValue() - 25}ms"; - }, - ), - Flexible( - child: PlatformTextField( - keyboardType: TextInputType.number, - controller: controller, - placeholder: "Delay in milliseconds", - onSubmitted: (_) { - Navigator.of(context).pop( - Duration( - milliseconds: getValue().toInt(), - ), - ); + content: SizedBox( + height: 100, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + PlatformIconButton( + icon: const Icon(Icons.remove_rounded), + onPressed: () { + controller.text = "${getValue() - 25}ms"; }, ), - ), - PlatformIconButton( - icon: const Icon(Icons.add_rounded), - onPressed: () { - controller.text = "${getValue() + 25}ms"; - }, - ), - ], + Flexible( + child: PlatformTextField( + keyboardType: TextInputType.number, + controller: controller, + placeholder: "Delay in milliseconds", + onSubmitted: (_) { + Navigator.of(context).pop( + Duration( + milliseconds: getValue().toInt(), + ), + ); + }, + ), + ), + PlatformIconButton( + icon: const Icon(Icons.add_rounded), + onPressed: () { + controller.text = "${getValue() + 25}ms"; + }, + ), + ], + ), ), ); } diff --git a/lib/components/Lyrics/SyncedLyrics.dart b/lib/components/Lyrics/SyncedLyrics.dart index 2b5c197c..ea130908 100644 --- a/lib/components/Lyrics/SyncedLyrics.dart +++ b/lib/components/Lyrics/SyncedLyrics.dart @@ -87,18 +87,24 @@ class SyncedLyrics extends HookConsumerWidget { Positioned.fill( child: Align( alignment: Alignment.centerRight, - child: PlatformIconButton( - tooltip: "Lyrics Delay", - icon: const Icon(Icons.av_timer_rounded), - onPressed: () async { - final delay = await showPlatformAlertDialog( - context, - builder: (context) => const LyricDelayAdjustDialog(), - ); - if (delay != null) { - ref.read(lyricDelayState.notifier).state = delay; - } - }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: PlatformFilledButton( + child: const Icon( + Icons.av_timer_rounded, + size: 16, + ), + onPressed: () async { + final delay = await showPlatformAlertDialog( + context, + builder: (context) => + const LyricDelayAdjustDialog(), + ); + if (delay != null) { + ref.read(lyricDelayState.notifier).state = delay; + } + }, + ), ), ), ), diff --git a/lib/components/Playlist/PlaylistCreateDialog.dart b/lib/components/Playlist/PlaylistCreateDialog.dart index ab96fc77..71fc1b3b 100644 --- a/lib/components/Playlist/PlaylistCreateDialog.dart +++ b/lib/components/Playlist/PlaylistCreateDialog.dart @@ -26,7 +26,7 @@ class PlaylistCreateDialog extends HookConsumerWidget { final collaborative = useState(false); return PlatformAlertDialog( - icon: Sidebar.brandLogo(), + macosAppIcon: Sidebar.brandLogo(), title: const Text("Create a Playlist"), primaryActions: [ PlatformFilledButton( diff --git a/lib/components/Settings/ColorSchemePickerDialog.dart b/lib/components/Settings/ColorSchemePickerDialog.dart index 84a515d4..d965f0d4 100644 --- a/lib/components/Settings/ColorSchemePickerDialog.dart +++ b/lib/components/Settings/ColorSchemePickerDialog.dart @@ -68,7 +68,7 @@ class ColorSchemePickerDialog extends HookConsumerWidget { ).key); return PlatformAlertDialog( - icon: Sidebar.brandLogo(), + macosAppIcon: Sidebar.brandLogo(), title: Text("Pick ${schemeType.name} color scheme"), primaryActions: [ PlatformFilledButton( diff --git a/lib/components/Shared/AdaptiveListTile.dart b/lib/components/Shared/AdaptiveListTile.dart index 9bb07bcd..48616d47 100644 --- a/lib/components/Shared/AdaptiveListTile.dart +++ b/lib/components/Shared/AdaptiveListTile.dart @@ -41,7 +41,7 @@ class AdaptiveListTile extends HookWidget { builder: (context) { return StatefulBuilder(builder: (context, update) { return PlatformAlertDialog( - icon: Sidebar.brandLogo(), + macosAppIcon: Sidebar.brandLogo(), title: title != null ? Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/components/Shared/DownloadConfirmationDialog.dart b/lib/components/Shared/DownloadConfirmationDialog.dart index d2f6d68c..07ef294b 100644 --- a/lib/components/Shared/DownloadConfirmationDialog.dart +++ b/lib/components/Shared/DownloadConfirmationDialog.dart @@ -9,7 +9,7 @@ class DownloadConfirmationDialog extends StatelessWidget { @override Widget build(BuildContext context) { return PlatformAlertDialog( - icon: Sidebar.brandLogo(), + macosAppIcon: Sidebar.brandLogo(), title: Padding( padding: const EdgeInsets.all(15), child: Row( diff --git a/lib/components/Shared/ReplaceDownloadedFileDialog.dart b/lib/components/Shared/ReplaceDownloadedFileDialog.dart index 1c3b16f0..883b1365 100644 --- a/lib/components/Shared/ReplaceDownloadedFileDialog.dart +++ b/lib/components/Shared/ReplaceDownloadedFileDialog.dart @@ -16,7 +16,7 @@ class ReplaceDownloadedFileDialog extends ConsumerWidget { final groupValue = ref.watch(replaceDownloadedFileState); return PlatformAlertDialog( - icon: Sidebar.brandLogo(), + macosAppIcon: Sidebar.brandLogo(), title: Text("Track ${track.name} Already Exists"), content: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/components/Shared/TrackTile.dart b/lib/components/Shared/TrackTile.dart index d7984c67..232faa56 100644 --- a/lib/components/Shared/TrackTile.dart +++ b/lib/components/Shared/TrackTile.dart @@ -96,7 +96,7 @@ class TrackTile extends HookConsumerWidget { return HookBuilder(builder: (context) { final playlistsCheck = useState({}); return PlatformAlertDialog( - icon: Sidebar.brandLogo(), + macosAppIcon: Sidebar.brandLogo(), title: PlatformText( "Add `${track.value.name}` to following Playlists", style: const TextStyle( diff --git a/lib/hooks/useUpdateChecker.dart b/lib/hooks/useUpdateChecker.dart index 797e6dc3..448c9fb7 100644 --- a/lib/hooks/useUpdateChecker.dart +++ b/lib/hooks/useUpdateChecker.dart @@ -57,7 +57,7 @@ void useUpdateChecker(WidgetRef ref) { const url = "https://spotube.netlify.app/other-downloads/stable-downloads"; return PlatformAlertDialog( - icon: Sidebar.brandLogo(), + macosAppIcon: Sidebar.brandLogo(), title: const PlatformText("Spotube has an update"), primaryActions: [ PlatformFilledButton( From 3b6bf27a984f5d4836143638396ed4b467c0eae7 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 24 Nov 2022 11:25:47 +0600 Subject: [PATCH 20/23] fix: settings Title alignment and play button card ripple effect in other platforms --- lib/components/Settings/Settings.dart | 6 ++---- lib/components/Shared/PlaybuttonCard.dart | 12 ++++-------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index 1049365f..5efd3efd 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -50,10 +50,8 @@ class Settings extends HookConsumerWidget { return SafeArea( child: PlatformScaffold( appBar: PageWindowTitleBar( - center: PlatformText( - "Settings", - style: PlatformTheme.of(context).textTheme?.headline, - ), + center: PlatformText.headline("Settings"), + centerTitle: true, ), body: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index 33d3558d..cb4cf3e3 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -50,14 +50,10 @@ class PlaybuttonCard extends HookWidget { ); final splash = usePlatformProperty( - (context) => PlatformProperty.multiPlatformGroup({ - InkRipple.splashFactory: {TargetPlatform.android, TargetPlatform.linux}, - NoSplash.splashFactory: { - TargetPlatform.windows, - TargetPlatform.macOS, - TargetPlatform.iOS, - } - }), + (context) => PlatformProperty.only( + android: InkRipple.splashFactory, + other: NoSplash.splashFactory, + ), ); final iconBgColor = PlatformTheme.of(context).primaryColor; From a8330ef2e1112012bbae19ee6a5c27a26c5fb719 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 24 Nov 2022 12:12:55 +0600 Subject: [PATCH 21/23] fix(mobile): SafeArea bugs and back button color --- lib/components/Home/Genres.dart | 3 +- lib/components/Library/UserLibrary.dart | 38 +- lib/components/Player/PlayerOverlay.dart | 6 +- lib/components/Search/Search.dart | 561 +++++++++--------- .../Shared/TrackCollectionView.dart | 9 +- lib/main.dart | 16 +- 6 files changed, 322 insertions(+), 311 deletions(-) diff --git a/lib/components/Home/Genres.dart b/lib/components/Home/Genres.dart index 0b0677f0..e555aa76 100644 --- a/lib/components/Home/Genres.dart +++ b/lib/components/Home/Genres.dart @@ -11,6 +11,7 @@ import 'package:spotube/components/Shared/Waypoint.dart'; import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; import 'package:spotube/provider/UserPreferences.dart'; +import 'package:spotube/utils/platform.dart'; class Genres extends HookConsumerWidget { const Genres({Key? key}) : super(key: key); @@ -43,7 +44,7 @@ class Genres extends HookConsumerWidget { ]; return PlatformScaffold( - appBar: PageWindowTitleBar(), + appBar: kIsDesktop ? PageWindowTitleBar() : null, body: ListView.builder( itemCount: categories.length, itemBuilder: (context, index) { diff --git a/lib/components/Library/UserLibrary.dart b/lib/components/Library/UserLibrary.dart index 2a5bffa5..e426b00e 100644 --- a/lib/components/Library/UserLibrary.dart +++ b/lib/components/Library/UserLibrary.dart @@ -23,26 +23,28 @@ class UserLibrary extends HookConsumerWidget { const UserAlbums(), ][index.value]; - return PlatformScaffold( - appBar: PageWindowTitleBar( - titleWidth: 347, - centerTitle: true, - center: PlatformTabBar( - androidIsScrollable: true, - selectedIndex: index.value, - onSelectedIndexChanged: (value) => index.value = value, - isNavigational: - PlatformProperty.byPlatformGroup(mobile: false, desktop: true), - tabs: [ - PlatformTab(label: 'Playlists', icon: const SizedBox.shrink()), - PlatformTab(label: 'Tracks', icon: const SizedBox.shrink()), - PlatformTab(label: 'Downloads', icon: const SizedBox.shrink()), - PlatformTab(label: 'Artists', icon: const SizedBox.shrink()), - PlatformTab(label: 'Albums', icon: const SizedBox.shrink()), - ], + return SafeArea( + child: PlatformScaffold( + appBar: PageWindowTitleBar( + titleWidth: 347, + centerTitle: true, + center: PlatformTabBar( + androidIsScrollable: true, + selectedIndex: index.value, + onSelectedIndexChanged: (value) => index.value = value, + isNavigational: + PlatformProperty.byPlatformGroup(mobile: false, desktop: true), + tabs: [ + PlatformTab(label: 'Playlists', icon: const SizedBox.shrink()), + PlatformTab(label: 'Tracks', icon: const SizedBox.shrink()), + PlatformTab(label: 'Downloads', icon: const SizedBox.shrink()), + PlatformTab(label: 'Artists', icon: const SizedBox.shrink()), + PlatformTab(label: 'Albums', icon: const SizedBox.shrink()), + ], + ), ), + body: body, ), - body: body, ); } } diff --git a/lib/components/Player/PlayerOverlay.dart b/lib/components/Player/PlayerOverlay.dart index aaa3d67f..a7726f68 100644 --- a/lib/components/Player/PlayerOverlay.dart +++ b/lib/components/Player/PlayerOverlay.dart @@ -81,7 +81,7 @@ class PlayerOverlay extends HookConsumerWidget { ), Row( children: [ - PlatformIconButton( + IconButton( icon: Icon( Icons.skip_previous_rounded, color: paletteColor.bodyTextColor, @@ -91,7 +91,7 @@ class PlayerOverlay extends HookConsumerWidget { }), Consumer( builder: (context, ref, _) { - return PlatformIconButton( + return IconButton( icon: Icon( ref.read(playbackProvider).isPlaying ? Icons.pause_rounded @@ -105,7 +105,7 @@ class PlayerOverlay extends HookConsumerWidget { ); }, ), - PlatformIconButton( + IconButton( icon: Icon( Icons.skip_next_rounded, color: paletteColor.bodyTextColor, diff --git a/lib/components/Search/Search.dart b/lib/components/Search/Search.dart index e5e12db2..b2584750 100644 --- a/lib/components/Search/Search.dart +++ b/lib/components/Search/Search.dart @@ -73,292 +73,299 @@ class Search extends HookConsumerWidget { } } - return PlatformScaffold( - appBar: !kIsMacOS ? PageWindowTitleBar() : null, - body: auth.isAnonymous - ? const AnonymousFallback() - : Column( - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 10, + return SafeArea( + child: PlatformScaffold( + appBar: kIsDesktop && !kIsMacOS ? PageWindowTitleBar() : null, + body: auth.isAnonymous + ? const AnonymousFallback() + : Column( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 10, + ), + child: PlatformTextField( + onChanged: (value) { + ref.read(searchTermStateProvider.notifier).state = + value; + }, + prefixIcon: Icons.search_rounded, + prefixIconColor: PlatformProperty.only( + ios: + PlatformTheme.of(context).textTheme?.caption?.color, + other: null, + ).resolve(platform!), + placeholder: "Search...", + onSubmitted: (value) { + onSearch(); + }, + ), ), - child: PlatformTextField( - onChanged: (value) { - ref.read(searchTermStateProvider.notifier).state = value; - }, - prefixIcon: Icons.search_rounded, - prefixIconColor: PlatformProperty.only( - ios: PlatformTheme.of(context).textTheme?.caption?.color, - other: null, - ).resolve(platform!), - placeholder: "Search...", - onSubmitted: (value) { - onSearch(); - }, - ), - ), - HookBuilder( - builder: (context) { - Playback playback = ref.watch(playbackProvider); - List albums = []; - List artists = []; - List tracks = []; - List playlists = []; - final pages = [ - ...searchTrack.pages, - ...searchAlbum.pages, - ...searchPlaylist.pages, - ...searchArtist.pages, - ].expand((page) => page ?? []).toList(); - for (MapEntry page in pages.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); + HookBuilder( + builder: (context) { + Playback playback = ref.watch(playbackProvider); + List albums = []; + List artists = []; + List tracks = []; + List playlists = []; + final pages = [ + ...searchTrack.pages, + ...searchAlbum.pages, + ...searchPlaylist.pages, + ...searchArtist.pages, + ].expand((page) => page ?? []).toList(); + for (MapEntry page in pages.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) - PlatformText.headline("Songs"), - if (searchTrack.isLoading && - !searchTrack.isFetchingNextPage) - const PlatformCircularProgressIndicator() - else if (searchTrack.hasError) - PlatformText(searchTrack - .error?[searchTrack.pageParams.last]) - else - ...tracks.asMap().entries.map((track) { - String duration = - "${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; - return TrackTile( - playback, - track: track, - duration: duration, - 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: TypeConversionUtils - .image_X_UrlString( - currentTrack.album?.images, - placeholder: - ImagePlaceholder.albumArt, - ), - ), - ); - } else if (isPlaylistPlaying && - currentTrack.id != null && - currentTrack.id != - playback.track?.id) { - playback.play(currentTrack); - } - }, - ); - }), - if (searchTrack.hasNextPage && tracks.isNotEmpty) - Center( - child: PlatformTextButton( - onPressed: searchTrack.isFetchingNextPage - ? null - : () => searchTrack.fetchNextPage(), - child: searchTrack.isFetchingNextPage - ? const PlatformCircularProgressIndicator() - : const PlatformText("Load more"), - ), - ), - if (playlists.isNotEmpty) - PlatformText.headline("Playlists"), - const SizedBox(height: 10), - if (searchPlaylist.isLoading && - !searchPlaylist.isFetchingNextPage) - const PlatformCircularProgressIndicator() - else if (searchPlaylist.hasError) - PlatformText(searchPlaylist - .error?[searchPlaylist.pageParams.last]) - else - ScrollConfiguration( - behavior: - ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }, - ), - child: Scrollbar( - scrollbarOrientation: - breakpoint > Breakpoints.md - ? ScrollbarOrientation.bottom - : ScrollbarOrientation.top, - controller: playlistController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: playlistController, - child: Row( - children: [ - ...playlists.mapIndexed( - (i, playlist) { - if (i == playlists.length - 1 && - searchPlaylist.hasNextPage) { - return Waypoint( - onEnter: () { - searchPlaylist - .fetchNextPage(); - }, - child: - const ShimmerPlaybuttonCard( - count: 1), - ); - } - return PlaylistCard(playlist); - }, - ), - ], - ), - ), - ), - ), - const SizedBox(height: 20), - if (artists.isNotEmpty) - PlatformText.headline("Artists"), - const SizedBox(height: 10), - if (searchArtist.isLoading && - !searchArtist.isFetchingNextPage) - const PlatformCircularProgressIndicator() - else if (searchArtist.hasError) - PlatformText(searchArtist - .error?[searchArtist.pageParams.last]) - else - ScrollConfiguration( - behavior: - ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }, - ), - child: Scrollbar( - controller: artistController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: artistController, - child: Row( - children: [ - ...artists.mapIndexed( - (i, artist) { - if (i == artists.length - 1 && - searchArtist.hasNextPage) { - return Waypoint( - onEnter: () { - searchArtist - .fetchNextPage(); - }, - child: - const ShimmerPlaybuttonCard( - count: 1), - ); - } - return Container( - margin: - const EdgeInsets.symmetric( - horizontal: 15), - child: ArtistCard(artist), - ); - }, - ), - ], - ), - ), - ), - ), - const SizedBox(height: 20), - if (albums.isNotEmpty) - PlatformText( - "Albums", - style: Theme.of(context).textTheme.headline5, - ), - const SizedBox(height: 10), - if (searchAlbum.isLoading && - !searchAlbum.isFetchingNextPage) - const PlatformCircularProgressIndicator() - else if (searchAlbum.hasError) - PlatformText(searchAlbum - .error?[searchAlbum.pageParams.last]) - else - ScrollConfiguration( - behavior: - ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }, - ), - child: Scrollbar( - controller: albumController, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - controller: albumController, - child: Row( - children: [ - ...albums.mapIndexed((i, album) { - if (i == albums.length - 1 && - searchAlbum.hasNextPage) { - return Waypoint( - onEnter: () { - searchAlbum.fetchNextPage(); - }, - child: - const ShimmerPlaybuttonCard( - count: 1), - ); - } - return AlbumCard( - TypeConversionUtils - .simpleAlbum_X_Album( - album, + return Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 20, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (tracks.isNotEmpty) + PlatformText.headline("Songs"), + if (searchTrack.isLoading && + !searchTrack.isFetchingNextPage) + const PlatformCircularProgressIndicator() + else if (searchTrack.hasError) + PlatformText(searchTrack + .error?[searchTrack.pageParams.last]) + else + ...tracks.asMap().entries.map((track) { + String duration = + "${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; + return TrackTile( + playback, + track: track, + duration: duration, + 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: TypeConversionUtils + .image_X_UrlString( + currentTrack.album?.images, + placeholder: + ImagePlaceholder.albumArt, ), - ); - }), - ], + ), + ); + } else if (isPlaylistPlaying && + currentTrack.id != null && + currentTrack.id != + playback.track?.id) { + playback.play(currentTrack); + } + }, + ); + }), + if (searchTrack.hasNextPage && + tracks.isNotEmpty) + Center( + child: PlatformTextButton( + onPressed: searchTrack.isFetchingNextPage + ? null + : () => searchTrack.fetchNextPage(), + child: searchTrack.isFetchingNextPage + ? const PlatformCircularProgressIndicator() + : const PlatformText("Load more"), + ), + ), + if (playlists.isNotEmpty) + PlatformText.headline("Playlists"), + const SizedBox(height: 10), + if (searchPlaylist.isLoading && + !searchPlaylist.isFetchingNextPage) + const PlatformCircularProgressIndicator() + else if (searchPlaylist.hasError) + PlatformText(searchPlaylist + .error?[searchPlaylist.pageParams.last]) + else + ScrollConfiguration( + behavior: ScrollConfiguration.of(context) + .copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }, + ), + child: Scrollbar( + scrollbarOrientation: + breakpoint > Breakpoints.md + ? ScrollbarOrientation.bottom + : ScrollbarOrientation.top, + controller: playlistController, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: playlistController, + child: Row( + children: [ + ...playlists.mapIndexed( + (i, playlist) { + if (i == playlists.length - 1 && + searchPlaylist + .hasNextPage) { + return Waypoint( + onEnter: () { + searchPlaylist + .fetchNextPage(); + }, + child: + const ShimmerPlaybuttonCard( + count: 1), + ); + } + return PlaylistCard(playlist); + }, + ), + ], + ), ), ), ), - ), - ], + const SizedBox(height: 20), + if (artists.isNotEmpty) + PlatformText.headline("Artists"), + const SizedBox(height: 10), + if (searchArtist.isLoading && + !searchArtist.isFetchingNextPage) + const PlatformCircularProgressIndicator() + else if (searchArtist.hasError) + PlatformText(searchArtist + .error?[searchArtist.pageParams.last]) + else + ScrollConfiguration( + behavior: ScrollConfiguration.of(context) + .copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }, + ), + child: Scrollbar( + controller: artistController, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: artistController, + child: Row( + children: [ + ...artists.mapIndexed( + (i, artist) { + if (i == artists.length - 1 && + searchArtist.hasNextPage) { + return Waypoint( + onEnter: () { + searchArtist + .fetchNextPage(); + }, + child: + const ShimmerPlaybuttonCard( + count: 1), + ); + } + return Container( + margin: const EdgeInsets + .symmetric( + horizontal: 15), + child: ArtistCard(artist), + ); + }, + ), + ], + ), + ), + ), + ), + const SizedBox(height: 20), + if (albums.isNotEmpty) + PlatformText( + "Albums", + style: + Theme.of(context).textTheme.headline5, + ), + const SizedBox(height: 10), + if (searchAlbum.isLoading && + !searchAlbum.isFetchingNextPage) + const PlatformCircularProgressIndicator() + else if (searchAlbum.hasError) + PlatformText(searchAlbum + .error?[searchAlbum.pageParams.last]) + else + ScrollConfiguration( + behavior: ScrollConfiguration.of(context) + .copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }, + ), + child: Scrollbar( + controller: albumController, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: albumController, + child: Row( + children: [ + ...albums.mapIndexed((i, album) { + if (i == albums.length - 1 && + searchAlbum.hasNextPage) { + return Waypoint( + onEnter: () { + searchAlbum.fetchNextPage(); + }, + child: + const ShimmerPlaybuttonCard( + count: 1), + ); + } + return AlbumCard( + TypeConversionUtils + .simpleAlbum_X_Album( + album, + ), + ); + }), + ], + ), + ), + ), + ), + ], + ), ), ), - ), - ); - }, - ) - ], - ), + ); + }, + ) + ], + ), + ), ); } } diff --git a/lib/components/Shared/TrackCollectionView.dart b/lib/components/Shared/TrackCollectionView.dart index c4bbb608..d1025372 100644 --- a/lib/components/Shared/TrackCollectionView.dart +++ b/lib/components/Shared/TrackCollectionView.dart @@ -114,11 +114,7 @@ class TrackCollectionView extends HookConsumerWidget { ? PageWindowTitleBar( backgroundColor: color?.color, foregroundColor: color?.titleTextColor, - leading: Row( - children: [ - PlatformBackButton(color: color?.titleTextColor) - ], - ), + leading: PlatformBackButton(color: color?.titleTextColor), ) : null, body: CustomScrollView( @@ -130,6 +126,9 @@ class TrackCollectionView extends HookConsumerWidget { pinned: true, expandedHeight: 400, automaticallyImplyLeading: kIsMobile, + leading: kIsMobile + ? PlatformBackButton(color: color?.titleTextColor) + : null, iconTheme: IconThemeData(color: color?.titleTextColor), primary: true, backgroundColor: color?.color, diff --git a/lib/main.dart b/lib/main.dart index 9a421cd9..aa1e687d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -235,13 +235,15 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { macosTheme: macosTheme, macosDarkTheme: macosDarkTheme, themeMode: themeMode, - windowButtonConfig: PlatformWindowButtonConfig( - isMaximized: () => appWindow.isMaximized, - onClose: appWindow.close, - onRestore: appWindow.restore, - onMaximize: appWindow.maximize, - onMinimize: appWindow.minimize, - ), + windowButtonConfig: kIsDesktop + ? PlatformWindowButtonConfig( + isMaximized: () => appWindow.isMaximized, + onClose: appWindow.close, + onRestore: appWindow.restore, + onMaximize: appWindow.maximize, + onMinimize: appWindow.minimize, + ) + : null, shortcuts: PlatformProperty.all({ ...WidgetsApp.defaultShortcuts.map((key, value) { return MapEntry( From 472da6b8b1c3e06b666da58351f3feafbfe6c98a Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 24 Nov 2022 12:24:05 +0600 Subject: [PATCH 22/23] feat: set platform to default platform on start up --- lib/main.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/main.dart b/lib/main.dart index aa1e687d..3d54019e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -159,6 +159,11 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { super.initState(); SharedPreferences.getInstance().then(((value) => localStorage = value)); WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + setState(() { + appPlatform = Theme.of(context).platform; + }); + }); } @override From 8cd3b9ad1239bd9581a5a92f1174952cbdb9fa11 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 24 Nov 2022 12:58:16 +0600 Subject: [PATCH 23/23] chore: pub update --- pubspec.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index d4baeb75..645a552d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,7 +21,7 @@ packages: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "4.7.0" + version: "5.2.0" app_package_maker: dependency: transitive description: @@ -240,7 +240,7 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "2.3.0" + version: "2.3.1" build_config: dependency: transitive description: @@ -261,7 +261,7 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "2.0.9" + version: "2.1.0" build_runner: dependency: "direct dev" description: @@ -429,7 +429,7 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "2.2.3" + version: "2.2.4" dbus: dependency: "direct main" description: @@ -478,7 +478,7 @@ packages: name: file_picker url: "https://pub.dartlang.org" source: hosted - version: "5.2.0" + version: "5.2.2" fixnum: dependency: transitive description: @@ -593,7 +593,7 @@ packages: name: flutter_riverpod url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.1.1" flutter_rust_bridge: dependency: transitive description: @@ -645,7 +645,7 @@ packages: name: go_router url: "https://pub.dartlang.org" source: hosted - version: "5.0.5" + version: "5.2.0" graphs: dependency: transitive description: @@ -680,14 +680,14 @@ packages: name: hive_generator url: "https://pub.dartlang.org" source: hosted - version: "1.1.3" + version: "2.0.0" hooks_riverpod: dependency: "direct main" description: name: hooks_riverpod url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.1.1" html: dependency: "direct main" description: @@ -1004,35 +1004,35 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "10.0.2" + version: "10.2.0" permission_handler_android: dependency: transitive description: name: permission_handler_android url: "https://pub.dartlang.org" source: hosted - version: "10.1.0" + version: "10.2.0" permission_handler_apple: dependency: transitive description: name: permission_handler_apple url: "https://pub.dartlang.org" source: hosted - version: "9.0.4" + version: "9.0.7" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.7.0" + version: "3.9.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows url: "https://pub.dartlang.org" source: hosted - version: "0.1.0" + version: "0.1.2" petitparser: dependency: transitive description: @@ -1123,7 +1123,7 @@ packages: name: riverpod url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "2.1.1" rxdart: dependency: transitive description: @@ -1233,14 +1233,14 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "1.2.2" + version: "1.2.6" source_helper: dependency: transitive description: name: source_helper url: "https://pub.dartlang.org" source: hosted - version: "1.3.2" + version: "1.3.3" source_span: dependency: transitive description: