diff --git a/lib/components/Home/Genres.dart b/lib/components/Home/Genres.dart index 4b85d4d9c..0f614f1b1 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 f012d9de3..1f09f57b3 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 bcec8e465..d8fde0267 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 3ef24250d..1d3afe4a0 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 c523e2468..3c21c581d 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 000000000..d074d574e --- /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 e4124d48e..cdc1260e0 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 114a99ca0..030fafd8b 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 280f5c49a..b9dfa5e2b 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 db52cc892..98cb2795b 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: