feat: tablet mode navigation bar & windows semi transparent bg,

This commit is contained in:
Kingkor Roy Tirtho 2022-12-09 11:25:44 +06:00
parent 65cad07e3a
commit 3282370f74
18 changed files with 150 additions and 163 deletions

View File

@ -34,7 +34,8 @@ class PlayerQueue extends HookConsumerWidget {
topLeft: Radius.circular(10), topLeft: Radius.circular(10),
topRight: Radius.circular(10), topRight: Radius.circular(10),
); );
final headlineColor = Theme.of(context).textTheme.headline4?.color; final headlineColor =
PlatformTheme.of(context).textTheme?.subheading?.color;
useEffect(() { useEffect(() {
if (playback.track == null || playback.playlist == null) return null; if (playback.track == null || playback.playlist == null) return null;

View File

@ -25,9 +25,8 @@ class PlaylistGenreView extends ConsumerWidget {
), ),
body: Column( body: Column(
children: [ children: [
Text( PlatformText.subheading(
genreName, genreName,
style: Theme.of(context).textTheme.headline4,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
Consumer( Consumer(

View File

@ -15,12 +15,9 @@ import 'package:spotube/provider/spotify_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/services/queries/queries.dart'; import 'package:spotube/services/queries/queries.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart';
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui; import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
final sidebarExtendedStateProvider = StateProvider<bool?>((ref) => null);
class Sidebar extends HookConsumerWidget { class Sidebar extends HookConsumerWidget {
final int selectedIndex; final int selectedIndex;
final void Function(int) onSelectedIndexChanged; final void Function(int) onSelectedIndexChanged;
@ -48,41 +45,73 @@ class Sidebar extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final breakpoints = useBreakpoints(); final breakpoints = useBreakpoints();
final extended = useState(false);
final downloadCount = ref.watch( final downloadCount = ref.watch(
downloaderProvider.select((s) => s.currentlyRunning), 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 = final layoutMode =
ref.watch(userPreferencesProvider.select((s) => s.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 || if (layoutMode == LayoutMode.compact ||
(breakpoints.isSm && layoutMode == LayoutMode.adaptive)) { (breakpoints.isSm && layoutMode == LayoutMode.adaptive)) {
return PlatformScaffold(body: child); return PlatformScaffold(body: child);
} }
void toggleExtended() =>
ref.read(sidebarExtendedStateProvider.notifier).state =
!(forceExtended ?? extended.value);
return SafeArea( return SafeArea(
top: false, top: false,
child: PlatformSidebar( child: PlatformSidebar(
@ -119,33 +148,8 @@ class Sidebar extends HookConsumerWidget {
}, },
), ),
), ),
expanded: extended.value, expanded: true,
header: Column( header: Padding(
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,
),
),
(extended.value)
? Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Row( child: Row(
children: [ children: [
@ -154,15 +158,8 @@ class Sidebar extends HookConsumerWidget {
width: 10, width: 10,
), ),
PlatformText.headline("Spotube"), PlatformText.headline("Spotube"),
PlatformIconButton(
icon: const Icon(Icons.menu_rounded),
onPressed: toggleExtended,
),
], ],
), ),
)
: brandLogo(),
],
), ),
windowsFooterItems: [ windowsFooterItems: [
fluent_ui.PaneItemAction( fluent_ui.PaneItemAction(
@ -170,17 +167,15 @@ class Sidebar extends HookConsumerWidget {
onTap: () => goToSettings(context), onTap: () => goToSettings(context),
), ),
], ],
footer: SidebarFooter(extended: extended.value), footer: const SidebarFooter(),
), ),
); );
} }
} }
class SidebarFooter extends HookConsumerWidget { class SidebarFooter extends HookConsumerWidget {
final bool extended;
const SidebarFooter({ const SidebarFooter({
Key? key, Key? key,
required this.extended,
}) : super(key: key); }) : super(key: key);
@override @override
@ -188,7 +183,7 @@ class SidebarFooter extends HookConsumerWidget {
final auth = ref.watch(authProvider); final auth = ref.watch(authProvider);
return SizedBox( return SizedBox(
width: extended ? 256 : 80, width: 256,
child: HookBuilder( child: HookBuilder(
builder: (context) { builder: (context) {
final me = useQuery( final me = useQuery(
@ -211,7 +206,6 @@ class SidebarFooter extends HookConsumerWidget {
return; return;
}, [auth.isLoggedIn, me.hasData]); }, [auth.isLoggedIn, me.hasData]);
if (extended) {
return Padding( return Padding(
padding: const EdgeInsets.all(16).copyWith(left: 0), padding: const EdgeInsets.all(16).copyWith(left: 0),
child: Row( child: Row(
@ -259,23 +253,6 @@ class SidebarFooter extends HookConsumerWidget {
onPressed: () => Sidebar.goToSettings(context)), 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,
),
),
),
);
}
}, },
), ),
); );

View File

@ -33,7 +33,7 @@ class Action extends StatelessWidget {
} }
return PlatformTextButton( return PlatformTextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: Theme.of(context).textTheme.bodyMedium?.color, foregroundColor: PlatformTextTheme.of(context).body?.color,
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
), ),
onPressed: onPressed, onPressed: onPressed,
@ -86,7 +86,8 @@ class AdaptiveActions extends HookWidget {
.toList(), .toList(),
); );
}, },
backgroundColor: Theme.of(context).cardColor, backgroundColor:
PlatformTheme.of(context).secondaryBackgroundColor!,
); );
}, },
); );

View File

@ -25,12 +25,12 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
RadioListTile<bool>( RadioListTile<bool>(
dense: true, dense: true,
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
activeColor: Theme.of(context).primaryColor, activeColor: PlatformTheme.of(context).primaryColor,
value: true, value: true,
groupValue: groupValue, groupValue: groupValue,
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
ref.read(replaceDownloadedFileState.state).state = value; ref.read(replaceDownloadedFileState.notifier).state = value;
} }
}, },
title: const Text("Replace all downloaded tracks"), title: const Text("Replace all downloaded tracks"),
@ -38,12 +38,12 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
RadioListTile<bool>( RadioListTile<bool>(
dense: true, dense: true,
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
activeColor: Theme.of(context).primaryColor, activeColor: PlatformTheme.of(context).primaryColor,
value: false, value: false,
groupValue: groupValue, groupValue: groupValue,
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
ref.read(replaceDownloadedFileState.state).state = value; ref.read(replaceDownloadedFileState.notifier).state = value;
} }
}, },
title: const Text("Skip downloading all downloaded tracks"), title: const Text("Skip downloading all downloaded tracks"),

View File

@ -1,7 +1,6 @@
import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart'; import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/platform.dart';
@ -78,7 +77,7 @@ class _PageWindowTitleBarState extends State<PageWindowTitleBar> {
return null; return null;
}, [platform, widget.hideWhenWindows]); }, [platform, widget.hideWhenWindows]);
var appBar = PlatformAppBar( final appBar = PlatformAppBar(
actions: [ actions: [
...?widget.actions, ...?widget.actions,
if (!kIsMacOS && !kIsMobile) if (!kIsMacOS && !kIsMobile)

View File

@ -83,12 +83,12 @@ BreakpointUtils useBreakpoints() {
width <= 1366 && width <= 1366 &&
breakpoint.value != Breakpoints.lg) { breakpoint.value != Breakpoints.lg) {
breakpoint.value = Breakpoints.lg; breakpoint.value = Breakpoints.lg;
} else if (width > 414 && } else if (width > 500 &&
width <= 800 && width <= 800 &&
breakpoint.value != Breakpoints.md) { breakpoint.value != Breakpoints.md) {
breakpoint.value = Breakpoints.md; breakpoint.value = Breakpoints.md;
} else if (width >= 250 && } else if (width >= 250 &&
width <= 414 && width <= 500 &&
breakpoint.value != Breakpoints.sm) { breakpoint.value != Breakpoints.sm) {
breakpoint.value = Breakpoints.sm; breakpoint.value = Breakpoints.sm;
} }

View File

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:audio_service/audio_service.dart'; import 'package:audio_service/audio_service.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:fl_query/fl_query.dart'; import 'package:fl_query/fl_query.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
@ -40,7 +41,7 @@ void main() async {
final savedSize = rawSize != null ? json.decode(rawSize) : null; final savedSize = rawSize != null ? json.decode(rawSize) : null;
final double? height = savedSize?["height"]; final double? height = savedSize?["height"];
final double? width = savedSize?["width"]; final double? width = savedSize?["width"];
appWindow.minSize = const Size(1020, 700); appWindow.minSize = const Size(kReleaseMode ? 1020 : 300, 700);
appWindow.alignment = Alignment.center; appWindow.alignment = Alignment.center;
appWindow.title = "Spotube"; appWindow.title = "Spotube";
if (height != null && width != null && height >= 700 && width >= 359) { if (height != null && width != null && height >= 700 && width >= 359) {

View File

@ -268,7 +268,7 @@ class ArtistPage extends HookConsumerWidget {
Container( Container(
margin: const EdgeInsets.symmetric(horizontal: 5), margin: const EdgeInsets.symmetric(horizontal: 5),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).primaryColor, color: PlatformTheme.of(context).primaryColor,
borderRadius: BorderRadius.circular(50), borderRadius: BorderRadius.circular(50),
), ),
child: PlatformIconButton( child: PlatformIconButton(

View File

@ -16,7 +16,10 @@ class DesktopLoginPage extends HookConsumerWidget {
return SafeArea( return SafeArea(
child: PlatformScaffold( child: PlatformScaffold(
appBar: PageWindowTitleBar(leading: const PlatformBackButton()), appBar: PageWindowTitleBar(
leading: const PlatformBackButton(),
hideWhenWindows: false,
),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Center( child: Center(
child: Container( child: Container(

View File

@ -18,6 +18,7 @@ class LoginTutorial extends ConsumerWidget {
return PlatformScaffold( return PlatformScaffold(
appBar: PageWindowTitleBar( appBar: PageWindowTitleBar(
hideWhenWindows: false,
leading: PlatformTextButton( leading: PlatformTextButton(
child: const PlatformText("Exit"), child: const PlatformText("Exit"),
onPressed: () { onPressed: () {

View File

@ -64,12 +64,13 @@ class PlayerView extends HookConsumerWidget {
return PlatformScaffold( return PlatformScaffold(
appBar: PageWindowTitleBar( appBar: PageWindowTitleBar(
hideWhenWindows: false,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
foregroundColor: paletteColor.titleTextColor, foregroundColor: paletteColor.titleTextColor,
toolbarOpacity: 0, toolbarOpacity: 0,
leading: PlatformBackButton( leading: PlatformBackButton(
color: PlatformProperty.only( color: PlatformProperty.only(
macos: Colors.transparent, macos: Colors.black,
other: paletteColor.titleTextColor, other: paletteColor.titleTextColor,
).resolve(platform!), ).resolve(platform!),
), ),
@ -88,7 +89,7 @@ class PlayerView extends HookConsumerWidget {
textStyle: PlatformTheme.of(context).textTheme!.body!, textStyle: PlatformTheme.of(context).textTheme!.body!,
color: paletteColor.color.withOpacity(.5), color: paletteColor.color.withOpacity(.5),
child: SafeArea( child: SafeArea(
child: Column( child: ListView(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),

View File

@ -4,11 +4,13 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.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/shared/dialogs/replace_downloaded_dialog.dart';
import 'package:spotube/components/root/bottom_player.dart'; import 'package:spotube/components/root/bottom_player.dart';
import 'package:spotube/components/root/sidebar.dart'; import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/components/root/spotube_navigation_bar.dart'; import 'package:spotube/components/root/spotube_navigation_bar.dart';
import 'package:spotube/components/shared/page_window_title_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/hooks/use_update_checker.dart';
import 'package:spotube/provider/downloader_provider.dart'; import 'package:spotube/provider/downloader_provider.dart';
@ -65,7 +67,7 @@ class RootApp extends HookConsumerWidget {
return PlatformScaffold( return PlatformScaffold(
appBar: platform == TargetPlatform.windows appBar: platform == TargetPlatform.windows
? PageWindowTitleBar(hideWhenWindows: false) ? PageWindowTitleBar(hideWhenWindows: false) as PreferredSizeWidget?
: null, : null,
body: Sidebar( body: Sidebar(
selectedIndex: index.value, selectedIndex: index.value,

View File

@ -302,11 +302,7 @@ class SearchPage extends HookConsumerWidget {
.error?[searchArtist.pageParams.last]), .error?[searchArtist.pageParams.last]),
const SizedBox(height: 20), const SizedBox(height: 20),
if (albums.isNotEmpty) if (albums.isNotEmpty)
PlatformText( PlatformText.subheading("Albums"),
"Albums",
style:
Theme.of(context).textTheme.headline5,
),
const SizedBox(height: 10), const SizedBox(height: 10),
ScrollConfiguration( ScrollConfiguration(
behavior: behavior:

View File

@ -71,7 +71,7 @@ class SettingsPage extends HookConsumerWidget {
AdaptiveListTile( AdaptiveListTile(
leading: Icon( leading: Icon(
Icons.login_rounded, Icons.login_rounded,
color: Theme.of(context).primaryColor, color: PlatformTheme.of(context).primaryColor,
), ),
title: SizedBox( title: SizedBox(
height: 50, height: 50,
@ -82,7 +82,7 @@ class SettingsPage extends HookConsumerWidget {
"Login with your Spotify account", "Login with your Spotify account",
maxLines: 1, maxLines: 1,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: PlatformTheme.of(context).primaryColor,
), ),
), ),
), ),

View File

@ -126,6 +126,9 @@ final windowsTheme = fluent_ui.ThemeData.light().copyWith(
iconSize: fluent_ui.ButtonState.all(20), 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( final windowsDarkTheme = fluent_ui.ThemeData.dark().copyWith(
buttonTheme: fluent_ui.ButtonThemeData( buttonTheme: fluent_ui.ButtonThemeData(
@ -133,6 +136,9 @@ final windowsDarkTheme = fluent_ui.ThemeData.dark().copyWith(
iconSize: fluent_ui.ButtonState.all(20), iconSize: fluent_ui.ButtonState.all(20),
), ),
), ),
navigationPaneTheme: fluent_ui.NavigationPaneThemeData(
backgroundColor: fluent_ui.Colors.grey[900].withOpacity(0.5),
),
); );
final macosTheme = MacosThemeData.light().copyWith( final macosTheme = MacosThemeData.light().copyWith(
pushButtonTheme: const PushButtonThemeData( pushButtonTheme: const PushButtonThemeData(

View File

@ -1051,8 +1051,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: fdff4771fed193aece0862c3216a8457410a856a ref: "7a4cda31e434de7cbee92ac7f418903641a2f023"
resolved-ref: fdff4771fed193aece0862c3216a8457410a856a resolved-ref: "7a4cda31e434de7cbee92ac7f418903641a2f023"
url: "https://github.com/KRTirtho/platform_ui.git" url: "https://github.com/KRTirtho/platform_ui.git"
source: git source: git
version: "0.1.0" version: "0.1.0"

View File

@ -65,7 +65,7 @@ dependencies:
platform_ui: platform_ui:
git: git:
url: https://github.com/KRTirtho/platform_ui.git url: https://github.com/KRTirtho/platform_ui.git
ref: fdff4771fed193aece0862c3216a8457410a856a ref: 7a4cda31e434de7cbee92ac7f418903641a2f023
fluent_ui: ^4.0.3 fluent_ui: ^4.0.3
macos_ui: ^1.7.5 macos_ui: ^1.7.5
libadwaita: ^1.2.5 libadwaita: ^1.2.5