From 3282370f74f323c00116fa8626fd1440ee9d4922 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 9 Dec 2022 11:25:44 +0600 Subject: [PATCH] feat: tablet mode navigation bar & windows semi transparent bg, --- lib/components/player/player_queue.dart | 3 +- .../playlist/playlist_genre_view.dart | 3 +- lib/components/root/sidebar.dart | 245 ++++++++---------- .../adaptive/adaptive_popup_menu_button.dart | 5 +- .../dialogs/replace_downloaded_dialog.dart | 8 +- .../shared/page_window_title_bar.dart | 3 +- lib/hooks/use_breakpoints.dart | 4 +- lib/main.dart | 3 +- lib/pages/artist/artist.dart | 2 +- lib/pages/desktop_login/desktop_login.dart | 5 +- lib/pages/desktop_login/login_tutorial.dart | 1 + lib/pages/player/player.dart | 5 +- lib/pages/root/root_app.dart | 4 +- lib/pages/search/search.dart | 6 +- lib/pages/settings/settings.dart | 4 +- lib/themes/light_theme.dart | 6 + pubspec.lock | 4 +- pubspec.yaml | 2 +- 18 files changed, 150 insertions(+), 163 deletions(-) diff --git a/lib/components/player/player_queue.dart b/lib/components/player/player_queue.dart index a160a7da9..ee6f92dfa 100644 --- a/lib/components/player/player_queue.dart +++ b/lib/components/player/player_queue.dart @@ -34,7 +34,8 @@ class PlayerQueue extends HookConsumerWidget { topLeft: Radius.circular(10), topRight: Radius.circular(10), ); - final headlineColor = Theme.of(context).textTheme.headline4?.color; + final headlineColor = + PlatformTheme.of(context).textTheme?.subheading?.color; useEffect(() { if (playback.track == null || playback.playlist == null) return null; diff --git a/lib/components/playlist/playlist_genre_view.dart b/lib/components/playlist/playlist_genre_view.dart index a0aeb0d0a..347089f58 100644 --- a/lib/components/playlist/playlist_genre_view.dart +++ b/lib/components/playlist/playlist_genre_view.dart @@ -25,9 +25,8 @@ class PlaylistGenreView extends ConsumerWidget { ), body: Column( children: [ - Text( + PlatformText.subheading( genreName, - style: Theme.of(context).textTheme.headline4, textAlign: TextAlign.center, ), Consumer( diff --git a/lib/components/root/sidebar.dart b/lib/components/root/sidebar.dart index 9c61eadc5..5820658df 100644 --- a/lib/components/root/sidebar.dart +++ b/lib/components/root/sidebar.dart @@ -15,12 +15,9 @@ import 'package:spotube/provider/spotify_provider.dart'; import 'package:spotube/provider/user_preferences_provider.dart'; import 'package:spotube/services/queries/queries.dart'; -import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:fluent_ui/fluent_ui.dart' as fluent_ui; -final sidebarExtendedStateProvider = StateProvider((ref) => null); - class Sidebar extends HookConsumerWidget { final int selectedIndex; final void Function(int) onSelectedIndexChanged; @@ -48,41 +45,73 @@ class Sidebar extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final breakpoints = useBreakpoints(); - final extended = useState(false); final downloadCount = ref.watch( downloaderProvider.select((s) => s.currentlyRunning), ); - final forceExtended = ref.watch(sidebarExtendedStateProvider); - - useEffect(() { - if (forceExtended != null) { - if (extended.value != forceExtended) { - extended.value = forceExtended; - } - return; - } - if (breakpoints.isMd && extended.value) { - extended.value = false; - } else if (breakpoints.isMoreThanOrEqualTo(Breakpoints.lg) && - !extended.value) { - extended.value = true; - } - return null; - }); final layoutMode = ref.watch(userPreferencesProvider.select((s) => s.layoutMode)); + if (breakpoints.isMd) { + return Row( + children: [ + NavigationRail( + selectedIndex: selectedIndex, + onDestinationSelected: onSelectedIndexChanged, + labelType: NavigationRailLabelType.all, + extended: false, + backgroundColor: PlatformTheme.of(context).scaffoldBackgroundColor, + leading: Padding( + padding: const EdgeInsets.all(8.0), + child: brandLogo(), + ), + trailing: PlatformIconButton( + icon: const Icon(fluent_ui.FluentIcons.settings), + onPressed: () => goToSettings(context), + ), + destinations: [ + for (final e in sidebarTileList) + NavigationRailDestination( + icon: Badge( + badgeColor: PlatformTheme.of(context).primaryColor!, + showBadge: e.title == "Library" && downloadCount > 0, + badgeContent: Text( + downloadCount.toString(), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + ), + ), + child: Icon(e.icon), + ), + label: PlatformText.label( + e.title, + style: selectedIndex == sidebarTileList.indexOf(e) + ? TextStyle( + color: PlatformTheme.of(context).primaryColor, + fontWeight: FontWeight.bold, + ) + : null, + ), + ), + ], + ), + Container( + width: 1, + height: double.infinity, + color: PlatformTheme.of(context).borderColor, + ), + Expanded(child: child) + ], + ); + } + if (layoutMode == LayoutMode.compact || (breakpoints.isSm && layoutMode == LayoutMode.adaptive)) { return PlatformScaffold(body: child); } - void toggleExtended() => - ref.read(sidebarExtendedStateProvider.notifier).state = - !(forceExtended ?? extended.value); - return SafeArea( top: false, child: PlatformSidebar( @@ -119,50 +148,18 @@ class Sidebar extends HookConsumerWidget { }, ), ), - expanded: extended.value, - header: Column( - children: [ - if (kIsMacOS) - SizedBox( - height: appWindow.titleBarHeight, - width: extended.value ? 256 : 80, - child: MoveWindow( - child: !extended.value - ? Center( - child: PlatformIconButton( - icon: const Icon(Icons.menu_rounded), - onPressed: toggleExtended, - ), - ) - : null, - ), - ), - if (!kIsDesktop && !extended.value) - Center( - child: PlatformIconButton( - icon: const Icon(Icons.menu_rounded), - onPressed: toggleExtended, - ), + expanded: true, + header: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + brandLogo(), + const SizedBox( + width: 10, ), - (extended.value) - ? 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(), - ], + PlatformText.headline("Spotube"), + ], + ), ), windowsFooterItems: [ fluent_ui.PaneItemAction( @@ -170,17 +167,15 @@ class Sidebar extends HookConsumerWidget { onTap: () => goToSettings(context), ), ], - footer: SidebarFooter(extended: extended.value), + footer: const SidebarFooter(), ), ); } } class SidebarFooter extends HookConsumerWidget { - final bool extended; const SidebarFooter({ Key? key, - required this.extended, }) : super(key: key); @override @@ -188,7 +183,7 @@ class SidebarFooter extends HookConsumerWidget { final auth = ref.watch(authProvider); return SizedBox( - width: extended ? 256 : 80, + width: 256, child: HookBuilder( builder: (context) { final me = useQuery( @@ -211,71 +206,53 @@ class SidebarFooter extends HookConsumerWidget { 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: PlatformCircularProgressIndicator(), - ) - 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, - ), + return Padding( + padding: const EdgeInsets.all(16).copyWith(left: 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (auth.isLoggedIn && data == null) + const Center( + child: PlatformCircularProgressIndicator(), + ) + 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: PlatformTheme.of(context) - .textTheme - ?.body - ?.copyWith(fontWeight: FontWeight.bold), - ), + ), + const SizedBox( + width: 10, + ), + Flexible( + child: Text( + data.displayName ?? "Guest", + maxLines: 1, + softWrap: false, + overflow: TextOverflow.fade, + style: PlatformTheme.of(context) + .textTheme + ?.body + ?.copyWith(fontWeight: FontWeight.bold), ), - ], - ), + ), + ], ), - PlatformIconButton( - 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, - ), - ), - ), - ); - } + ), + PlatformIconButton( + icon: const Icon(Icons.settings_outlined), + onPressed: () => Sidebar.goToSettings(context)), + ], + )); }, ), ); diff --git a/lib/components/shared/adaptive/adaptive_popup_menu_button.dart b/lib/components/shared/adaptive/adaptive_popup_menu_button.dart index e1a6e95af..a94b585ce 100644 --- a/lib/components/shared/adaptive/adaptive_popup_menu_button.dart +++ b/lib/components/shared/adaptive/adaptive_popup_menu_button.dart @@ -33,7 +33,7 @@ class Action extends StatelessWidget { } return PlatformTextButton( style: TextButton.styleFrom( - foregroundColor: Theme.of(context).textTheme.bodyMedium?.color, + foregroundColor: PlatformTextTheme.of(context).body?.color, padding: const EdgeInsets.all(20), ), onPressed: onPressed, @@ -86,7 +86,8 @@ class AdaptiveActions extends HookWidget { .toList(), ); }, - backgroundColor: Theme.of(context).cardColor, + backgroundColor: + PlatformTheme.of(context).secondaryBackgroundColor!, ); }, ); diff --git a/lib/components/shared/dialogs/replace_downloaded_dialog.dart b/lib/components/shared/dialogs/replace_downloaded_dialog.dart index 9b8b441f0..5ba95772f 100644 --- a/lib/components/shared/dialogs/replace_downloaded_dialog.dart +++ b/lib/components/shared/dialogs/replace_downloaded_dialog.dart @@ -25,12 +25,12 @@ class ReplaceDownloadedDialog extends ConsumerWidget { RadioListTile( dense: true, contentPadding: EdgeInsets.zero, - activeColor: Theme.of(context).primaryColor, + activeColor: PlatformTheme.of(context).primaryColor, value: true, groupValue: groupValue, onChanged: (value) { if (value != null) { - ref.read(replaceDownloadedFileState.state).state = value; + ref.read(replaceDownloadedFileState.notifier).state = value; } }, title: const Text("Replace all downloaded tracks"), @@ -38,12 +38,12 @@ class ReplaceDownloadedDialog extends ConsumerWidget { RadioListTile( dense: true, contentPadding: EdgeInsets.zero, - activeColor: Theme.of(context).primaryColor, + activeColor: PlatformTheme.of(context).primaryColor, value: false, groupValue: groupValue, onChanged: (value) { if (value != null) { - ref.read(replaceDownloadedFileState.state).state = value; + ref.read(replaceDownloadedFileState.notifier).state = value; } }, title: const Text("Skip downloading all downloaded tracks"), diff --git a/lib/components/shared/page_window_title_bar.dart b/lib/components/shared/page_window_title_bar.dart index 991a5b45a..b7fdac9bf 100644 --- a/lib/components/shared/page_window_title_bar.dart +++ b/lib/components/shared/page_window_title_bar.dart @@ -1,7 +1,6 @@ import 'package:bitsdojo_window/bitsdojo_window.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/utils/platform.dart'; @@ -78,7 +77,7 @@ class _PageWindowTitleBarState extends State { return null; }, [platform, widget.hideWhenWindows]); - var appBar = PlatformAppBar( + final appBar = PlatformAppBar( actions: [ ...?widget.actions, if (!kIsMacOS && !kIsMobile) diff --git a/lib/hooks/use_breakpoints.dart b/lib/hooks/use_breakpoints.dart index 5a52e8403..7177ed8d8 100644 --- a/lib/hooks/use_breakpoints.dart +++ b/lib/hooks/use_breakpoints.dart @@ -83,12 +83,12 @@ BreakpointUtils useBreakpoints() { width <= 1366 && breakpoint.value != Breakpoints.lg) { breakpoint.value = Breakpoints.lg; - } else if (width > 414 && + } else if (width > 500 && width <= 800 && breakpoint.value != Breakpoints.md) { breakpoint.value = Breakpoints.md; } else if (width >= 250 && - width <= 414 && + width <= 500 && breakpoint.value != Breakpoints.sm) { breakpoint.value = Breakpoints.sm; } diff --git a/lib/main.dart b/lib/main.dart index 5a80e69f1..8b2c3da7e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ 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:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -40,7 +41,7 @@ void main() async { final savedSize = rawSize != null ? json.decode(rawSize) : null; final double? height = savedSize?["height"]; final double? width = savedSize?["width"]; - appWindow.minSize = const Size(1020, 700); + appWindow.minSize = const Size(kReleaseMode ? 1020 : 300, 700); appWindow.alignment = Alignment.center; appWindow.title = "Spotube"; if (height != null && width != null && height >= 700 && width >= 359) { diff --git a/lib/pages/artist/artist.dart b/lib/pages/artist/artist.dart index 14faafc1e..dfc36fd12 100644 --- a/lib/pages/artist/artist.dart +++ b/lib/pages/artist/artist.dart @@ -268,7 +268,7 @@ class ArtistPage extends HookConsumerWidget { Container( margin: const EdgeInsets.symmetric(horizontal: 5), decoration: BoxDecoration( - color: Theme.of(context).primaryColor, + color: PlatformTheme.of(context).primaryColor, borderRadius: BorderRadius.circular(50), ), child: PlatformIconButton( diff --git a/lib/pages/desktop_login/desktop_login.dart b/lib/pages/desktop_login/desktop_login.dart index a4de341ad..19a8aeadd 100644 --- a/lib/pages/desktop_login/desktop_login.dart +++ b/lib/pages/desktop_login/desktop_login.dart @@ -16,7 +16,10 @@ class DesktopLoginPage extends HookConsumerWidget { return SafeArea( child: PlatformScaffold( - appBar: PageWindowTitleBar(leading: const PlatformBackButton()), + appBar: PageWindowTitleBar( + leading: const PlatformBackButton(), + hideWhenWindows: false, + ), body: SingleChildScrollView( child: Center( child: Container( diff --git a/lib/pages/desktop_login/login_tutorial.dart b/lib/pages/desktop_login/login_tutorial.dart index b320f6385..8e3331b84 100644 --- a/lib/pages/desktop_login/login_tutorial.dart +++ b/lib/pages/desktop_login/login_tutorial.dart @@ -18,6 +18,7 @@ class LoginTutorial extends ConsumerWidget { return PlatformScaffold( appBar: PageWindowTitleBar( + hideWhenWindows: false, leading: PlatformTextButton( child: const PlatformText("Exit"), onPressed: () { diff --git a/lib/pages/player/player.dart b/lib/pages/player/player.dart index 8468eefeb..2873deb91 100644 --- a/lib/pages/player/player.dart +++ b/lib/pages/player/player.dart @@ -64,12 +64,13 @@ class PlayerView extends HookConsumerWidget { return PlatformScaffold( appBar: PageWindowTitleBar( + hideWhenWindows: false, backgroundColor: Colors.transparent, foregroundColor: paletteColor.titleTextColor, toolbarOpacity: 0, leading: PlatformBackButton( color: PlatformProperty.only( - macos: Colors.transparent, + macos: Colors.black, other: paletteColor.titleTextColor, ).resolve(platform!), ), @@ -88,7 +89,7 @@ class PlayerView extends HookConsumerWidget { textStyle: PlatformTheme.of(context).textTheme!.body!, color: paletteColor.color.withOpacity(.5), child: SafeArea( - child: Column( + child: ListView( children: [ Padding( padding: const EdgeInsets.all(10), diff --git a/lib/pages/root/root_app.dart b/lib/pages/root/root_app.dart index 847aa9489..d601ddad3 100644 --- a/lib/pages/root/root_app.dart +++ b/lib/pages/root/root_app.dart @@ -4,11 +4,13 @@ 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/collections/side_bar_tiles.dart'; import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart'; import 'package:spotube/components/root/bottom_player.dart'; import 'package:spotube/components/root/sidebar.dart'; import 'package:spotube/components/root/spotube_navigation_bar.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart'; +import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_update_checker.dart'; import 'package:spotube/provider/downloader_provider.dart'; @@ -65,7 +67,7 @@ class RootApp extends HookConsumerWidget { return PlatformScaffold( appBar: platform == TargetPlatform.windows - ? PageWindowTitleBar(hideWhenWindows: false) + ? PageWindowTitleBar(hideWhenWindows: false) as PreferredSizeWidget? : null, body: Sidebar( selectedIndex: index.value, diff --git a/lib/pages/search/search.dart b/lib/pages/search/search.dart index 7b6f36a81..6f3ff966a 100644 --- a/lib/pages/search/search.dart +++ b/lib/pages/search/search.dart @@ -302,11 +302,7 @@ class SearchPage extends HookConsumerWidget { .error?[searchArtist.pageParams.last]), const SizedBox(height: 20), if (albums.isNotEmpty) - PlatformText( - "Albums", - style: - Theme.of(context).textTheme.headline5, - ), + PlatformText.subheading("Albums"), const SizedBox(height: 10), ScrollConfiguration( behavior: diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index d99e04d73..3c24fd0a6 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -71,7 +71,7 @@ class SettingsPage extends HookConsumerWidget { AdaptiveListTile( leading: Icon( Icons.login_rounded, - color: Theme.of(context).primaryColor, + color: PlatformTheme.of(context).primaryColor, ), title: SizedBox( height: 50, @@ -82,7 +82,7 @@ class SettingsPage extends HookConsumerWidget { "Login with your Spotify account", maxLines: 1, style: TextStyle( - color: Theme.of(context).primaryColor, + color: PlatformTheme.of(context).primaryColor, ), ), ), diff --git a/lib/themes/light_theme.dart b/lib/themes/light_theme.dart index 188be16ea..8be9bc74e 100644 --- a/lib/themes/light_theme.dart +++ b/lib/themes/light_theme.dart @@ -126,6 +126,9 @@ final windowsTheme = fluent_ui.ThemeData.light().copyWith( iconSize: fluent_ui.ButtonState.all(20), ), ), + navigationPaneTheme: fluent_ui.NavigationPaneThemeData( + backgroundColor: fluent_ui.Colors.grey[100].withOpacity(0.5), + ), ); final windowsDarkTheme = fluent_ui.ThemeData.dark().copyWith( buttonTheme: fluent_ui.ButtonThemeData( @@ -133,6 +136,9 @@ final windowsDarkTheme = fluent_ui.ThemeData.dark().copyWith( iconSize: fluent_ui.ButtonState.all(20), ), ), + navigationPaneTheme: fluent_ui.NavigationPaneThemeData( + backgroundColor: fluent_ui.Colors.grey[900].withOpacity(0.5), + ), ); final macosTheme = MacosThemeData.light().copyWith( pushButtonTheme: const PushButtonThemeData( diff --git a/pubspec.lock b/pubspec.lock index bc6e1c4ac..8170dfeb2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1051,8 +1051,8 @@ packages: dependency: "direct main" description: path: "." - ref: fdff4771fed193aece0862c3216a8457410a856a - resolved-ref: fdff4771fed193aece0862c3216a8457410a856a + ref: "7a4cda31e434de7cbee92ac7f418903641a2f023" + resolved-ref: "7a4cda31e434de7cbee92ac7f418903641a2f023" url: "https://github.com/KRTirtho/platform_ui.git" source: git version: "0.1.0" diff --git a/pubspec.yaml b/pubspec.yaml index bca6a4482..9db31b792 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,7 +65,7 @@ dependencies: platform_ui: git: url: https://github.com/KRTirtho/platform_ui.git - ref: fdff4771fed193aece0862c3216a8457410a856a + ref: 7a4cda31e434de7cbee92ac7f418903641a2f023 fluent_ui: ^4.0.3 macos_ui: ^1.7.5 libadwaita: ^1.2.5