refactor: working dissect of platform_ui

This commit is contained in:
Kingkor Roy Tirtho 2023-03-09 23:24:09 +06:00
parent cfa9e04377
commit a4927c7013
67 changed files with 1145 additions and 1977 deletions

View File

@ -1,13 +1,13 @@
import 'package:fluent_ui/fluent_ui.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:flutter_feather_icons/flutter_feather_icons.dart';
abstract class SpotubeIcons { abstract class SpotubeIcons {
static const home = FluentIcons.home; static const home = FluentIcons.home_12_regular;
static const search = FeatherIcons.search; static const search = FeatherIcons.search;
static const library = FluentIcons.library; static const library = FluentIcons.library_16_regular;
static const music = FeatherIcons.music; static const music = FeatherIcons.music;
static const play = FluentIcons.play; static const play = FluentIcons.play_12_regular;
static const pause = FeatherIcons.pause; static const pause = FeatherIcons.pause;
static const skipForward = FeatherIcons.skipForward; static const skipForward = FeatherIcons.skipForward;
static const skipBack = FeatherIcons.skipBack; static const skipBack = FeatherIcons.skipBack;
@ -16,8 +16,8 @@ abstract class SpotubeIcons {
static const refresh = FeatherIcons.refreshCw; static const refresh = FeatherIcons.refreshCw;
static const settings = FeatherIcons.settings; static const settings = FeatherIcons.settings;
static const shuffle = FeatherIcons.shuffle; static const shuffle = FeatherIcons.shuffle;
static const repeat = FluentIcons.repeat_all; static const repeat = FluentIcons.arrow_repeat_all_16_regular;
static const repeatOne = FluentIcons.repeat_one; static const repeatOne = Icons.repeat_one_rounded;
static const remove = FeatherIcons.minus; static const remove = FeatherIcons.minus;
static const removeFilled = FeatherIcons.minusCircle; static const removeFilled = FeatherIcons.minusCircle;
static const add = FeatherIcons.plus; static const add = FeatherIcons.plus;

View File

@ -1,12 +1,10 @@
import 'package:auto_size_text/auto_size_text.dart'; 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/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/hover_builder.dart'; import 'package:spotube/components/shared/hover_builder.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/hooks/use_platform_property.dart';
import 'package:spotube/provider/blacklist_provider.dart'; import 'package:spotube/provider/blacklist_provider.dart';
import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/service_utils.dart';
import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart';
@ -30,74 +28,34 @@ class ArtistCard extends HookConsumerWidget {
), ),
), ),
); );
final boxShadow = usePlatformProperty<BoxShadow?>(
(context) => PlatformProperty(
android: BoxShadow(
blurRadius: 10,
offset: const Offset(0, 3),
spreadRadius: 5,
color: Theme.of(context).colorScheme.shadow,
),
ios: null,
macos: null,
linux: BoxShadow(
blurRadius: 6,
color: Theme.of(context).shadowColor.withOpacity(0.3),
),
windows: null,
),
);
final splash = usePlatformProperty<InteractiveInkFeatureFactory?>(
(context) => PlatformProperty.only(
android: InkRipple.splashFactory,
other: NoSplash.splashFactory,
),
);
return SizedBox( return SizedBox(
height: 240, height: 240,
width: 200, width: 200,
child: InkWell( child: InkWell(
splashFactory: splash,
onTap: () { onTap: () {
ServiceUtils.navigate(context, "/artist/${artist.id}"); ServiceUtils.navigate(context, "/artist/${artist.id}");
}, },
customBorder: platform == TargetPlatform.windows borderRadius: BorderRadius.circular(8),
? 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) { child: HoverBuilder(builder: (context, isHovering) {
return Ink( return Ink(
width: 200, width: 200,
decoration: BoxDecoration( decoration: BoxDecoration(
color: PlatformTheme.of(context).secondaryBackgroundColor, color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(8),
platform == TargetPlatform.windows ? 5 : 8,
),
boxShadow: [ boxShadow: [
if (boxShadow != null) boxShadow, BoxShadow(
blurRadius: 10,
offset: const Offset(0, 3),
spreadRadius: 5,
color: Theme.of(context).colorScheme.shadow,
),
], ],
border: isBlackListed border: isBlackListed
? Border.all( ? Border.all(
color: Colors.red[400]!, color: Colors.red[400]!,
width: 2, width: 2,
) )
: [TargetPlatform.windows, TargetPlatform.macOS]
.contains(platform)
? Border.all(
color: PlatformTheme.of(context).borderColor ??
Colors.transparent,
width: 1,
)
: null, : null,
), ),
child: Padding( child: Padding(
@ -138,7 +96,7 @@ class ArtistCard extends HookConsumerWidget {
artist.name!, artist.name!,
maxLines: 2, maxLines: 2,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: PlatformTextTheme.of(context).body?.copyWith( style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),

View File

@ -1,7 +1,7 @@
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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
class TokenLoginForm extends HookConsumerWidget { class TokenLoginForm extends HookConsumerWidget {
@ -25,27 +25,31 @@ class TokenLoginForm extends HookConsumerWidget {
), ),
child: Column( child: Column(
children: [ children: [
PlatformTextField( TextField(
controller: directCodeController, controller: directCodeController,
placeholder: "Spotify \"sp_dc\" Cookie", decoration: const InputDecoration(
label: "sp_dc Cookie", hintText: "Spotify \"sp_dc\" Cookie",
labelText: "sp_dc Cookie",
),
keyboardType: TextInputType.visiblePassword, keyboardType: TextInputType.visiblePassword,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
PlatformTextField( TextField(
controller: keyCodeController, controller: keyCodeController,
placeholder: "Spotify \"sp_key\" Cookie", decoration: const InputDecoration(
label: "sp_key Cookie", hintText: "Spotify \"sp_key\" Cookie",
labelText: "sp_key Cookie",
),
keyboardType: TextInputType.visiblePassword, keyboardType: TextInputType.visiblePassword,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
PlatformFilledButton( FilledButton(
onPressed: () async { onPressed: () async {
if (keyCodeController.text.isEmpty || if (keyCodeController.text.isEmpty ||
directCodeController.text.isEmpty) { directCodeController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: PlatformText("Please fill in all fields"), content: Text("Please fill in all fields"),
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
), ),
); );
@ -61,7 +65,7 @@ class TokenLoginForm extends HookConsumerWidget {
onDone?.call(); onDone?.call();
} }
}, },
child: const PlatformText("Submit"), child: const Text("Submit"),
) )
], ],
), ),

View File

@ -2,7 +2,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' hide Page; import 'package:flutter/material.dart' hide Page;
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart'; import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
import 'package:spotube/components/shared/waypoint.dart'; import 'package:spotube/components/shared/waypoint.dart';
@ -39,13 +39,15 @@ class CategoryCard extends HookConsumerWidget {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Row( child: Row(
children: [ children: [
PlatformText.headline(category.name ?? "Unknown"), Text(
category.name ?? "Unknown",
style: Theme.of(context).textTheme.titleLarge,
),
], ],
), ),
), ),
playlistQuery.hasPageError && !playlistQuery.hasPageData playlistQuery.hasPageError && !playlistQuery.hasPageData
? PlatformText( ? Text("Something Went Wrong\n${playlistQuery.errors.first}")
"Something Went Wrong\n${playlistQuery.errors.first}")
: SizedBox( : SizedBox(
height: 245, height: 245,
child: ScrollConfiguration( child: ScrollConfiguration(

View File

@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/album/album_card.dart'; import 'package:spotube/components/album/album_card.dart';
import 'package:spotube/components/shared/playbutton_card.dart'; import 'package:spotube/components/shared/playbutton_card.dart';
@ -65,16 +65,17 @@ class UserAlbums extends HookConsumerWidget {
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,
textStyle: PlatformTheme.of(context).textTheme!.body!, color: Theme.of(context).scaffoldBackgroundColor,
color: PlatformTheme.of(context).scaffoldBackgroundColor,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
children: [ children: [
PlatformTextField( TextField(
onChanged: (value) => searchText.value = value, onChanged: (value) => searchText.value = value,
prefixIcon: SpotubeIcons.filter, decoration: const InputDecoration(
placeholder: 'Filter Albums...', prefixIcon: Icon(SpotubeIcons.filter),
hintText: 'Filter albums...',
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Wrap( Wrap(

View File

@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart'; import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
@ -56,25 +56,27 @@ class UserArtists extends HookConsumerWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0), padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ColoredBox( child: ColoredBox(
color: PlatformTheme.of(context).scaffoldBackgroundColor!, color: Theme.of(context).scaffoldBackgroundColor,
child: PlatformTextField( child: TextField(
onChanged: (value) => searchText.value = value, onChanged: (value) => searchText.value = value,
prefixIcon: SpotubeIcons.filter, decoration: const InputDecoration(
placeholder: 'Filter artists...', prefixIcon: Icon(SpotubeIcons.filter),
hintText: 'Filter artists...',
), ),
), ),
), ),
), ),
backgroundColor: PlatformTheme.of(context).scaffoldBackgroundColor, ),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: artistQuery.pages.isEmpty body: artistQuery.pages.isEmpty
? Padding( ? Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: const [ children: const [
PlatformCircularProgressIndicator(), CircularProgressIndicator(),
SizedBox(width: 10), SizedBox(width: 10),
PlatformText("Loading..."), Text("Loading..."),
], ],
), ),
) )

View File

@ -1,7 +1,7 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/provider/downloader_provider.dart'; import 'package:spotube/provider/downloader_provider.dart';
@ -26,20 +26,19 @@ class UserDownloads extends HookConsumerWidget {
child: AutoSizeText( child: AutoSizeText(
"Currently downloading (${downloader.currentlyRunning})", "Currently downloading (${downloader.currentlyRunning})",
maxLines: 1, maxLines: 1,
style: PlatformTextTheme.of(context).headline, style: Theme.of(context).textTheme.headlineMedium,
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
PlatformFilledButton( FilledButton(
style: ButtonStyle( style: FilledButton.styleFrom(
backgroundColor: MaterialStatePropertyAll(Colors.red[50]), backgroundColor: Colors.red[50],
foregroundColor: MaterialStatePropertyAll(Colors.red[400]), foregroundColor: Colors.red[400],
), ),
onPressed: downloader.currentlyRunning > 0 onPressed: downloader.currentlyRunning > 0
? downloader.cancelAll ? downloader.cancelAll
: null, : null,
isSecondary: true, child: const Text("Cancel All"),
child: const PlatformText("Cancel All"),
), ),
], ],
), ),
@ -49,7 +48,7 @@ class UserDownloads extends HookConsumerWidget {
itemCount: downloader.inQueue.length, itemCount: downloader.inQueue.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final track = downloader.inQueue.elementAt(index); final track = downloader.inQueue.elementAt(index);
return PlatformListTile( return ListTile(
title: Text(track.name ?? ''), title: Text(track.name ?? ''),
leading: Padding( leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5), padding: const EdgeInsets.symmetric(horizontal: 5),
@ -68,7 +67,7 @@ class UserDownloads extends HookConsumerWidget {
trailing: const SizedBox( trailing: const SizedBox(
width: 30, width: 30,
height: 30, height: 30,
child: PlatformCircularProgressIndicator(), child: CircularProgressIndicator(),
), ),
subtitle: Text( subtitle: Text(
TypeConversionUtils.artists_X_String( TypeConversionUtils.artists_X_String(

View File

@ -12,7 +12,7 @@ import 'package:mime/mime.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/compact_search.dart'; import 'package:spotube/components/shared/compact_search.dart';
@ -189,7 +189,7 @@ class UserLocalTracks extends HookConsumerWidget {
child: Row( child: Row(
children: [ children: [
const SizedBox(width: 10), const SizedBox(width: 10),
PlatformFilledButton( FilledButton(
onPressed: trackSnapshot.value != null onPressed: trackSnapshot.value != null
? () { ? () {
if (trackSnapshot.value?.isNotEmpty == true) { if (trackSnapshot.value?.isNotEmpty == true) {
@ -221,7 +221,7 @@ class UserLocalTracks extends HookConsumerWidget {
}, },
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
PlatformFilledButton( FilledButton(
child: const Icon(SpotubeIcons.refresh), child: const Icon(SpotubeIcons.refresh),
onPressed: () { onPressed: () {
ref.refresh(localTracksProvider); ref.refresh(localTracksProvider);

View File

@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/playlist/playlist_create_dialog.dart'; import 'package:spotube/components/playlist/playlist_create_dialog.dart';
@ -94,15 +94,16 @@ class UserPlaylists extends HookConsumerWidget {
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,
textStyle: PlatformTheme.of(context).textTheme!.body!,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
children: [ children: [
PlatformTextField( TextField(
onChanged: (value) => searchText.value = value, onChanged: (value) => searchText.value = value,
placeholder: "Filter your playlists...", decoration: const InputDecoration(
prefixIcon: SpotubeIcons.filter, hintText: "Filter your playlists...",
prefixIcon: Icon(SpotubeIcons.filter),
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
if (playlistsQuery.isLoading || !playlistsQuery.hasData) if (playlistsQuery.isLoading || !playlistsQuery.hasData)

View File

@ -1,6 +1,6 @@
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:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
class ZoomControls extends HookWidget { class ZoomControls extends HookWidget {
@ -32,15 +32,15 @@ class ZoomControls extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final actions = [ final actions = [
PlatformIconButton( IconButton(
icon: decreaseIcon, icon: decreaseIcon,
onPressed: () { onPressed: () {
if (value == min) return; if (value == min) return;
onChanged(value - interval); onChanged(value - interval);
}, },
), ),
PlatformText("$value$unit"), Text("$value$unit"),
PlatformIconButton( IconButton(
icon: increaseIcon, icon: increaseIcon,
onPressed: () { onPressed: () {
if (value == max) return; if (value == max) return;
@ -51,9 +51,7 @@ class ZoomControls extends HookWidget {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: PlatformTheme.of(context) color: Theme.of(context).cardColor.withOpacity(0.7),
.secondaryBackgroundColor
?.withOpacity(0.7),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
constraints: BoxConstraints( constraints: BoxConstraints(

View File

@ -2,7 +2,7 @@ import 'package:flutter/foundation.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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/player/player_queue.dart'; import 'package:spotube/components/player/player_queue.dart';
@ -54,7 +54,7 @@ class PlayerActions extends HookConsumerWidget {
return Row( return Row(
mainAxisAlignment: mainAxisAlignment, mainAxisAlignment: mainAxisAlignment,
children: [ children: [
PlatformIconButton( IconButton(
icon: const Icon(SpotubeIcons.queue), icon: const Icon(SpotubeIcons.queue),
tooltip: 'Queue', tooltip: 'Queue',
onPressed: playlist != null onPressed: playlist != null
@ -80,7 +80,7 @@ class PlayerActions extends HookConsumerWidget {
: null, : null,
), ),
if (!isLocalTrack) if (!isLocalTrack)
PlatformIconButton( IconButton(
icon: const Icon(SpotubeIcons.alternativeRoute), icon: const Icon(SpotubeIcons.alternativeRoute),
tooltip: "Alternative Track Sources", tooltip: "Alternative Track Sources",
onPressed: playlist?.activeTrack != null onPressed: playlist?.activeTrack != null
@ -115,7 +115,7 @@ class PlayerActions extends HookConsumerWidget {
), ),
) )
else else
PlatformIconButton( IconButton(
tooltip: 'Download track', tooltip: 'Download track',
icon: Icon( icon: Icon(
isDownloaded ? SpotubeIcons.done : SpotubeIcons.download, isDownloaded ? SpotubeIcons.done : SpotubeIcons.download,

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/collections/intents.dart'; import 'package:spotube/collections/intents.dart';
import 'package:spotube/models/logger.dart'; import 'package:spotube/models/logger.dart';
@ -106,9 +106,9 @@ class PlayerControls extends HookConsumerWidget {
return Column( return Column(
children: [ children: [
PlatformTooltip( Tooltip(
message: "Slide to seek forward or backward", message: "Slide to seek forward or backward",
child: PlatformSlider( child: Slider(
// cannot divide by zero // cannot divide by zero
// there's an edge case for value being bigger // there's an edge case for value being bigger
// than total duration. Keeping it resolved // than total duration. Keeping it resolved
@ -135,10 +135,10 @@ class PlayerControls extends HookConsumerWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
PlatformText( Text(
"$currentMinutes:$currentSeconds", "$currentMinutes:$currentSeconds",
), ),
PlatformText("$totalMinutes:$totalSeconds"), Text("$totalMinutes:$totalSeconds"),
], ],
), ),
), ),
@ -149,14 +149,14 @@ class PlayerControls extends HookConsumerWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
PlatformIconButton( IconButton(
tooltip: playlist?.isShuffled == true tooltip: playlist?.isShuffled == true
? "Unshuffle playlist" ? "Unshuffle playlist"
: "Shuffle playlist", : "Shuffle playlist",
icon: Icon( icon: Icon(
SpotubeIcons.shuffle, SpotubeIcons.shuffle,
color: playlist?.isShuffled == true color: playlist?.isShuffled == true
? PlatformTheme.of(context).primaryColor ? Theme.of(context).primaryColor
: null, : null,
), ),
onPressed: playlist == null onPressed: playlist == null
@ -169,7 +169,7 @@ class PlayerControls extends HookConsumerWidget {
} }
}, },
), ),
PlatformIconButton( IconButton(
tooltip: "Previous track", tooltip: "Previous track",
icon: Icon( icon: Icon(
SpotubeIcons.skipBack, SpotubeIcons.skipBack,
@ -177,13 +177,13 @@ class PlayerControls extends HookConsumerWidget {
), ),
onPressed: playlistNotifier.previous, onPressed: playlistNotifier.previous,
), ),
PlatformIconButton( IconButton(
tooltip: playing ? "Pause playback" : "Resume playback", tooltip: playing ? "Pause playback" : "Resume playback",
icon: playlist?.isLoading == true icon: playlist?.isLoading == true
? const SizedBox( ? const SizedBox(
height: 20, height: 20,
width: 20, width: 20,
child: PlatformCircularProgressIndicator(), child: CircularProgressIndicator(),
) )
: Icon( : Icon(
playing ? SpotubeIcons.pause : SpotubeIcons.play, playing ? SpotubeIcons.pause : SpotubeIcons.play,
@ -194,7 +194,7 @@ class PlayerControls extends HookConsumerWidget {
PlayPauseIntent(ref), PlayPauseIntent(ref),
), ),
), ),
PlatformIconButton( IconButton(
tooltip: "Next track", tooltip: "Next track",
icon: Icon( icon: Icon(
SpotubeIcons.skipForward, SpotubeIcons.skipForward,
@ -202,7 +202,7 @@ class PlayerControls extends HookConsumerWidget {
), ),
onPressed: playlistNotifier.next, onPressed: playlistNotifier.next,
), ),
PlatformIconButton( IconButton(
tooltip: playlist?.isLooping != true tooltip: playlist?.isLooping != true
? "Loop Track" ? "Loop Track"
: "Repeat playlist", : "Repeat playlist",
@ -211,7 +211,7 @@ class PlayerControls extends HookConsumerWidget {
? SpotubeIcons.repeatOne ? SpotubeIcons.repeatOne
: SpotubeIcons.repeat, : SpotubeIcons.repeat,
color: playlist?.isLooping == true color: playlist?.isLooping == true
? PlatformTheme.of(context).primaryColor ? Theme.of(context).primaryColor
: null, : null,
), ),
onPressed: playlist == null || playlist.isLoading onPressed: playlist == null || playlist.isLoading

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/player/player_track_details.dart'; import 'package:spotube/components/player/player_track_details.dart';
import 'package:spotube/hooks/use_palette_color.dart'; import 'package:spotube/hooks/use_palette_color.dart';
@ -59,7 +59,6 @@ class PlayerOverlay extends HookConsumerWidget {
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
opacity: canShow ? 1 : 0, opacity: canShow ? 1 : 0,
child: Material( child: Material(
textStyle: PlatformTheme.of(context).textTheme!.body!,
type: MaterialType.transparency, type: MaterialType.transparency,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@ -3,7 +3,7 @@ import 'dart:ui';
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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/fallbacks/not_found.dart'; import 'package:spotube/components/shared/fallbacks/not_found.dart';
@ -36,8 +36,7 @@ class PlayerQueue extends HookConsumerWidget {
topLeft: Radius.circular(10), topLeft: Radius.circular(10),
topRight: Radius.circular(10), topRight: Radius.circular(10),
); );
final headlineColor = final headlineColor = Theme.of(context).textTheme.headlineSmall?.color;
PlatformTheme.of(context).textTheme?.subheading?.color;
useEffect(() { useEffect(() {
if (playlist == null) return null; if (playlist == null) return null;
@ -61,9 +60,7 @@ class PlayerQueue extends HookConsumerWidget {
top: 5.0, top: 5.0,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: PlatformTheme.of(context) color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5),
.scaffoldBackgroundColor
?.withOpacity(0.5),
borderRadius: borderRadius, borderRadius: borderRadius,
), ),
child: Column( child: Column(
@ -80,7 +77,7 @@ class PlayerQueue extends HookConsumerWidget {
Row( Row(
children: [ children: [
const SizedBox(width: 10), const SizedBox(width: 10),
PlatformText( Text(
"${tracks.length} tracks in Queue", "${tracks.length} tracks in Queue",
style: TextStyle( style: TextStyle(
color: headlineColor, color: headlineColor,
@ -89,14 +86,13 @@ class PlayerQueue extends HookConsumerWidget {
), ),
), ),
const Spacer(), const Spacer(),
PlatformFilledButton( FilledButton(
style: ButtonStyle( style: FilledButton.styleFrom(
backgroundColor: MaterialStatePropertyAll( backgroundColor: Theme.of(context)
PlatformTheme.of(context)
.scaffoldBackgroundColor .scaffoldBackgroundColor
?.withOpacity(0.5)), .withOpacity(0.5),
foregroundColor: MaterialStatePropertyAll( foregroundColor:
PlatformTheme.of(context).textTheme?.subheading?.color), Theme.of(context).textTheme.headlineSmall?.color,
), ),
child: Row( child: Row(
children: const [ children: const [

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_breakpoints.dart';
@ -37,7 +37,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
), ),
if (breakpoint.isLessThanOrEqualTo(Breakpoints.md)) if (breakpoint.isLessThanOrEqualTo(Breakpoints.md))
Flexible( Flexible(
child: PlatformText( child: Text(
playback?.activeTrack.name ?? "Not playing", playback?.activeTrack.name ?? "Not playing",
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold, color: color), style: TextStyle(fontWeight: FontWeight.bold, color: color),
@ -50,7 +50,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
flex: 1, flex: 1,
child: Column( child: Column(
children: [ children: [
PlatformText( Text(
playback?.activeTrack.name ?? "Not playing", playback?.activeTrack.name ?? "Not playing",
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold, color: color), style: TextStyle(fontWeight: FontWeight.bold, color: color),

View File

@ -3,7 +3,7 @@ import 'dart:ui';
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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/models/spotube_track.dart'; import 'package:spotube/models/spotube_track.dart';
import 'package:spotube/provider/playlist_queue_provider.dart'; import 'package:spotube/provider/playlist_queue_provider.dart';
@ -50,16 +50,15 @@ class SiblingTracksSheet extends HookConsumerWidget {
margin: const EdgeInsets.all(8.0), margin: const EdgeInsets.all(8.0),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: borderRadius, borderRadius: borderRadius,
color: PlatformTheme.of(context) color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.3),
.scaffoldBackgroundColor!
.withOpacity(.3),
), ),
child: Scaffold( child: Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
appBar: PlatformAppBar( appBar: AppBar(
centerTitle: true, centerTitle: true,
title: PlatformText.subheading( title: Text(
'Alternative Tracks Sources', 'Alternative Tracks Sources',
style: Theme.of(context).textTheme.headlineSmall,
), ),
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -71,8 +70,8 @@ class SiblingTracksSheet extends HookConsumerWidget {
itemCount: siblings.length, itemCount: siblings.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final video = siblings[index]; final video = siblings[index];
return PlatformListTile( return ListTile(
title: PlatformText(video.title), title: Text(video.title),
leading: Padding( leading: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: UniversalImage( child: UniversalImage(
@ -84,12 +83,12 @@ class SiblingTracksSheet extends HookConsumerWidget {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
), ),
trailing: PlatformText( trailing: Text(
PrimitiveUtils.toReadableDuration( PrimitiveUtils.toReadableDuration(
video.duration ?? Duration.zero, video.duration ?? Duration.zero,
), ),
), ),
subtitle: PlatformText(video.author), subtitle: Text(video.author),
enabled: playlist?.isLoading != true, enabled: playlist?.isLoading != true,
selected: playlist?.isLoading != true && selected: playlist?.isLoading != true &&
video.id.value == video.id.value ==

View File

@ -1,11 +1,9 @@
import 'package:fl_query_hooks/fl_query_hooks.dart'; import 'package:fl_query_hooks/fl_query_hooks.dart';
import 'package:flutter/cupertino.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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/provider/spotify_provider.dart'; import 'package:spotube/provider/spotify_provider.dart';
class PlaylistCreateDialog extends HookConsumerWidget { class PlaylistCreateDialog extends HookConsumerWidget {
@ -17,10 +15,10 @@ class PlaylistCreateDialog extends HookConsumerWidget {
return SizedBox( return SizedBox(
width: 200, width: 200,
child: PlatformTextButton( child: TextButton(
onPressed: () { onPressed: () {
showPlatformAlertDialog( showDialog(
context, context: context,
builder: (context) { builder: (context) {
return HookBuilder(builder: (context) { return HookBuilder(builder: (context) {
final playlistName = useTextEditingController(); final playlistName = useTextEditingController();
@ -48,48 +46,18 @@ class PlaylistCreateDialog extends HookConsumerWidget {
navigator.pop(); navigator.pop();
} }
return PlatformAlertDialog( return AlertDialog(
macosAppIcon: Sidebar.brandLogo(), title: const Text("Create a Playlist"),
title: const PlatformText("Create a Playlist"), actions: [
primaryActions: [ OutlinedButton(
PlatformBuilder( child: const Text("Cancel"),
fallback: PlatformBuilderFallback.android, onPressed: () {
android: (context, _) { Navigator.pop(context);
return PlatformFilledButton(
onPressed: onCreate,
child: const Text("Create"),
);
},
ios: (context, data) {
return CupertinoDialogAction(
isDefaultAction: true,
onPressed: onCreate,
child: const Text("Create"),
);
}, },
), ),
], FilledButton(
secondaryActions: [ onPressed: onCreate,
PlatformBuilder( child: const Text("Create"),
fallback: PlatformBuilderFallback.android,
android: (context, _) {
return PlatformFilledButton(
isSecondary: true,
child: const Text("Cancel"),
onPressed: () {
Navigator.pop(context);
},
);
},
ios: (context, data) {
return CupertinoDialogAction(
onPressed: () {
Navigator.pop(context);
},
isDestructiveAction: true,
child: const Text("Cancel"),
);
},
), ),
], ],
content: Container( content: Container(
@ -98,28 +66,32 @@ class PlaylistCreateDialog extends HookConsumerWidget {
child: ListView( child: ListView(
shrinkWrap: true, shrinkWrap: true,
children: [ children: [
PlatformTextField( TextField(
controller: playlistName, controller: playlistName,
placeholder: "Name of the playlist", decoration: const InputDecoration(
label: "Playlist Name", hintText: "Name of the playlist",
labelText: "Playlist Name",
),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
PlatformTextField( TextField(
controller: description, controller: description,
placeholder: "Description...", decoration: const InputDecoration(
hintText: "Description...",
),
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
maxLines: 5, maxLines: 5,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
PlatformCheckbox( CheckboxListTile(
title: const Text("Public"),
value: public.value, value: public.value,
label: const PlatformText("Public"),
onChanged: (val) => public.value = val ?? false, onChanged: (val) => public.value = val ?? false,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
PlatformCheckbox( CheckboxListTile(
title: const Text("Collaborative"),
value: collaborative.value, value: collaborative.value,
label: const PlatformText("Collaborative"),
onChanged: (val) => onChanged: (val) =>
collaborative.value = val ?? false, collaborative.value = val ?? false,
), ),
@ -141,7 +113,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: const [ children: const [
Icon(SpotubeIcons.addFilled, size: 40), Icon(SpotubeIcons.addFilled, size: 40),
PlatformText("Create Playlist", style: TextStyle(fontSize: 20)), Text("Create Playlist", style: TextStyle(fontSize: 20)),
], ],
), ),
), ),

View File

@ -1,17 +1,13 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:macos_ui/macos_ui.dart';
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/player/player_actions.dart'; import 'package:spotube/components/player/player_actions.dart';
import 'package:spotube/components/player/player_overlay.dart'; import 'package:spotube/components/player/player_overlay.dart';
import 'package:spotube/components/player/player_track_details.dart'; import 'package:spotube/components/player/player_track_details.dart';
import 'package:spotube/components/player/player_controls.dart'; import 'package:spotube/components/player/player_controls.dart';
import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/hooks/use_platform_property.dart';
import 'package:spotube/models/logger.dart'; import 'package:spotube/models/logger.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:spotube/provider/playlist_queue_provider.dart'; import 'package:spotube/provider/playlist_queue_provider.dart';
@ -52,51 +48,13 @@ class BottomPlayer extends HookConsumerWidget {
); );
} }
final backgroundColor = usePlatformProperty<Color?>(
(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: fluent_ui.FluentTheme.maybeOf(context)?.micaBackgroundColor,
),
);
final border = usePlatformProperty<BoxBorder?>(
(context) => PlatformProperty(
android: null,
ios: Border(
top: BorderSide(
color: PlatformTheme.of(context).borderColor ?? Colors.transparent,
width: 1,
),
),
macos: Border(
top: BorderSide(
color: PlatformTheme.of(context).borderColor ?? Colors.transparent,
width: 1,
),
),
linux: Border(
top: BorderSide(
color: PlatformTheme.of(context).borderColor ?? Colors.transparent,
width: 1,
),
),
windows: null,
),
);
return DecoratedBox( return DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: backgroundColor, color: Theme.of(context).colorScheme.background,
border: border,
), ),
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,
textStyle: PlatformTheme.of(context).textTheme!.body!, textStyle: Theme.of(context).textTheme.bodyMedium!,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
@ -141,7 +99,7 @@ class BottomPlayer extends HookConsumerWidget {
} }
} }
}, },
child: PlatformSlider( child: Slider(
min: 0, min: 0,
max: 1, max: 1,
value: volume.value, value: volume.value,

View File

@ -1,9 +1,8 @@
import 'package:badges/badges.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter/material.dart' hide Badge; import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/side_bar_tiles.dart'; import 'package:spotube/collections/side_bar_tiles.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
@ -16,7 +15,6 @@ 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/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;
class Sidebar extends HookConsumerWidget { class Sidebar extends HookConsumerWidget {
final int selectedIndex; final int selectedIndex;
@ -57,32 +55,23 @@ class Sidebar extends HookConsumerWidget {
final layoutMode = final layoutMode =
ref.watch(userPreferencesProvider.select((s) => s.layoutMode)); ref.watch(userPreferencesProvider.select((s) => s.layoutMode));
if (breakpoints.isMd) { if (layoutMode == LayoutMode.compact ||
(breakpoints.isSm && layoutMode == LayoutMode.adaptive)) {
return Scaffold(body: child);
}
return Row( return Row(
children: [ children: [
NavigationRail( NavigationRail(
selectedIndex: selectedIndex, selectedIndex: selectedIndex,
onDestinationSelected: onSelectedIndexChanged, onDestinationSelected: onSelectedIndexChanged,
labelType: NavigationRailLabelType.all, destinations: sidebarTileList.map(
extended: false, (e) {
backgroundColor: PlatformTheme.of(context).scaffoldBackgroundColor, return NavigationRailDestination(
leading: Column(
children: [
if (kIsMacOS) macSpacer,
brandLogo(),
],
),
trailing: PlatformIconButton(
icon: const Icon(fluent_ui.FluentIcons.settings),
onPressed: () => goToSettings(context),
),
destinations: [
for (final e in sidebarTileList)
NavigationRailDestination(
icon: Badge( icon: Badge(
badgeColor: PlatformTheme.of(context).primaryColor!, backgroundColor: Theme.of(context).primaryColor,
showBadge: e.title == "Library" && downloadCount > 0, isLabelVisible: e.title == "Library" && downloadCount > 0,
badgeContent: Text( label: Text(
downloadCount.toString(), downloadCount.toString(),
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
@ -91,71 +80,18 @@ class Sidebar extends HookConsumerWidget {
), ),
child: Icon(e.icon), child: Icon(e.icon),
), ),
label: PlatformText.label( label: Text(
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);
}
return SafeArea(
top: false,
child: PlatformSidebar(
currentIndex: selectedIndex,
onIndexChanged: onSelectedIndexChanged,
body: Map.fromEntries(
sidebarTileList.map(
(e) {
final icon = Icon(e.icon);
return MapEntry(
PlatformSidebarItem(
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,
),
title: Text(
e.title, e.title,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 16, fontSize: 16,
), ),
), ),
),
child,
); );
}, },
), ).toList(),
), extended: true,
expanded: true, leading: Padding(
header: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
children: [ children: [
@ -164,20 +100,19 @@ class Sidebar extends HookConsumerWidget {
children: [ children: [
brandLogo(), brandLogo(),
const SizedBox(width: 10), const SizedBox(width: 10),
PlatformText.headline("Spotube"), Text(
"Spotube",
style: Theme.of(context).textTheme.titleLarge,
),
], ],
), ),
], ],
), ),
), ),
windowsFooterItems: [ trailing: const SidebarFooter(),
fluent_ui.PaneItemAction(
icon: const Icon(SpotubeIcons.settings),
onTap: () => goToSettings(context),
), ),
Expanded(child: child)
], ],
footer: const SidebarFooter(),
),
); );
} }
} }
@ -211,7 +146,7 @@ class SidebarFooter extends HookConsumerWidget {
children: [ children: [
if (auth != null && data == null) if (auth != null && data == null)
const Center( const Center(
child: PlatformCircularProgressIndicator(), child: CircularProgressIndicator(),
) )
else if (data != null) else if (data != null)
Flexible( Flexible(
@ -236,16 +171,16 @@ class SidebarFooter extends HookConsumerWidget {
maxLines: 1, maxLines: 1,
softWrap: false, softWrap: false,
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
style: PlatformTheme.of(context) style: Theme.of(context)
.textTheme .textTheme
?.body .bodyMedium
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
), ),
), ),
], ],
), ),
), ),
PlatformIconButton( IconButton(
icon: const Icon(SpotubeIcons.settings), icon: const Icon(SpotubeIcons.settings),
onPressed: () => Sidebar.goToSettings(context)), onPressed: () => Sidebar.goToSettings(context)),
], ],

View File

@ -1,7 +1,7 @@
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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/side_bar_tiles.dart'; import 'package:spotube/collections/side_bar_tiles.dart';
import 'package:spotube/components/root/sidebar.dart'; import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_breakpoints.dart';
@ -37,19 +37,30 @@ class SpotubeNavigationBar extends HookConsumerWidget {
if (layoutMode == LayoutMode.extended || if (layoutMode == LayoutMode.extended ||
(breakpoint.isMoreThan(Breakpoints.sm) && (breakpoint.isMoreThan(Breakpoints.sm) &&
layoutMode == LayoutMode.adaptive)) return const SizedBox(); layoutMode == LayoutMode.adaptive)) return const SizedBox();
return PlatformBottomNavigationBar( return BottomNavigationBar(
items: [ items: [
...navbarTileList.map( ...navbarTileList.map(
(e) { (e) {
return PlatformBottomNavigationBarItem( return BottomNavigationBarItem(
icon: e.icon, icon: Badge(
backgroundColor: Theme.of(context).primaryColor,
isLabelVisible: e.title == "Library" && downloadCount > 0,
label: Text(
downloadCount.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 10,
),
),
child: Icon(e.icon),
),
label: e.title, label: e.title,
); );
}, },
), ),
], ],
selectedIndex: insideSelectedIndex.value, currentIndex: insideSelectedIndex.value,
onSelectedIndexChanged: (i) { onTap: (i) {
insideSelectedIndex.value = i; insideSelectedIndex.value = i;
if (navbarTileList[i].title == "Settings") { if (navbarTileList[i].title == "Settings") {
Sidebar.goToSettings(context); Sidebar.goToSettings(context);

View File

@ -1,9 +1,7 @@
import 'package:flutter/cupertino.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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/provider/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences_provider.dart';
final colorsMap = { final colorsMap = {
@ -59,48 +57,18 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
Navigator.pop(context); Navigator.pop(context);
} }
return PlatformAlertDialog( return AlertDialog(
macosAppIcon: Sidebar.brandLogo(),
title: Text("Pick ${schemeType.name} color scheme"), title: Text("Pick ${schemeType.name} color scheme"),
primaryActions: [ actions: [
PlatformBuilder( OutlinedButton(
android: (context, data) { child: const Text("Cancel"),
return PlatformFilledButton( onPressed: () {
onPressed: onOk, Navigator.pop(context);
child: const Text("Save"),
);
}, },
ios: (context, data) {
return CupertinoDialogAction(
onPressed: onOk,
isDefaultAction: true,
child: const Text("Save"),
);
},
fallback: PlatformBuilderFallback.android,
), ),
], FilledButton(
secondaryActions: [ onPressed: onOk,
PlatformBuilder( child: const Text("Save"),
fallback: PlatformBuilderFallback.android,
android: (context, _) {
return PlatformFilledButton(
isSecondary: true,
child: const Text("Cancel"),
onPressed: () {
Navigator.pop(context);
},
);
},
ios: (context, data) {
return CupertinoDialogAction(
onPressed: () {
Navigator.pop(context);
},
isDestructiveAction: true,
child: const Text("Cancel"),
);
},
), ),
], ],
content: SizedBox( content: SizedBox(

View File

@ -1,7 +1,6 @@
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:platform_ui/platform_ui.dart';
import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_breakpoints.dart';
class AdaptiveListTile extends HookWidget { class AdaptiveListTile extends HookWidget {
@ -26,7 +25,7 @@ class AdaptiveListTile extends HookWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final breakpoint = useBreakpoints(); final breakpoint = useBreakpoints();
return PlatformListTile( return ListTile(
title: title, title: title,
subtitle: subtitle, subtitle: subtitle,
trailing: trailing:
@ -35,13 +34,12 @@ class AdaptiveListTile extends HookWidget {
onTap: breakpoint.isLessThan(breakOn) onTap: breakpoint.isLessThan(breakOn)
? () { ? () {
onTap?.call(); onTap?.call();
showPlatformAlertDialog( showDialog(
context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (context) { builder: (context) {
return StatefulBuilder(builder: (context, update) { return StatefulBuilder(builder: (context, update) {
return PlatformAlertDialog( return AlertDialog(
macosAppIcon: Sidebar.brandLogo(),
title: title != null title: title != null
? Row( ? Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,

View File

@ -1,6 +1,6 @@
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:platform_ui/platform_ui.dart';
import 'package:popover/popover.dart'; import 'package:popover/popover.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_breakpoints.dart';
@ -23,12 +23,14 @@ class Action extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (isExpanded != true) { if (isExpanded != true) {
return PlatformIconButton( return IconButton(
icon: icon, icon: icon,
onPressed: onPressed, onPressed: onPressed,
style: IconButton.styleFrom(
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
tooltip: text is PlatformText ),
? (text as PlatformText).data tooltip: text is Text
? (text as Text).data
: text.toStringShallow().split(",").last.replaceAll( : text.toStringShallow().split(",").last.replaceAll(
"\"", "\"",
"", "",
@ -36,7 +38,7 @@ class Action extends StatelessWidget {
); );
} }
return PlatformListTile( return ListTile(
tileColor: backgroundColor, tileColor: backgroundColor,
onTap: onPressed, onTap: onPressed,
leading: icon, leading: icon,
@ -59,7 +61,7 @@ class AdaptiveActions extends HookWidget {
final breakpoint = useBreakpoints(); final breakpoint = useBreakpoints();
if (breakpoint.isLessThan(breakOn)) { if (breakpoint.isLessThan(breakOn)) {
return PlatformIconButton( return IconButton(
icon: const Icon(SpotubeIcons.moreHorizontal), icon: const Icon(SpotubeIcons.moreHorizontal),
onPressed: () { onPressed: () {
showPopover( showPopover(
@ -83,8 +85,7 @@ class AdaptiveActions extends HookWidget {
.toList(), .toList(),
); );
}, },
backgroundColor: backgroundColor: Theme.of(context).cardColor,
PlatformTheme.of(context).secondaryBackgroundColor!,
); );
}, },
); );

View File

@ -1,6 +1,6 @@
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:platform_ui/platform_ui.dart';
import 'package:popover/popover.dart'; import 'package:popover/popover.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
@ -20,11 +20,11 @@ class CompactSearch extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PlatformIconButton( return IconButton(
onPressed: () { onPressed: () {
showPopover( showPopover(
context: context, context: context,
backgroundColor: PlatformTheme.of(context).secondaryBackgroundColor!, backgroundColor: Theme.of(context).cardColor,
transitionDuration: const Duration(milliseconds: 100), transitionDuration: const Duration(milliseconds: 100),
barrierColor: Colors.transparent, barrierColor: Colors.transparent,
arrowDxOffset: -6, arrowDxOffset: -6,
@ -32,14 +32,13 @@ class CompactSearch extends HookWidget {
return Container( return Container(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
width: 300, width: 300,
child: PlatformTextField( child: TextField(
autofocus: true, autofocus: true,
onChanged: onChanged, onChanged: onChanged,
placeholder: placeholder, decoration: InputDecoration(
prefixIcon: icon, hintText: placeholder,
padding: platform == TargetPlatform.android prefixIcon: Icon(icon),
? const EdgeInsets.all(0) ),
: null,
), ),
); );
}, },

View File

@ -1,16 +1,13 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/root/sidebar.dart';
class ConfirmDownloadDialog extends StatelessWidget { class ConfirmDownloadDialog extends StatelessWidget {
const ConfirmDownloadDialog({Key? key}) : super(key: key); const ConfirmDownloadDialog({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PlatformAlertDialog( return AlertDialog(
macosAppIcon: Sidebar.brandLogo(),
title: Padding( title: Padding(
padding: const EdgeInsets.all(15), padding: const EdgeInsets.all(15),
child: Row( child: Row(
@ -62,47 +59,20 @@ class ConfirmDownloadDialog extends StatelessWidget {
), ),
), ),
), ),
primaryActions: [ actions: [
PlatformBuilder( OutlinedButton(
android: (context, _) { child: const Text("Decline"),
return PlatformFilledButton( onPressed: () {
style: const ButtonStyle( Navigator.pop(context, false);
foregroundColor: MaterialStatePropertyAll(Colors.white), },
backgroundColor: MaterialStatePropertyAll(Colors.red), ),
FilledButton(
style: FilledButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.red,
), ),
onPressed: () => Navigator.of(context).pop(true), onPressed: () => Navigator.of(context).pop(true),
child: const Text("Accept"), child: const Text("Accept"),
);
},
ios: (context, data) {
return CupertinoDialogAction(
onPressed: () => Navigator.of(context).pop(true),
isDestructiveAction: true,
child: const Text("Accept"),
);
},
)
],
secondaryActions: [
PlatformBuilder(
fallback: PlatformBuilderFallback.android,
android: (context, _) {
return PlatformFilledButton(
child: const Text("Decline"),
onPressed: () {
Navigator.pop(context, false);
},
);
},
ios: (context, data) {
return CupertinoDialogAction(
onPressed: () {
Navigator.pop(context, false);
},
isDefaultAction: true,
child: const Text("Decline"),
);
},
), ),
], ],
); );

View File

@ -1,7 +1,7 @@
import 'package:flutter/cupertino.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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/provider/spotify_provider.dart'; import 'package:spotube/provider/spotify_provider.dart';
import 'package:spotube/services/queries/queries.dart'; import 'package:spotube/services/queries/queries.dart';
@ -42,61 +42,32 @@ class PlaylistAddTrackDialog extends HookConsumerWidget {
).then((_) => Navigator.pop(context)); ).then((_) => Navigator.pop(context));
} }
return PlatformAlertDialog( return AlertDialog(
title: const PlatformText("Add to Playlist"), title: const Text("Add to Playlist"),
secondaryActions: [ actions: [
PlatformBuilder( OutlinedButton(
fallback: PlatformBuilderFallback.android,
android: (context, _) {
return PlatformFilledButton(
isSecondary: true,
child: const Text("Cancel"), child: const Text("Cancel"),
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
}, },
);
},
ios: (context, data) {
return CupertinoDialogAction(
onPressed: () {
Navigator.pop(context);
},
isDestructiveAction: true,
child: const Text("Cancel"),
);
},
), ),
], FilledButton(
primaryActions: [
PlatformBuilder(
fallback: PlatformBuilderFallback.android,
android: (context, _) {
return PlatformFilledButton(
onPressed: onAdd, onPressed: onAdd,
child: const Text("Add"), child: const Text("Add"),
);
},
ios: (context, data) {
return CupertinoDialogAction(
isDefaultAction: true,
onPressed: onAdd,
child: const Text("Add"),
);
},
), ),
], ],
content: SizedBox( content: SizedBox(
height: 300, height: 300,
width: 300, width: 300,
child: !userPlaylists.hasData child: !userPlaylists.hasData
? const Center(child: PlatformCircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: ListView.builder( : ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: filteredPlaylists!.length, itemCount: filteredPlaylists!.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final playlist = filteredPlaylists.elementAt(index); final playlist = filteredPlaylists.elementAt(index);
return PlatformCheckbox( return CheckboxListTile(
label: PlatformText(playlist.name!), title: Text(playlist.name!),
value: playlistsCheck.value[playlist.id] ?? false, value: playlistsCheck.value[playlist.id] ?? false,
onChanged: (val) { onChanged: (val) {
playlistsCheck.value = { playlistsCheck.value = {

View File

@ -1,6 +1,4 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/components/root/sidebar.dart';
Future<bool> showPromptDialog({ Future<bool> showPromptDialog({
required BuildContext context, required BuildContext context,
@ -9,39 +7,20 @@ Future<bool> showPromptDialog({
String okText = "Ok", String okText = "Ok",
String cancelText = "Cancel", String cancelText = "Cancel",
}) async { }) async {
return showPlatformAlertDialog<bool>( return showDialog<bool>(
context, context: context,
builder: (context) { builder: (context) {
return PlatformAlertDialog( return AlertDialog(
title: PlatformText(title), title: Text(title),
content: PlatformText(message), content: Text(message),
macosAppIcon: Sidebar.brandLogo(), actions: [
primaryActions: [ OutlinedButton(
if (platform == TargetPlatform.iOS) onPressed: () => Navigator.of(context).pop(false),
CupertinoDialogAction( child: Text(cancelText),
isDefaultAction: false,
isDestructiveAction: true,
child: PlatformText(okText),
onPressed: () => Navigator.of(context).pop(true),
)
else
PlatformFilledButton(
child: PlatformText(okText),
onPressed: () => Navigator.of(context).pop(true),
), ),
], FilledButton(
secondaryActions: [ child: Text(okText),
if (platform == TargetPlatform.iOS) onPressed: () => Navigator.of(context).pop(true),
CupertinoDialogAction(
isDefaultAction: true,
child: PlatformText(cancelText),
onPressed: () => Navigator.of(context).pop(false),
)
else
PlatformFilledButton(
isSecondary: true,
onPressed: () => Navigator.of(context).pop(false),
child: PlatformText(cancelText),
), ),
], ],
); );

View File

@ -1,9 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/root/sidebar.dart';
final replaceDownloadedFileState = StateProvider<bool?>((ref) => null); final replaceDownloadedFileState = StateProvider<bool?>((ref) => null);
@ -16,8 +14,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final groupValue = ref.watch(replaceDownloadedFileState); final groupValue = ref.watch(replaceDownloadedFileState);
return PlatformAlertDialog( return AlertDialog(
macosAppIcon: Sidebar.brandLogo(),
title: Text("Track ${track.name} Already Exists"), title: Text("Track ${track.name} Already Exists"),
content: Column( content: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -26,7 +23,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
RadioListTile<bool>( RadioListTile<bool>(
dense: true, dense: true,
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
activeColor: PlatformTheme.of(context).primaryColor, activeColor: Theme.of(context).primaryColor,
value: true, value: true,
groupValue: groupValue, groupValue: groupValue,
onChanged: (value) { onChanged: (value) {
@ -39,7 +36,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
RadioListTile<bool>( RadioListTile<bool>(
dense: true, dense: true,
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
activeColor: PlatformTheme.of(context).primaryColor, activeColor: Theme.of(context).primaryColor,
value: false, value: false,
groupValue: groupValue, groupValue: groupValue,
onChanged: (value) { onChanged: (value) {
@ -51,48 +48,17 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
), ),
], ],
), ),
primaryActions: [ actions: [
PlatformBuilder( OutlinedButton(
fallback: PlatformBuilderFallback.android, child: const Text("No"),
android: (context, _) {
return PlatformFilledButton(
child: const Text("Yes"),
onPressed: () { onPressed: () {
Navigator.pop(context, true); Navigator.pop(context, false);
},
);
},
ios: (context, data) {
return CupertinoDialogAction(
onPressed: () {
Navigator.pop(context, true);
},
isDefaultAction: true,
child: const Text("Yes"),
);
}, },
), ),
], FilledButton(
secondaryActions: [ child: const Text("Yes"),
PlatformBuilder(
fallback: PlatformBuilderFallback.android,
android: (context, _) {
return PlatformFilledButton(
isSecondary: true,
child: const Text("No"),
onPressed: () { onPressed: () {
Navigator.pop(context, false); Navigator.pop(context, true);
},
);
},
ios: (context, data) {
return CupertinoDialogAction(
onPressed: () {
Navigator.pop(context, false);
},
isDestructiveAction: true,
child: const Text("No"),
);
}, },
), ),
], ],

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/service_utils.dart';
@ -20,10 +20,10 @@ class AnonymousFallback extends ConsumerWidget {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const PlatformText("You're not logged in"), const Text("You're not logged in"),
const SizedBox(height: 10), const SizedBox(height: 10),
PlatformFilledButton( FilledButton(
child: const PlatformText("Login with Spotify"), child: const Text("Login with Spotify"),
onPressed: () => ServiceUtils.navigate(context, "/settings"), onPressed: () => ServiceUtils.navigate(context, "/settings"),
) )
], ],

View File

@ -2,7 +2,7 @@ import 'package:fl_query/fl_query.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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/hooks/use_palette_color.dart'; import 'package:spotube/hooks/use_palette_color.dart';
@ -34,7 +34,7 @@ class HeartButton extends ConsumerWidget {
if (auth == null) return Container(); if (auth == null) return Container();
return PlatformIconButton( return IconButton(
tooltip: tooltip, tooltip: tooltip,
icon: Icon( icon: Icon(
icon ?? (!isLiked ? SpotubeIcons.heart : SpotubeIcons.heartFilled), icon ?? (!isLiked ? SpotubeIcons.heart : SpotubeIcons.heartFilled),
@ -95,7 +95,7 @@ class TrackHeartButton extends HookConsumerWidget {
useQueries.playlist.tracksOfQuery(ref, "user-liked-tracks"); useQueries.playlist.tracksOfQuery(ref, "user-liked-tracks");
final toggler = useTrackToggleLike(track, ref); final toggler = useTrackToggleLike(track, ref);
if (toggler.item3.isLoading || !toggler.item3.hasData) { if (toggler.item3.isLoading || !toggler.item3.hasData) {
return const PlatformCircularProgressIndicator(); return const CircularProgressIndicator();
} }
return HeartButton( return HeartButton(
@ -150,7 +150,7 @@ class PlaylistHeartButton extends HookConsumerWidget {
).dominantColor; ).dominantColor;
if (me.isLoading || !me.hasData) { if (me.isLoading || !me.hasData) {
return const PlatformCircularProgressIndicator(); return const CircularProgressIndicator();
} }
return HeartButton( return HeartButton(
@ -193,7 +193,7 @@ class AlbumHeartButton extends HookConsumerWidget {
); );
if (me.isLoading || !me.hasData) { if (me.isLoading || !me.hasData) {
return const PlatformCircularProgressIndicator(); return const CircularProgressIndicator();
} }
return HeartButton( return HeartButton(

View File

@ -1,6 +1,5 @@
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:platform_ui/platform_ui.dart';
class AnchorButton<T> extends HookWidget { class AnchorButton<T> extends HookWidget {
final String text; final String text;
@ -29,7 +28,7 @@ class AnchorButton<T> extends HookWidget {
onTap: onTap, onTap: onTap,
child: MouseRegion( child: MouseRegion(
cursor: MaterialStateMouseCursor.clickable, cursor: MaterialStateMouseCursor.clickable,
child: PlatformText( child: Text(
text, text,
style: style.copyWith( style: style.copyWith(
decoration: decoration:

View File

@ -1,10 +1,11 @@
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:platform_ui/platform_ui.dart';
import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
class PageWindowTitleBar extends StatefulHookWidget with PreferredSizeWidget { class PageWindowTitleBar extends StatefulHookWidget
implements PreferredSizeWidget {
final Widget? leading; final Widget? leading;
final bool automaticallyImplyLeading; final bool automaticallyImplyLeading;
final List<Widget>? actions; final List<Widget>? actions;
@ -18,13 +19,12 @@ class PageWindowTitleBar extends StatefulHookWidget with PreferredSizeWidget {
final TextStyle? toolbarTextStyle; final TextStyle? toolbarTextStyle;
final TextStyle? titleTextStyle; final TextStyle? titleTextStyle;
final double? titleWidth; final double? titleWidth;
final Widget? center; final Widget? title;
final bool hideWhenWindows;
PageWindowTitleBar({ const PageWindowTitleBar({
Key? key, Key? key,
this.actions, this.actions,
this.center, this.title,
this.toolbarOpacity = 1, this.toolbarOpacity = 1,
this.backgroundColor, this.backgroundColor,
this.actionsIconTheme, this.actionsIconTheme,
@ -37,13 +37,10 @@ class PageWindowTitleBar extends StatefulHookWidget with PreferredSizeWidget {
this.titleTextStyle, this.titleTextStyle,
this.titleWidth, this.titleWidth,
this.toolbarTextStyle, this.toolbarTextStyle,
this.hideWhenWindows = true,
}) : super(key: key); }) : super(key: key);
@override @override
Size get preferredSize => Size.fromHeight( Size get preferredSize => const Size.fromHeight(kToolbarHeight);
platform == TargetPlatform.windows ? 33 : kToolbarHeight,
);
@override @override
State<PageWindowTitleBar> createState() => _PageWindowTitleBarState(); State<PageWindowTitleBar> createState() => _PageWindowTitleBarState();
@ -54,33 +51,6 @@ class _PageWindowTitleBarState extends State<PageWindowTitleBar> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isMaximized = useState<bool?>(null); final isMaximized = useState<bool?>(null);
useEffect(() {
if (platform == TargetPlatform.windows &&
widget.hideWhenWindows &&
Navigator.of(context).canPop()) {
final entry = OverlayEntry(
builder: (context) => const Positioned(
left: 5,
top: 5,
child: PlatformBackButton(),
),
);
WidgetsBinding.instance.addPostFrameCallback((_) {
Overlay.of(context)?.insert(entry);
});
return () {
entry.remove();
};
}
return null;
}, [platform, widget.hideWhenWindows]);
if (platform == TargetPlatform.windows && widget.hideWhenWindows) {
return const SizedBox.shrink();
}
maximizeOrRestore() async { maximizeOrRestore() async {
if (await windowManager.isMaximized()) { if (await windowManager.isMaximized()) {
await windowManager.unmaximize(); await windowManager.unmaximize();
@ -91,42 +61,52 @@ class _PageWindowTitleBarState extends State<PageWindowTitleBar> {
} }
} }
return PlatformAppBar( return GestureDetector(
onHorizontalDragStart: (details) {
if (kIsDesktop) {
windowManager.startDragging();
}
},
onVerticalDragStart: (details) {
if (kIsDesktop) {
windowManager.startDragging();
}
},
child: AppBar(
leading: widget.leading,
automaticallyImplyLeading: widget.automaticallyImplyLeading,
actions: [ actions: [
...?widget.actions, ...?widget.actions,
if (!kIsMacOS && !kIsMobile) if (kIsDesktop && !kIsMacOS) ...[
PlatformWindowButtons( IconButton(
isMaximized: () async => icon: const Icon(Icons.minimize),
isMaximized.value ?? await windowManager.isMaximized(), onPressed: () => windowManager.minimize(),
onMaximize: maximizeOrRestore,
onRestore: maximizeOrRestore,
onMinimize: () {
windowManager.minimize();
},
onClose: () {
windowManager.close();
},
showCloseButton: true,
showMaximizeButton: true,
showMinimizeButton: true,
), ),
IconButton(
icon: Icon(
isMaximized.value ?? false
? Icons.fullscreen_exit
: Icons.fullscreen,
),
onPressed: maximizeOrRestore,
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => windowManager.close(),
),
]
], ],
onDrag: () {
if (kIsDesktop) windowManager.startDragging();
},
title: widget.center,
toolbarOpacity: widget.toolbarOpacity,
backgroundColor: widget.backgroundColor, backgroundColor: widget.backgroundColor,
actionsIconTheme: widget.actionsIconTheme,
automaticallyImplyLeading: widget.automaticallyImplyLeading,
centerTitle: widget.centerTitle,
foregroundColor: widget.foregroundColor, foregroundColor: widget.foregroundColor,
leading: widget.leading, actionsIconTheme: widget.actionsIconTheme,
leadingWidth: widget.leadingWidth, centerTitle: widget.centerTitle,
titleSpacing: widget.titleSpacing, titleSpacing: widget.titleSpacing,
titleTextStyle: widget.titleTextStyle, toolbarOpacity: widget.toolbarOpacity,
titleWidth: widget.titleWidth, leadingWidth: widget.leadingWidth,
toolbarTextStyle: widget.toolbarTextStyle, toolbarTextStyle: widget.toolbarTextStyle,
titleTextStyle: widget.titleTextStyle,
title: widget.title,
),
); );
} }
} }

View File

@ -1,12 +1,11 @@
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:platform_ui/platform_ui.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/hover_builder.dart'; import 'package:spotube/components/shared/hover_builder.dart';
import 'package:spotube/components/shared/spotube_marquee_text.dart'; import 'package:spotube/components/shared/spotube_marquee_text.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/hooks/use_platform_property.dart';
enum PlaybuttonCardViewType { square, list } enum PlaybuttonCardViewType { square, list }
@ -38,32 +37,7 @@ class PlaybuttonCard extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final backgroundColor = PlatformTheme.of(context).secondaryBackgroundColor; final backgroundColor = Theme.of(context).cardColor;
final boxShadow = usePlatformProperty<BoxShadow?>(
(context) => PlatformProperty(
android: BoxShadow(
blurRadius: 10,
offset: const Offset(0, 3),
spreadRadius: 5,
color: Theme.of(context).colorScheme.shadow,
),
ios: null,
macos: null,
linux: BoxShadow(
blurRadius: 6,
color: Theme.of(context).shadowColor.withOpacity(0.3),
),
windows: null,
),
);
final splash = usePlatformProperty<InteractiveInkFeatureFactory?>(
(context) => PlatformProperty.only(
android: InkRipple.splashFactory,
other: NoSplash.splashFactory,
),
);
final isSquare = viewType == PlaybuttonCardViewType.square; final isSquare = viewType == PlaybuttonCardViewType.square;
@ -72,7 +46,6 @@ class PlaybuttonCard extends HookWidget {
child: InkWell( child: InkWell(
onTap: onTap, onTap: onTap,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
splashFactory: splash,
highlightColor: Colors.black12, highlightColor: Colors.black12,
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
@ -80,18 +53,19 @@ class PlaybuttonCard extends HookWidget {
maxHeight: !isSquare ? 60 : double.infinity, maxHeight: !isSquare ? 60 : double.infinity,
), ),
child: HoverBuilder(builder: (context, isHovering) { child: HoverBuilder(builder: (context, isHovering) {
final playButton = PlatformIconButton( final playButton = IconButton(
onPressed: onPlaybuttonPressed, onPressed: onPlaybuttonPressed,
backgroundColor: PlatformTheme.of(context).primaryColor, style: IconButton.styleFrom(
hoverColor: backgroundColor: Theme.of(context).primaryColor,
PlatformTheme.of(context).primaryColor?.withOpacity(0.5), hoverColor: Theme.of(context).primaryColor.withOpacity(0.5),
),
icon: isLoading icon: isLoading
? SizedBox( ? SizedBox(
height: 23, height: 23,
width: 23, width: 23,
child: PlatformCircularProgressIndicator( child: CircularProgressIndicator(
color: ThemeData.estimateBrightnessForColor( color: ThemeData.estimateBrightnessForColor(
PlatformTheme.of(context).primaryColor!, Theme.of(context).primaryColor,
) == ) ==
Brightness.dark Brightness.dark
? Colors.white ? Colors.white
@ -103,32 +77,23 @@ class PlaybuttonCard extends HookWidget {
color: Colors.white, color: Colors.white,
), ),
); );
final addToQueueButton = PlatformIconButton( final addToQueueButton = IconButton(
onPressed: isLoading ? null : onAddToQueuePressed, onPressed: isLoading ? null : onAddToQueuePressed,
backgroundColor: style: IconButton.styleFrom(
PlatformTheme.of(context).secondaryBackgroundColor, backgroundColor: Theme.of(context).cardColor,
hoverColor: PlatformTheme.of(context) hoverColor: Theme.of(context)
.secondaryBackgroundColor .cardColor
?.withOpacity(0.5), .withOpacity(isLoading ? 1 : 0.5),
),
icon: const Icon(SpotubeIcons.queueAdd), icon: const Icon(SpotubeIcons.queueAdd),
); );
final image = Padding( final image = ClipRRect(
padding: EdgeInsets.all( borderRadius: BorderRadius.circular(8),
platform == TargetPlatform.windows ? 5 : 0,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(
[TargetPlatform.windows, TargetPlatform.linux]
.contains(platform)
? 5
: 8,
),
child: UniversalImage( child: UniversalImage(
path: imageUrl, path: imageUrl,
width: isSquare ? 200 : 60, width: isSquare ? 200 : 60,
placeholder: (context, url) => Assets.placeholder.image(), placeholder: (context, url) => Assets.placeholder.image(),
), ),
),
); );
final square = Column( final square = Column(
@ -147,7 +112,6 @@ class PlaybuttonCard extends HookWidget {
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
if (!isPlaying) addToQueueButton, if (!isPlaying) addToQueueButton,
if (platform != TargetPlatform.linux)
const SizedBox(height: 5), const SizedBox(height: 5),
playButton, playButton,
], ],
@ -178,7 +142,7 @@ class PlaybuttonCard extends HookWidget {
height: 30, height: 30,
child: SpotubeMarqueeText( child: SpotubeMarqueeText(
text: description!, text: description!,
style: PlatformTextTheme.of(context).caption, style: Theme.of(context).textTheme.bodySmall,
isHovering: isHovering, isHovering: isHovering,
), ),
), ),
@ -205,14 +169,15 @@ class PlaybuttonCard extends HookWidget {
children: [ children: [
TextSpan( TextSpan(
text: title, text: title,
style: PlatformTextTheme.of(context) style: Theme.of(context)
.body .textTheme
.bodyMedium
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
), ),
if (description != null) if (description != null)
TextSpan( TextSpan(
text: '\n$description', text: '\n$description',
style: PlatformTextTheme.of(context).caption, style: Theme.of(context).textTheme.bodySmall,
), ),
], ],
), ),
@ -235,23 +200,15 @@ class PlaybuttonCard extends HookWidget {
return Ink( return Ink(
decoration: BoxDecoration( decoration: BoxDecoration(
color: backgroundColor, color: backgroundColor,
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(8),
[TargetPlatform.windows, TargetPlatform.linux]
.contains(platform)
? 5
: 8,
),
boxShadow: [ boxShadow: [
if (boxShadow != null) boxShadow, BoxShadow(
blurRadius: 10,
offset: const Offset(0, 3),
spreadRadius: 5,
color: Theme.of(context).colorScheme.shadow,
),
], ],
border: [TargetPlatform.windows, TargetPlatform.macOS]
.contains(platform)
? Border.all(
color: PlatformTheme.of(context).borderColor ??
Colors.transparent,
width: 1,
)
: null,
), ),
child: isSquare ? square : list, child: isSquare ? square : list,
); );

View File

@ -1,6 +1,6 @@
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:platform_ui/platform_ui.dart';
import 'package:skeleton_text/skeleton_text.dart'; import 'package:skeleton_text/skeleton_text.dart';
import 'package:spotube/components/shared/shimmers/shimmer_track_tile.dart'; import 'package:spotube/components/shared/shimmers/shimmer_track_tile.dart';
import 'package:spotube/extensions/theme.dart'; import 'package:spotube/extensions/theme.dart';
@ -11,7 +11,7 @@ class ShimmerArtistProfile extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDark = PlatformTheme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
final shimmerTheme = ShimmerColorTheme( final shimmerTheme = ShimmerColorTheme(
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200], shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300], shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart'; import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
import 'package:spotube/extensions/theme.dart'; import 'package:spotube/extensions/theme.dart';
@ -8,7 +8,7 @@ class ShimmerCategories extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDark = PlatformTheme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
final shimmerTheme = ShimmerColorTheme( final shimmerTheme = ShimmerColorTheme(
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200], shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
); );

View File

@ -1,6 +1,6 @@
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:platform_ui/platform_ui.dart';
import 'package:skeleton_text/skeleton_text.dart'; import 'package:skeleton_text/skeleton_text.dart';
import 'package:spotube/extensions/theme.dart'; import 'package:spotube/extensions/theme.dart';
import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_breakpoints.dart';
@ -12,7 +12,7 @@ class ShimmerLyrics extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDark = PlatformTheme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
final shimmerTheme = ShimmerColorTheme( final shimmerTheme = ShimmerColorTheme(
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200], shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300], shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/extensions/theme.dart'; import 'package:spotube/extensions/theme.dart';
class ShimmerPlaybuttonCardPainter extends CustomPainter { class ShimmerPlaybuttonCardPainter extends CustomPainter {
@ -53,7 +53,7 @@ class ShimmerPlaybuttonCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDark = PlatformTheme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
final shimmerTheme = ShimmerColorTheme( final shimmerTheme = ShimmerColorTheme(
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200], shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300], shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/extensions/theme.dart'; import 'package:spotube/extensions/theme.dart';
class ShimmerTrackTilePainter extends CustomPainter { class ShimmerTrackTilePainter extends CustomPainter {
@ -67,7 +67,7 @@ class ShimmerTrackTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDark = PlatformTheme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
final shimmerTheme = ShimmerColorTheme( final shimmerTheme = ShimmerColorTheme(
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200], shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300], shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/library/user_local_tracks.dart'; import 'package:spotube/components/library/user_local_tracks.dart';
@ -14,39 +14,41 @@ class SortTracksDropdown extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PlatformPopupMenuButton<SortBy>( return PopupMenuButton<SortBy>(
items: [ itemBuilder: (context) {
PlatformPopupMenuItem( return [
PopupMenuItem(
value: SortBy.none, value: SortBy.none,
enabled: value != SortBy.none, enabled: value != SortBy.none,
child: const Text("None"), child: const Text("None"),
), ),
PlatformPopupMenuItem( PopupMenuItem(
value: SortBy.ascending, value: SortBy.ascending,
enabled: value != SortBy.ascending, enabled: value != SortBy.ascending,
child: const Text("Sort by A-Z"), child: const Text("Sort by A-Z"),
), ),
PlatformPopupMenuItem( PopupMenuItem(
value: SortBy.descending, value: SortBy.descending,
enabled: value != SortBy.descending, enabled: value != SortBy.descending,
child: const Text("Sort by Z-A"), child: const Text("Sort by Z-A"),
), ),
PlatformPopupMenuItem( PopupMenuItem(
value: SortBy.dateAdded, value: SortBy.dateAdded,
enabled: value != SortBy.dateAdded, enabled: value != SortBy.dateAdded,
child: const Text("Sort by Date"), child: const Text("Sort by Date"),
), ),
PlatformPopupMenuItem( PopupMenuItem(
value: SortBy.artist, value: SortBy.artist,
enabled: value != SortBy.artist, enabled: value != SortBy.artist,
child: const Text("Sort by Artist"), child: const Text("Sort by Artist"),
), ),
PlatformPopupMenuItem( PopupMenuItem(
value: SortBy.album, value: SortBy.album,
enabled: value != SortBy.album, enabled: value != SortBy.album,
child: const Text("Sort by Album"), child: const Text("Sort by Album"),
), ),
], ];
},
onSelected: onChanged, onSelected: onChanged,
tooltip: "Sort tracks", tooltip: "Sort tracks",
child: const Icon(SpotubeIcons.sort), child: const Icon(SpotubeIcons.sort),

View File

@ -1,27 +1,5 @@
import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:platform_ui/platform_ui.dart';
class SpotubePage extends CustomTransitionPage { class SpotubePage<T> extends MaterialPage<T> {
SpotubePage({ const SpotubePage({required super.child});
required super.child,
}) : super(
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return child;
},
);
@override
Route createRoute(BuildContext context) {
if (platform == TargetPlatform.windows) {
return FluentPageRoute(
builder: (context) => child,
settings: this,
maintainState: maintainState,
barrierLabel: barrierLabel,
fullscreenDialog: fullscreenDialog,
);
}
return super.createRoute(context);
}
} }

View File

@ -4,7 +4,7 @@ import 'package:collection/collection.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.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:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/compact_search.dart'; import 'package:spotube/components/shared/compact_search.dart';
@ -72,7 +72,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
final List<Widget> buttons = [ final List<Widget> buttons = [
if (showShare) if (showShare)
PlatformIconButton( IconButton(
icon: Icon( icon: Icon(
SpotubeIcons.share, SpotubeIcons.share,
color: color?.titleTextColor, color: color?.titleTextColor,
@ -80,7 +80,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
onPressed: onShare, onPressed: onShare,
), ),
if (heartBtn != null && auth != null) heartBtn!, if (heartBtn != null && auth != null) heartBtn!,
PlatformIconButton( IconButton(
tooltip: "Shuffle", tooltip: "Shuffle",
icon: Icon( icon: Icon(
SpotubeIcons.shuffle, SpotubeIcons.shuffle,
@ -91,7 +91,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
const SizedBox(width: 5), const SizedBox(width: 5),
// add to queue playlist // add to queue playlist
if (!isPlaying) if (!isPlaying)
PlatformIconButton( IconButton(
onPressed: tracksSnapshot.data != null ? onAddToQueue : null, onPressed: tracksSnapshot.data != null ? onAddToQueue : null,
icon: Icon( icon: Icon(
SpotubeIcons.queueAdd, SpotubeIcons.queueAdd,
@ -99,8 +99,10 @@ class TrackCollectionView<T> extends HookConsumerWidget {
), ),
), ),
// play playlist // play playlist
PlatformIconButton( IconButton(
backgroundColor: PlatformTheme.of(context).primaryColor, style: IconButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
),
onPressed: tracksSnapshot.data != null ? onPlay : null, onPressed: tracksSnapshot.data != null ? onPlay : null,
icon: Icon(isPlaying ? SpotubeIcons.stop : SpotubeIcons.play), icon: Icon(isPlaying ? SpotubeIcons.stop : SpotubeIcons.play),
), ),
@ -136,7 +138,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
}, [tracksSnapshot.data, searchText.value]); }, [tracksSnapshot.data, searchText.value]);
useCustomStatusBarColor( useCustomStatusBarColor(
color?.color ?? PlatformTheme.of(context).scaffoldBackgroundColor!, color?.color ?? Theme.of(context).scaffoldBackgroundColor,
GoRouter.of(context).location == routePath, GoRouter.of(context).location == routePath,
); );
@ -156,60 +158,38 @@ class TrackCollectionView<T> extends HookConsumerWidget {
final searchbar = ConstrainedBox( final searchbar = ConstrainedBox(
constraints: const BoxConstraints( constraints: const BoxConstraints(
maxWidth: 400, maxWidth: 300,
maxHeight: 50, maxHeight: 50,
), ),
child: PlatformTextField( child: TextField(
controller: searchController, controller: searchController,
onChanged: (value) => searchText.value = value, onChanged: (value) => searchText.value = value,
placeholder: "Search tracks...", style: TextStyle(color: color?.titleTextColor),
backgroundColor: Colors.transparent, decoration: InputDecoration(
focusedBackgroundColor: Colors.transparent, hintText: "Search tracks...",
style: TextStyle( hintStyle: TextStyle(color: color?.titleTextColor),
color: color?.titleTextColor, border: Theme.of(context).inputDecorationTheme.border?.copyWith(
borderSide: BorderSide(
color: color?.titleTextColor ?? Colors.white,
), ),
placeholderStyle: TextStyle(
color: color?.titleTextColor,
), ),
focusedStyle: TextStyle(
color: color?.titleTextColor,
),
borderColor: color?.titleTextColor,
prefixIconColor: color?.titleTextColor, prefixIconColor: color?.titleTextColor,
cursorColor: color?.titleTextColor, prefixIcon: const Icon(SpotubeIcons.search),
prefixIcon: SpotubeIcons.search, ),
), ),
); );
useEffect(() {
OverlayEntry? entry;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (platform == TargetPlatform.windows &&
kIsDesktop &&
!collapsed.value) {
entry = OverlayEntry(builder: (context) {
return Positioned(
left: 40,
top: 7,
child: searchbar,
);
});
Overlay.of(context).insert(entry!);
}
});
return () => entry?.remove();
}, [color?.titleTextColor, collapsed.value]);
return SafeArea( return SafeArea(
child: PlatformScaffold( child: Scaffold(
appBar: kIsDesktop appBar: kIsDesktop
? PageWindowTitleBar( ? PageWindowTitleBar(
backgroundColor: color?.color, backgroundColor: color?.color,
foregroundColor: color?.titleTextColor, foregroundColor: color?.titleTextColor,
leadingWidth: 400,
leading: Row( leading: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
PlatformBackButton(color: color?.titleTextColor), BackButton(color: color?.titleTextColor),
const SizedBox(width: 10), const SizedBox(width: 10),
searchbar, searchbar,
], ],
@ -239,15 +219,16 @@ class TrackCollectionView<T> extends HookConsumerWidget {
expandedHeight: 400, expandedHeight: 400,
automaticallyImplyLeading: kIsMobile, automaticallyImplyLeading: kIsMobile,
leading: kIsMobile leading: kIsMobile
? PlatformBackButton(color: color?.titleTextColor) ? BackButton(color: color?.titleTextColor)
: null, : null,
iconTheme: IconThemeData(color: color?.titleTextColor), iconTheme: IconThemeData(color: color?.titleTextColor),
primary: true, primary: true,
backgroundColor: color?.color, backgroundColor: color?.color,
title: collapsed.value title: collapsed.value
? PlatformText.headline( ? Text(
title, title,
style: TextStyle( style:
Theme.of(context).textTheme.titleLarge!.copyWith(
color: color?.titleTextColor, color: color?.titleTextColor,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@ -268,7 +249,6 @@ class TrackCollectionView<T> extends HookConsumerWidget {
), ),
), ),
child: Material( child: Material(
textStyle: PlatformTheme.of(context).textTheme!.body!,
type: MaterialType.transparency, type: MaterialType.transparency,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -299,15 +279,18 @@ class TrackCollectionView<T> extends HookConsumerWidget {
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.spaceBetween, MainAxisAlignment.spaceBetween,
children: [ children: [
PlatformText.headline( Text(
title, title,
style: TextStyle( style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
color: color?.titleTextColor, color: color?.titleTextColor,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
if (description != null) if (description != null)
PlatformText( Text(
description!, description!,
style: TextStyle( style: TextStyle(
color: color?.bodyTextColor, color: color?.bodyTextColor,
@ -335,7 +318,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
return const ShimmerTrackTile(); return const ShimmerTrackTile();
} else if (tracksSnapshot.hasError) { } else if (tracksSnapshot.hasError) {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: PlatformText("Error ${tracksSnapshot.error}")); child: Text("Error ${tracksSnapshot.error}"));
} }
return TracksTableView( return TracksTableView(

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart' hide Action;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart' hide Image; import 'package:spotify/spotify.dart' hide Image;
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
@ -11,7 +11,6 @@ import 'package:spotube/components/shared/adaptive/adaptive_popup_menu_button.da
import 'package:spotube/components/shared/heart_button.dart'; import 'package:spotube/components/shared/heart_button.dart';
import 'package:spotube/components/shared/links/link_text.dart'; import 'package:spotube/components/shared/links/link_text.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/hooks/use_breakpoints.dart'; import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/models/logger.dart'; import 'package:spotube/models/logger.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
@ -85,7 +84,7 @@ class TrackTile extends HookConsumerWidget {
SnackBar( SnackBar(
width: 300, width: 300,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
content: PlatformText( content: Text(
"Copied $data to clipboard", "Copied $data to clipboard",
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@ -95,37 +94,37 @@ class TrackTile extends HookConsumerWidget {
} }
Future<void> actionAddToPlaylist() async { Future<void> actionAddToPlaylist() async {
showPlatformAlertDialog(context, builder: (context) { showDialog(
context: context,
builder: (context) {
return FutureBuilder<Iterable<PlaylistSimple>>( return FutureBuilder<Iterable<PlaylistSimple>>(
future: spotify.playlists.me.all().then((playlists) async { future: spotify.playlists.me.all().then((playlists) async {
final me = await spotify.me.get(); final me = await spotify.me.get();
return playlists.where((playlist) => return playlists.where((playlist) =>
playlist.owner?.id != null && playlist.owner!.id == me.id); playlist.owner?.id != null &&
playlist.owner!.id == me.id);
}), }),
builder: (context, snapshot) { builder: (context, snapshot) {
return HookBuilder(builder: (context) { return HookBuilder(builder: (context) {
final playlistsCheck = useState(<String, bool>{}); final playlistsCheck = useState(<String, bool>{});
return PlatformAlertDialog( return AlertDialog(
macosAppIcon: Sidebar.brandLogo(), title: Text(
title: PlatformText(
"Add `${track.value.name}` to following Playlists", "Add `${track.value.name}` to following Playlists",
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
secondaryActions: [ actions: [
PlatformFilledButton( OutlinedButton(
isSecondary: true, child: const Text("Cancel"),
child: const PlatformText("Cancel"),
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
), ),
], FilledButton(
primaryActions: [ child: const Text("Add"),
PlatformFilledButton(
child: const PlatformText("Add"),
onPressed: () async { onPressed: () async {
final selectedPlaylists = playlistsCheck.value.entries final selectedPlaylists = playlistsCheck
.value.entries
.where((entry) => entry.value) .where((entry) => entry.value)
.map((entry) => entry.key); .map((entry) => entry.key);
@ -142,17 +141,16 @@ class TrackTile extends HookConsumerWidget {
height: 300, height: 300,
width: 300, width: 300,
child: !snapshot.hasData child: !snapshot.hasData
? const Center( ? const Center(child: CircularProgressIndicator())
child: PlatformCircularProgressIndicator())
: ListView.builder( : ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: snapshot.data!.length, itemCount: snapshot.data!.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final playlist = snapshot.data!.elementAt(index); final playlist =
return PlatformCheckbox( snapshot.data!.elementAt(index);
label: PlatformText(playlist.name!), return Checkbox(
value: value: playlistsCheck.value[playlist.id] ??
playlistsCheck.value[playlist.id] ?? false, false,
onChanged: (val) { onChanged: (val) {
playlistsCheck.value = { playlistsCheck.value = {
...playlistsCheck.value, ...playlistsCheck.value,
@ -189,11 +187,10 @@ class TrackTile extends HookConsumerWidget {
), ),
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,
textStyle: PlatformTheme.of(context).textTheme!.body!,
child: Row( child: Row(
children: [ children: [
if (showCheck) if (showCheck)
PlatformCheckbox( Checkbox(
value: isChecked, value: isChecked,
onChanged: (s) => onCheckChange?.call(s), onChanged: (s) => onCheckChange?.call(s),
) )
@ -229,16 +226,17 @@ class TrackTile extends HookConsumerWidget {
), ),
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: PlatformIconButton( child: IconButton(
icon: Icon( icon: Icon(
playlist?.activeTrack.id == track.value.id playlist?.activeTrack.id == track.value.id
? SpotubeIcons.pause ? SpotubeIcons.pause
: SpotubeIcons.play, : SpotubeIcons.play,
color: Colors.white, color: Colors.white,
), ),
backgroundColor: PlatformTheme.of(context).primaryColor, style: IconButton.styleFrom(
hoverColor: backgroundColor: Theme.of(context).primaryColor,
PlatformTheme.of(context).primaryColor?.withOpacity(0.5), hoverColor: Theme.of(context).primaryColor.withOpacity(0.5),
),
onPressed: !isBlackListed onPressed: !isBlackListed
? () => onTrackPlayButtonPressed?.call( ? () => onTrackPlayButtonPressed?.call(
track.value, track.value,
@ -255,7 +253,7 @@ class TrackTile extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Flexible( Flexible(
child: PlatformText( child: Text(
track.value.name ?? "", track.value.name ?? "",
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -266,7 +264,7 @@ class TrackTile extends HookConsumerWidget {
), ),
if (isBlackListed) ...[ if (isBlackListed) ...[
const SizedBox(width: 5), const SizedBox(width: 5),
PlatformText( Text(
"Blacklisted", "Blacklisted",
style: TextStyle( style: TextStyle(
color: Colors.red[400], color: Colors.red[400],
@ -278,7 +276,7 @@ class TrackTile extends HookConsumerWidget {
], ],
), ),
isLocal isLocal
? PlatformText( ? Text(
TypeConversionUtils.artists_X_String<Artist>( TypeConversionUtils.artists_X_String<Artist>(
track.value.artists ?? []), track.value.artists ?? []),
) )
@ -294,7 +292,7 @@ class TrackTile extends HookConsumerWidget {
if (breakpoint.isMoreThan(Breakpoints.md) && showAlbum) if (breakpoint.isMoreThan(Breakpoints.md) && showAlbum)
Expanded( Expanded(
child: isLocal child: isLocal
? PlatformText(track.value.album?.name ?? "") ? Text(track.value.album?.name ?? "")
: LinkText( : LinkText(
track.value.album!.name!, track.value.album!.name!,
"/album/${track.value.album?.id}", "/album/${track.value.album?.id}",
@ -304,7 +302,7 @@ class TrackTile extends HookConsumerWidget {
), ),
if (!breakpoint.isSm) ...[ if (!breakpoint.isSm) ...[
const SizedBox(width: 10), const SizedBox(width: 10),
PlatformText(duration), Text(duration),
], ],
const SizedBox(width: 10), const SizedBox(width: 10),
if (!isLocal) if (!isLocal)
@ -313,13 +311,12 @@ class TrackTile extends HookConsumerWidget {
if (!playlistQueueNotifier.isTrackOnQueue(track.value)) if (!playlistQueueNotifier.isTrackOnQueue(track.value))
Action( Action(
icon: const Icon(SpotubeIcons.queueAdd), icon: const Icon(SpotubeIcons.queueAdd),
text: const PlatformText("Add to queue"), text: const Text("Add to queue"),
onPressed: () { onPressed: () {
playlistQueueNotifier.add([track.value]); playlistQueueNotifier.add([track.value]);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: PlatformText( content: Text("Added ${track.value.name} to queue"),
"Added ${track.value.name} to queue"),
), ),
); );
}, },
@ -327,13 +324,13 @@ class TrackTile extends HookConsumerWidget {
else else
Action( Action(
icon: const Icon(SpotubeIcons.queueRemove), icon: const Icon(SpotubeIcons.queueRemove),
text: const PlatformText("Remove from queue"), text: const Text("Remove from queue"),
onPressed: () { onPressed: () {
playlistQueueNotifier.remove([track.value]); playlistQueueNotifier.remove([track.value]);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: PlatformText( content:
"Removed ${track.value.name} from queue"), Text("Removed ${track.value.name} from queue"),
), ),
); );
}, },
@ -346,7 +343,7 @@ class TrackTile extends HookConsumerWidget {
color: Colors.pink, color: Colors.pink,
) )
: const Icon(SpotubeIcons.heart), : const Icon(SpotubeIcons.heart),
text: const PlatformText("Save as favorite"), text: const Text("Save as favorite"),
onPressed: () { onPressed: () {
toggler.item2.mutate(toggler.item1); toggler.item2.mutate(toggler.item1);
}, },
@ -354,7 +351,7 @@ class TrackTile extends HookConsumerWidget {
if (auth != null) if (auth != null)
Action( Action(
icon: const Icon(SpotubeIcons.playlistAdd), icon: const Icon(SpotubeIcons.playlistAdd),
text: const PlatformText("Add To playlist"), text: const Text("Add To playlist"),
onPressed: actionAddToPlaylist, onPressed: actionAddToPlaylist,
), ),
if (userPlaylist && auth != null) if (userPlaylist && auth != null)
@ -362,10 +359,10 @@ class TrackTile extends HookConsumerWidget {
icon: (removeTrack.isMutating || !removeTrack.hasData) && icon: (removeTrack.isMutating || !removeTrack.hasData) &&
removingTrack.value == track.value.uri removingTrack.value == track.value.uri
? const Center( ? const Center(
child: PlatformCircularProgressIndicator(), child: CircularProgressIndicator(),
) )
: const Icon(SpotubeIcons.removeFilled), : const Icon(SpotubeIcons.removeFilled),
text: const PlatformText("Remove from playlist"), text: const Text("Remove from playlist"),
onPressed: () { onPressed: () {
removingTrack.value = track.value.uri; removingTrack.value = track.value.uri;
removeTrack.mutate(track.value.uri!); removeTrack.mutate(track.value.uri!);
@ -373,7 +370,7 @@ class TrackTile extends HookConsumerWidget {
), ),
Action( Action(
icon: const Icon(SpotubeIcons.share), icon: const Icon(SpotubeIcons.share),
text: const PlatformText("Share"), text: const Text("Share"),
onPressed: () { onPressed: () {
actionShare(track.value); actionShare(track.value);
}, },
@ -384,7 +381,7 @@ class TrackTile extends HookConsumerWidget {
color: isBlackListed ? Colors.white : Colors.red[400], color: isBlackListed ? Colors.white : Colors.red[400],
), ),
backgroundColor: isBlackListed ? Colors.red[400] : null, backgroundColor: isBlackListed ? Colors.red[400] : null,
text: PlatformText( text: Text(
"${isBlackListed ? "Remove from" : "Add to"} blacklist", "${isBlackListed ? "Remove from" : "Add to"} blacklist",
style: TextStyle( style: TextStyle(
color: isBlackListed ? Colors.white : Colors.red[400], color: isBlackListed ? Colors.white : Colors.red[400],

View File

@ -1,7 +1,7 @@
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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/dialogs/confirm_download_dialog.dart'; import 'package:spotube/components/shared/dialogs/confirm_download_dialog.dart';
@ -71,7 +71,7 @@ class TracksTableView extends HookConsumerWidget {
if (heading != null) heading!, if (heading != null) heading!,
Row( Row(
children: [ children: [
PlatformCheckbox( Checkbox(
value: selected.value.length == sortedTracks.length, value: selected.value.length == sortedTracks.length,
onChanged: (checked) { onChanged: (checked) {
if (!showCheck.value) showCheck.value = true; if (!showCheck.value) showCheck.value = true;
@ -85,7 +85,7 @@ class TracksTableView extends HookConsumerWidget {
), ),
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: PlatformText( child: Text(
"#", "#",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: tableHeadStyle, style: tableHeadStyle,
@ -94,7 +94,7 @@ class TracksTableView extends HookConsumerWidget {
Expanded( Expanded(
child: Row( child: Row(
children: [ children: [
PlatformText( Text(
"Title", "Title",
style: tableHeadStyle, style: tableHeadStyle,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
@ -108,7 +108,7 @@ class TracksTableView extends HookConsumerWidget {
Expanded( Expanded(
child: Row( child: Row(
children: [ children: [
PlatformText( Text(
"Album", "Album",
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: tableHeadStyle, style: tableHeadStyle,
@ -119,7 +119,7 @@ class TracksTableView extends HookConsumerWidget {
], ],
if (!breakpoint.isSm) ...[ if (!breakpoint.isSm) ...[
const SizedBox(width: 10), const SizedBox(width: 10),
PlatformText("Time", style: tableHeadStyle), Text("Time", style: tableHeadStyle),
const SizedBox(width: 10), const SizedBox(width: 10),
], ],
SortTracksDropdown( SortTracksDropdown(
@ -131,44 +131,45 @@ class TracksTableView extends HookConsumerWidget {
.state = value; .state = value;
}, },
), ),
PlatformPopupMenuButton( PopupMenuButton(
closeAfterClick: false,
tooltip: "More Actions", tooltip: "More Actions",
items: [ itemBuilder: (context) {
PlatformPopupMenuItem( return [
PopupMenuItem(
enabled: selectedTracks.isNotEmpty, enabled: selectedTracks.isNotEmpty,
value: "download", value: "download",
child: Row( child: Row(
children: [ children: [
const Icon(SpotubeIcons.download), const Icon(SpotubeIcons.download),
const SizedBox(width: 5), const SizedBox(width: 5),
PlatformText( Text(
"Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}", "Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}",
), ),
], ],
), ),
), ),
if (!userPlaylist) if (!userPlaylist)
PlatformPopupMenuItem( PopupMenuItem(
enabled: selectedTracks.isNotEmpty, enabled: selectedTracks.isNotEmpty,
value: "add-to-playlist", value: "add-to-playlist",
child: Row( child: Row(
children: [ children: [
const Icon(SpotubeIcons.playlistAdd), const Icon(SpotubeIcons.playlistAdd),
const SizedBox(width: 5), const SizedBox(width: 5),
PlatformText( Text(
"Add (${selectedTracks.length}) to Playlist", "Add (${selectedTracks.length}) to Playlist",
), ),
], ],
), ),
), ),
], ];
},
onSelected: (action) async { onSelected: (action) async {
switch (action) { switch (action) {
case "download": case "download":
{ {
final confirmed = await showPlatformAlertDialog( final confirmed = await showDialog(
context, context: context,
builder: (context) { builder: (context) {
return const ConfirmDownloadDialog(); return const ConfirmDownloadDialog();
}, },
@ -183,8 +184,8 @@ class TracksTableView extends HookConsumerWidget {
} }
case "add-to-playlist": case "add-to-playlist":
{ {
await showPlatformAlertDialog( await showDialog(
context, context: context,
builder: (context) { builder: (context) {
return PlaylistAddTrackDialog( return PlaylistAddTrackDialog(
tracks: selectedTracks.toList(), tracks: selectedTracks.toList(),

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:platform_ui/platform_ui.dart';
void useCustomStatusBarColor( void useCustomStatusBarColor(
Color color, Color color,
@ -9,7 +8,7 @@ void useCustomStatusBarColor(
bool noSetBGColor = false, bool noSetBGColor = false,
}) { }) {
final context = useContext(); final context = useContext();
final backgroundColor = PlatformTheme.of(context).scaffoldBackgroundColor!; final backgroundColor = Theme.of(context).scaffoldBackgroundColor!;
resetStatusbar() => SystemChrome.setSystemUIOverlayStyle( resetStatusbar() => SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle( SystemUiOverlayStyle(
statusBarColor: backgroundColor, // status bar color statusBarColor: backgroundColor, // status bar color

View File

@ -1,10 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:platform_ui/platform_ui.dart';
T usePlatformProperty<T>(
PlatformProperty<T> Function(BuildContext context) getProperties) {
final context = useContext();
return getProperties(context).resolve(platform ?? Theme.of(context).platform);
}

View File

@ -5,8 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/components/shared/links/anchor_button.dart'; import 'package:spotube/components/shared/links/anchor_button.dart';
import 'package:spotube/hooks/use_package_info.dart'; import 'package:spotube/hooks/use_package_info.dart';
import 'package:spotube/provider/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences_provider.dart';
@ -56,18 +55,17 @@ void useUpdateChecker(WidgetRef ref) {
(latestVersion.isPreRelease && !currentVersion.isPreRelease) || (latestVersion.isPreRelease && !currentVersion.isPreRelease) ||
(!latestVersion.isPreRelease && currentVersion.isPreRelease)) return; (!latestVersion.isPreRelease && currentVersion.isPreRelease)) return;
if (latestVersion <= currentVersion) return; if (latestVersion <= currentVersion) return;
showPlatformAlertDialog( showDialog(
context, context: context,
barrierDismissible: true, barrierDismissible: true,
barrierColor: Colors.black26, barrierColor: Colors.black26,
builder: (context) { builder: (context) {
const url = const url =
"https://spotube.netlify.app/other-downloads/stable-downloads"; "https://spotube.netlify.app/other-downloads/stable-downloads";
return PlatformAlertDialog( return AlertDialog(
macosAppIcon: Sidebar.brandLogo(), title: const Text("Spotube has an update"),
title: const PlatformText("Spotube has an update"), actions: [
primaryActions: [ FilledButton(
PlatformFilledButton(
child: const Text("Download Now"), child: const Text("Download Now"),
onPressed: () => download(url), onPressed: () => download(url),
), ),
@ -79,7 +77,7 @@ void useUpdateChecker(WidgetRef ref) {
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const PlatformText("Read the latest "), const Text("Read the latest "),
AnchorButton( AnchorButton(
"release notes", "release notes",
style: const TextStyle(color: Colors.blue), style: const TextStyle(color: Colors.blue),

View File

@ -11,7 +11,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotube/collections/cache_keys.dart'; import 'package:spotube/collections/cache_keys.dart';
import 'package:spotube/collections/env.dart'; import 'package:spotube/collections/env.dart';
@ -25,7 +24,7 @@ import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/services/audio_player.dart'; import 'package:spotube/services/audio_player.dart';
import 'package:spotube/services/pocketbase.dart'; import 'package:spotube/services/pocketbase.dart';
import 'package:spotube/services/youtube.dart'; import 'package:spotube/services/youtube.dart';
import 'package:spotube/themes/light_theme.dart'; import 'package:spotube/themes/theme.dart';
import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/platform.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'package:window_size/window_size.dart'; import 'package:window_size/window_size.dart';
@ -157,8 +156,8 @@ void main(List<String> rawArgs) async {
logger.v( logger.v(
"[onFileExists] download confirmation for ${track.name}", "[onFileExists] download confirmation for ${track.name}",
); );
return showPlatformAlertDialog<bool>( return showDialog<bool>(
context, context: context,
builder: (_) => builder: (_) =>
ReplaceDownloadedDialog(track: track), ReplaceDownloadedDialog(track: track),
).then((s) => s ?? false); ).then((s) => s ?? false);
@ -206,14 +205,6 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
super.initState(); super.initState();
SharedPreferences.getInstance().then(((value) => localStorage = value)); SharedPreferences.getInstance().then(((value) => localStorage = value));
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
appPlatform = Theme.of(context).platform;
if (appPlatform == TargetPlatform.macOS) {
appPlatform = TargetPlatform.android;
}
});
});
} }
@override @override
@ -243,16 +234,6 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
prevSize = size; prevSize = size;
} }
TargetPlatform appPlatform = TargetPlatform.android;
void changePlatform(TargetPlatform targetPlatform) {
appPlatform = targetPlatform;
if (appPlatform == TargetPlatform.macOS) {
appPlatform = TargetPlatform.android;
}
setState(() {});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final themeMode = final themeMode =
@ -268,9 +249,7 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
}; };
}, []); }, []);
platform = appPlatform; return MaterialApp.router(
return PlatformApp.router(
routeInformationParser: router.routeInformationParser, routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate, routerDelegate: router.routerDelegate,
routeInformationProvider: router.routeInformationProvider, routeInformationProvider: router.routeInformationProvider,
@ -279,17 +258,10 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
builder: (context, child) { builder: (context, child) {
return DragToResizeArea(child: child!); return DragToResizeArea(child: child!);
}, },
androidTheme: theme(accentMaterialColor, Brightness.light),
androidDarkTheme: theme(accentMaterialColor, Brightness.dark),
linuxTheme: linuxTheme,
linuxDarkTheme: linuxDarkTheme,
iosTheme: themeMode == ThemeMode.dark ? iosDarkTheme : iosTheme,
windowsTheme: windowsTheme,
windowsDarkTheme: windowsDarkTheme,
macosTheme: macosTheme,
macosDarkTheme: macosDarkTheme,
themeMode: themeMode, themeMode: themeMode,
shortcuts: PlatformProperty.all({ theme: theme(accentMaterialColor, Brightness.light),
darkTheme: theme(accentMaterialColor, Brightness.dark),
shortcuts: {
...WidgetsApp.defaultShortcuts.map((key, value) { ...WidgetsApp.defaultShortcuts.map((key, value) {
return MapEntry( return MapEntry(
LogicalKeySet.fromSet(key.triggers?.toSet() ?? {}), LogicalKeySet.fromSet(key.triggers?.toSet() ?? {}),
@ -324,14 +296,14 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
LogicalKeyboardKey.control, LogicalKeyboardKey.control,
LogicalKeyboardKey.shift, LogicalKeyboardKey.shift,
): CloseAppIntent(), ): CloseAppIntent(),
}), },
actions: PlatformProperty.all({ actions: {
...WidgetsApp.defaultActions, ...WidgetsApp.defaultActions,
PlayPauseIntent: PlayPauseAction(), PlayPauseIntent: PlayPauseAction(),
NavigationIntent: NavigationAction(), NavigationIntent: NavigationAction(),
HomeTabIntent: HomeTabAction(), HomeTabIntent: HomeTabAction(),
CloseAppIntent: CloseAppAction(), CloseAppIntent: CloseAppAction(),
}), },
); );
} }
} }

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/shimmers/shimmer_artist_profile.dart'; import 'package:spotube/components/shared/shimmers/shimmer_artist_profile.dart';
@ -33,13 +33,13 @@ class ArtistPage extends HookConsumerWidget {
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
SpotifyApi spotify = ref.watch(spotifyProvider); SpotifyApi spotify = ref.watch(spotifyProvider);
final parentScrollController = useScrollController(); final parentScrollController = useScrollController();
final textTheme = PlatformTheme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
final chipTextVariant = useBreakpointValue( final chipTextVariant = useBreakpointValue(
sm: textTheme!.caption, sm: textTheme.bodySmall,
md: textTheme.body, md: textTheme.bodyMedium,
lg: textTheme.subheading, lg: textTheme.bodyLarge,
xl: textTheme.headline, xl: textTheme.titleSmall,
xxl: textTheme.headline, xxl: textTheme.titleMedium,
); );
final avatarWidth = useBreakpointValue( final avatarWidth = useBreakpointValue(
@ -58,9 +58,9 @@ class ArtistPage extends HookConsumerWidget {
final auth = ref.watch(AuthenticationNotifier.provider); final auth = ref.watch(AuthenticationNotifier.provider);
return SafeArea( return SafeArea(
child: PlatformScaffold( child: Scaffold(
appBar: PageWindowTitleBar( appBar: const PageWindowTitleBar(
leading: const PlatformBackButton(), leading: BackButton(),
), ),
body: HookBuilder( body: HookBuilder(
builder: (context) { builder: (context) {
@ -70,7 +70,7 @@ class ArtistPage extends HookConsumerWidget {
return const ShimmerArtistProfile(); return const ShimmerArtistProfile();
} else if (artistsQuery.hasError) { } else if (artistsQuery.hasError) {
return Center( return Center(
child: PlatformText(artistsQuery.error.toString()), child: Text(artistsQuery.error.toString()),
); );
} }
@ -116,7 +116,7 @@ class ArtistPage extends HookConsumerWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blue, color: Colors.blue,
borderRadius: BorderRadius.circular(50)), borderRadius: BorderRadius.circular(50)),
child: PlatformText( child: Text(
data.type!.toUpperCase(), data.type!.toUpperCase(),
style: chipTextVariant?.copyWith( style: chipTextVariant?.copyWith(
color: Colors.white, color: Colors.white,
@ -132,7 +132,7 @@ class ArtistPage extends HookConsumerWidget {
color: Colors.red[400], color: Colors.red[400],
borderRadius: borderRadius:
BorderRadius.circular(50)), BorderRadius.circular(50)),
child: PlatformText( child: Text(
"Blacklisted", "Blacklisted",
style: chipTextVariant?.copyWith( style: chipTextVariant?.copyWith(
color: Colors.white, color: Colors.white,
@ -142,18 +142,18 @@ class ArtistPage extends HookConsumerWidget {
] ]
], ],
), ),
PlatformText( Text(
data.name!, data.name!,
style: breakpoint.isSm style: breakpoint.isSm
? textTheme.subheading ? textTheme.headlineSmall
: textTheme.headline, : textTheme.headlineMedium,
), ),
PlatformText( Text(
"${PrimitiveUtils.toReadableNumber(data.followers!.total!.toDouble())} followers", "${PrimitiveUtils.toReadableNumber(data.followers!.total!.toDouble())} followers",
style: breakpoint.isSm style: textTheme.bodyMedium?.copyWith(
? textTheme.body fontWeight:
: textTheme.body breakpoint.isSm ? null : FontWeight.bold,
?.copyWith(fontWeight: FontWeight.bold), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Row( Row(
@ -170,14 +170,13 @@ class ArtistPage extends HookConsumerWidget {
return const SizedBox( return const SizedBox(
height: 20, height: 20,
width: 20, width: 20,
child: child: CircularProgressIndicator(),
PlatformCircularProgressIndicator(),
); );
} }
final queryBowl = QueryClient.of(context); final queryBowl = QueryClient.of(context);
return PlatformFilledButton( return FilledButton(
onPressed: () async { onPressed: () async {
try { try {
isFollowingQuery.data! isFollowingQuery.data!
@ -199,7 +198,7 @@ class ArtistPage extends HookConsumerWidget {
"user-follows-artists-query/$artistId"); "user-follows-artists-query/$artistId");
} }
}, },
child: PlatformText( child: Text(
isFollowingQuery.data! isFollowingQuery.data!
? "Following" ? "Following"
: "Follow", : "Follow",
@ -208,7 +207,7 @@ class ArtistPage extends HookConsumerWidget {
}, },
), ),
const SizedBox(width: 5), const SizedBox(width: 5),
PlatformIconButton( IconButton(
tooltip: "Add to blacklisted artists", tooltip: "Add to blacklisted artists",
icon: Icon( icon: Icon(
SpotubeIcons.userRemove, SpotubeIcons.userRemove,
@ -216,8 +215,10 @@ class ArtistPage extends HookConsumerWidget {
? Colors.red[400] ? Colors.red[400]
: Colors.white, : Colors.white,
), ),
style: IconButton.styleFrom(
backgroundColor: backgroundColor:
isBlackListed ? Colors.red[400] : null, isBlackListed ? Colors.red[400] : null,
),
onPressed: () async { onPressed: () async {
if (isBlackListed) { if (isBlackListed) {
ref ref
@ -238,7 +239,7 @@ class ArtistPage extends HookConsumerWidget {
} }
}, },
), ),
PlatformIconButton( IconButton(
icon: const Icon(SpotubeIcons.share), icon: const Icon(SpotubeIcons.share),
onPressed: () async { onPressed: () async {
await Clipboard.setData( await Clipboard.setData(
@ -250,7 +251,7 @@ class ArtistPage extends HookConsumerWidget {
const SnackBar( const SnackBar(
width: 300, width: 300,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
content: PlatformText( content: Text(
"Artist URL copied to clipboard", "Artist URL copied to clipboard",
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@ -279,10 +280,10 @@ class ArtistPage extends HookConsumerWidget {
); );
if (topTracksQuery.isLoading || !topTracksQuery.hasData) { if (topTracksQuery.isLoading || !topTracksQuery.hasData) {
return const PlatformCircularProgressIndicator(); return const CircularProgressIndicator();
} else if (topTracksQuery.hasError) { } else if (topTracksQuery.hasError) {
return Center( return Center(
child: PlatformText(topTracksQuery.error.toString()), child: Text(topTracksQuery.error.toString()),
); );
} }
@ -305,13 +306,12 @@ class ArtistPage extends HookConsumerWidget {
return Column(children: [ return Column(children: [
Row( Row(
children: [ children: [
PlatformText( Text(
"Top Tracks", "Top Tracks",
style: style: Theme.of(context).textTheme.headlineSmall,
PlatformTheme.of(context).textTheme?.headline,
), ),
if (!isPlaylistPlaying) if (!isPlaylistPlaying)
PlatformIconButton( IconButton(
icon: const Icon( icon: const Icon(
SpotubeIcons.queueAdd, SpotubeIcons.queueAdd,
), ),
@ -321,7 +321,7 @@ class ArtistPage extends HookConsumerWidget {
SnackBar( SnackBar(
width: 300, width: 300,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
content: PlatformText( content: Text(
"Added ${topTracks.length} tracks to queue", "Added ${topTracks.length} tracks to queue",
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@ -329,17 +329,17 @@ class ArtistPage extends HookConsumerWidget {
); );
}, },
), ),
if (platform != TargetPlatform.linux)
const SizedBox(width: 5), const SizedBox(width: 5),
PlatformIconButton( IconButton(
icon: Icon( icon: Icon(
isPlaylistPlaying isPlaylistPlaying
? SpotubeIcons.stop ? SpotubeIcons.stop
: SpotubeIcons.play, : SpotubeIcons.play,
color: Colors.white, color: Colors.white,
), ),
backgroundColor: style: IconButton.styleFrom(
PlatformTheme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
),
onPressed: () => playPlaylist(topTracks.toList()), onPressed: () => playPlaylist(topTracks.toList()),
) )
], ],
@ -364,16 +364,16 @@ class ArtistPage extends HookConsumerWidget {
}, },
), ),
const SizedBox(height: 50), const SizedBox(height: 50),
PlatformText( Text(
"Albums", "Albums",
style: PlatformTheme.of(context).textTheme?.headline, style: Theme.of(context).textTheme.headlineSmall,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
ArtistAlbumList(artistId), ArtistAlbumList(artistId),
const SizedBox(height: 20), const SizedBox(height: 20),
PlatformText( Text(
"Fans also likes", "Fans also likes",
style: PlatformTheme.of(context).textTheme?.headline, style: Theme.of(context).textTheme.headlineSmall,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
HookBuilder( HookBuilder(
@ -384,10 +384,10 @@ class ArtistPage extends HookConsumerWidget {
); );
if (relatedArtists.isLoading || !relatedArtists.hasData) { if (relatedArtists.isLoading || !relatedArtists.hasData) {
return const PlatformCircularProgressIndicator(); return const CircularProgressIndicator();
} else if (relatedArtists.hasError) { } else if (relatedArtists.hasError) {
return Center( return Center(
child: PlatformText(relatedArtists.error.toString()), child: Text(relatedArtists.error.toString()),
); );
} }

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/desktop_login/login_form.dart'; import 'package:spotube/components/desktop_login/login_form.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart';
@ -15,10 +15,9 @@ class DesktopLoginPage extends HookConsumerWidget {
final breakpoint = useBreakpoints(); final breakpoint = useBreakpoints();
return SafeArea( return SafeArea(
child: PlatformScaffold( child: Scaffold(
appBar: PageWindowTitleBar( appBar: const PageWindowTitleBar(
leading: const PlatformBackButton(), leading: BackButton(),
hideWhenWindows: false,
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Center( child: Center(
@ -26,7 +25,7 @@ class DesktopLoginPage extends HookConsumerWidget {
margin: const EdgeInsets.all(10), margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: PlatformTheme.of(context).secondaryBackgroundColor, color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: Column( child: Column(
@ -35,11 +34,13 @@ class DesktopLoginPage extends HookConsumerWidget {
width: MediaQuery.of(context).size.width * width: MediaQuery.of(context).size.width *
(breakpoint <= Breakpoints.md ? .5 : .3), (breakpoint <= Breakpoints.md ? .5 : .3),
), ),
PlatformText.subheading( Text(
"Add your spotify credentials to get started", "Add your spotify credentials to get started",
style: Theme.of(context).textTheme.titleMedium,
), ),
PlatformText.label( Text(
"Don't worry, any of your credentials won't be collected or shared with anyone", "Don't worry, any of your credentials won't be collected or shared with anyone",
style: Theme.of(context).textTheme.labelMedium,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
TokenLoginForm( TokenLoginForm(
@ -50,9 +51,9 @@ class DesktopLoginPage extends HookConsumerWidget {
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
const PlatformText("Don't know how to do this?"), const Text("Don't know how to do this?"),
PlatformTextButton( TextButton(
child: const PlatformText( child: const Text(
"Follow along the Step by Step guide", "Follow along the Step by Step guide",
), ),
onPressed: () => GoRouter.of(context).push( onPressed: () => GoRouter.of(context).push(

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:introduction_screen/introduction_screen.dart'; import 'package:introduction_screen/introduction_screen.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/desktop_login/login_form.dart'; import 'package:spotube/components/desktop_login/login_form.dart';
import 'package:spotube/components/shared/links/hyper_link.dart'; import 'package:spotube/components/shared/links/hyper_link.dart';
@ -21,14 +21,13 @@ class LoginTutorial extends ConsumerWidget {
final key = GlobalKey<State<IntroductionScreen>>(); final key = GlobalKey<State<IntroductionScreen>>();
final pageDecoration = PageDecoration( final pageDecoration = PageDecoration(
bodyTextStyle: PlatformTheme.of(context).textTheme!.body!, bodyTextStyle: Theme.of(context).textTheme.bodyMedium!,
titleTextStyle: PlatformTheme.of(context).textTheme!.subheading!, titleTextStyle: Theme.of(context).textTheme.headlineMedium!,
); );
return PlatformScaffold( return Scaffold(
appBar: PageWindowTitleBar( appBar: PageWindowTitleBar(
hideWhenWindows: false, leading: TextButton(
leading: PlatformTextButton( child: const Text("Exit"),
child: const PlatformText("Exit"),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
@ -36,29 +35,27 @@ class LoginTutorial extends ConsumerWidget {
), ),
body: IntroductionScreen( body: IntroductionScreen(
key: key, key: key,
globalBackgroundColor: globalBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
PlatformTheme.of(context).scaffoldBackgroundColor, overrideBack: OutlinedButton(
overrideBack: PlatformFilledButton( child: const Center(child: Text("Previous")),
isSecondary: true,
child: const Center(child: PlatformText("Previous")),
onPressed: () { onPressed: () {
(key.currentState as IntroductionScreenState).previous(); (key.currentState as IntroductionScreenState).previous();
}, },
), ),
overrideNext: PlatformFilledButton( overrideNext: FilledButton(
child: const Center(child: PlatformText("Next")), child: const Center(child: Text("Next")),
onPressed: () { onPressed: () {
(key.currentState as IntroductionScreenState).next(); (key.currentState as IntroductionScreenState).next();
}, },
), ),
showBackButton: true, showBackButton: true,
overrideDone: PlatformFilledButton( overrideDone: FilledButton(
onPressed: authenticationNotifier.isLoggedIn onPressed: authenticationNotifier.isLoggedIn
? () { ? () {
ServiceUtils.navigate(context, "/"); ServiceUtils.navigate(context, "/");
} }
: null, : null,
child: const Center(child: PlatformText("Done")), child: const Center(child: Text("Done")),
), ),
pages: [ pages: [
PageViewModel( PageViewModel(
@ -67,14 +64,14 @@ class LoginTutorial extends ConsumerWidget {
image: Assets.tutorial.step1.image(), image: Assets.tutorial.step1.image(),
bodyWidget: Wrap( bodyWidget: Wrap(
children: const [ children: const [
PlatformText( Text(
"First, Go to ", "First, Go to ",
), ),
Hyperlink( Hyperlink(
"accounts.spotify.com ", "accounts.spotify.com ",
"https://accounts.spotify.com", "https://accounts.spotify.com",
), ),
PlatformText( Text(
"and Login/Sign up if you're not logged in", "and Login/Sign up if you're not logged in",
), ),
], ],
@ -84,7 +81,7 @@ class LoginTutorial extends ConsumerWidget {
decoration: pageDecoration, decoration: pageDecoration,
title: "Step 2", title: "Step 2",
image: Assets.tutorial.step2.image(), image: Assets.tutorial.step2.image(),
bodyWidget: const PlatformText( bodyWidget: const Text(
"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", "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, textAlign: TextAlign.left,
), ),
@ -93,7 +90,7 @@ class LoginTutorial extends ConsumerWidget {
decoration: pageDecoration, decoration: pageDecoration,
title: "Step 3", title: "Step 3",
image: Assets.tutorial.step3.image(), image: Assets.tutorial.step3.image(),
bodyWidget: const PlatformText( bodyWidget: const Text(
"Copy the values of \"sp_dc\" and \"sp_key\" Cookies", "Copy the values of \"sp_dc\" and \"sp_key\" Cookies",
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),
@ -114,8 +111,9 @@ class LoginTutorial extends ConsumerWidget {
title: "Step 5", title: "Step 5",
bodyWidget: Column( bodyWidget: Column(
children: [ children: [
PlatformText.label( Text(
"Paste the copied \"sp_dc\" and \"sp_key\" values in the respective fields", "Paste the copied \"sp_dc\" and \"sp_key\" values in the respective fields",
style: Theme.of(context).textTheme.labelMedium,
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
TokenLoginForm( TokenLoginForm(

View File

@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/pages/home/genres.dart'; import 'package:spotube/pages/home/genres.dart';
import 'package:spotube/pages/home/personalized.dart'; import 'package:spotube/pages/home/personalized.dart';
@ -12,41 +10,19 @@ class HomePage extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final index = useState(0); return DefaultTabController(
final tabbar = PlatformTabBar( length: 2,
androidIsScrollable: true, child: Scaffold(
selectedIndex: index.value, appBar: const PageWindowTitleBar(
onSelectedIndexChanged: (value) => index.value = value,
isNavigational:
PlatformProperty.byPlatformGroup(mobile: false, desktop: true),
tabs: [
PlatformTab(
label: 'Genres',
icon: PlatformProperty.only(
android: const SizedBox.shrink(),
other: const Icon(SpotubeIcons.genres),
).resolve(platform!),
),
PlatformTab(
label: 'Personalized',
icon: PlatformProperty.only(
android: const SizedBox.shrink(),
other: const Icon(SpotubeIcons.personalized),
).resolve(platform!),
),
],
);
return PlatformScaffold(
appBar: platform == TargetPlatform.windows
? PreferredSize(
preferredSize: const Size.fromHeight(40),
child: tabbar,
)
: PageWindowTitleBar(
titleWidth: 347, titleWidth: 347,
centerTitle: true, centerTitle: true,
center: tabbar, title: TabBar(
isScrollable: true,
tabs: [
Tab(text: 'Genres'),
Tab(text: 'Personalized'),
],
),
), ),
body: AnimatedSwitcher( body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
@ -59,10 +35,13 @@ class HomePage extends HookConsumerWidget {
), ),
child: child, child: child,
), ),
child: [ child: const TabBarView(
const GenrePage(), children: [
const PersonalizedPage(), GenrePage(),
][index.value], PersonalizedPage(),
],
),
),
), ),
); );
} }

View File

@ -2,7 +2,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' hide Page; import 'package:flutter/material.dart' hide Page;
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/album/album_card.dart'; import 'package:spotube/components/album/album_card.dart';
import 'package:spotube/components/playlist/playlist_card.dart'; import 'package:spotube/components/playlist/playlist_card.dart';
@ -53,7 +53,10 @@ class PersonalizedItemCard extends HookWidget {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Row( child: Row(
children: [ children: [
PlatformText.headline(title), Text(
title,
style: Theme.of(context).textTheme.titleLarge,
),
], ],
), ),
), ),

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart' hide Image; import 'package:flutter/material.dart' hide Image;
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/components/library/user_local_tracks.dart'; import 'package:spotube/components/library/user_local_tracks.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/components/library/user_albums.dart'; import 'package:spotube/components/library/user_albums.dart';
@ -13,43 +12,34 @@ class LibraryPage extends HookConsumerWidget {
const LibraryPage({Key? key}) : super(key: key); const LibraryPage({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final index = useState(0); return const SafeArea(
child: DefaultTabController(
final body = [ length: 5,
const UserPlaylists(), child: Scaffold(
const UserLocalTracks(), appBar: PageWindowTitleBar(
const UserDownloads(),
const UserArtists(),
const UserAlbums(),
][index.value];
final tabbar = 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: platform == TargetPlatform.windows
? PreferredSize(
preferredSize: const Size.fromHeight(40),
child: tabbar,
)
: PageWindowTitleBar(
titleWidth: 347, titleWidth: 347,
centerTitle: true, centerTitle: true,
center: tabbar, title: TabBar(
isScrollable: true,
tabs: [
Tab(text: 'Playlists'),
Tab(text: 'Tracks'),
Tab(text: 'Downloads'),
Tab(text: 'Artists'),
Tab(text: 'Albums'),
],
),
),
body: TabBarView(
children: [
UserPlaylists(),
UserLocalTracks(),
UserDownloads(),
UserArtists(),
UserAlbums(),
],
),
), ),
body: body,
), ),
); );
} }

View File

@ -3,7 +3,7 @@ import 'dart:ui';
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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
@ -31,7 +31,6 @@ class LyricsPage extends HookConsumerWidget {
[playlist?.activeTrack.album?.images], [playlist?.activeTrack.album?.images],
); );
final palette = usePaletteColor(albumArt, ref); final palette = usePaletteColor(albumArt, ref);
final index = useState(0);
useCustomStatusBarColor( useCustomStatusBarColor(
palette.color, palette.color,
@ -39,35 +38,18 @@ class LyricsPage extends HookConsumerWidget {
noSetBGColor: true, noSetBGColor: true,
); );
Widget body = [ const tabbar = TabBar(
SyncedLyrics(palette: palette, isModal: isModal), isScrollable: true,
PlainLyrics(palette: palette, isModal: isModal),
][index.value];
final tabbar = PreferredSize(
preferredSize: const Size.fromHeight(50),
child: PlatformTabBar(
isNavigational: PlatformProperty.only(linux: true, other: false),
selectedIndex: index.value,
onSelectedIndexChanged: (value) => index.value = value,
backgroundColor: PlatformTheme.of(context).scaffoldBackgroundColor,
tabs: [ tabs: [
PlatformTab( Tab(text: "Synced"),
label: "Synced", Tab(text: "Plain"),
icon: const SizedBox.shrink(),
color: PlatformTextTheme.of(context).caption?.color,
),
PlatformTab(
label: "Plain",
icon: const SizedBox.shrink(),
color: PlatformTextTheme.of(context).caption?.color,
),
], ],
),
); );
if (isModal) { if (isModal) {
return SafeArea( return DefaultTabController(
length: 2,
child: SafeArea(
child: Container( child: Container(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -90,37 +72,45 @@ class LyricsPage extends HookConsumerWidget {
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
PlatformAppBar( AppBar(
title: tabbar, title: tabbar,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
toolbarOpacity: platform == TargetPlatform.iOS ? 0 : 1, toolbarOpacity: 1,
actions: [ actions: [
PlatformIconButton( IconButton(
icon: const Icon(SpotubeIcons.minimize), icon: const Icon(SpotubeIcons.minimize),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
const SizedBox(width: 5), const SizedBox(width: 5),
], ],
), ),
Expanded(child: body), Expanded(
child: TabBarView(
children: [
SyncedLyrics(palette: palette, isModal: isModal),
PlainLyrics(palette: palette, isModal: isModal),
], ],
), ),
), ),
],
),
),
),
), ),
); );
} }
return PlatformScaffold( return DefaultTabController(
length: 2,
child: Scaffold(
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
appBar: !kIsMacOS appBar: !kIsMacOS
? (platform != TargetPlatform.windows && !isModal ? const PageWindowTitleBar(
? PageWindowTitleBar(
toolbarOpacity: 0, toolbarOpacity: 0,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
center: tabbar, title: tabbar,
) )
: tabbar) : tabbar as PreferredSizeWidget?,
: null,
body: Container( body: Container(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -134,7 +124,15 @@ class LyricsPage extends HookConsumerWidget {
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: ColoredBox( child: ColoredBox(
color: palette.color.withOpacity(.7), color: palette.color.withOpacity(.7),
child: SafeArea(child: body), child: SafeArea(
child: TabBarView(
children: [
SyncedLyrics(palette: palette, isModal: isModal),
PlainLyrics(palette: palette, isModal: isModal),
],
),
),
),
), ),
), ),
), ),

View File

@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.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:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/platform.dart';
@ -24,7 +24,7 @@ class WebViewLogin extends HookConsumerWidget {
); );
} }
return PlatformScaffold( return Scaffold(
body: SafeArea( body: SafeArea(
child: InAppWebView( child: InAppWebView(
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(

View File

@ -5,7 +5,7 @@ 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:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/player/player_actions.dart'; import 'package:spotube/components/player/player_actions.dart';
@ -65,20 +65,12 @@ class PlayerView extends HookConsumerWidget {
noSetBGColor: true, noSetBGColor: true,
); );
return PlatformScaffold( return Scaffold(
appBar: PageWindowTitleBar( appBar: PageWindowTitleBar(
hideWhenWindows: false,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
foregroundColor: paletteColor.titleTextColor, foregroundColor: paletteColor.titleTextColor,
toolbarOpacity: toolbarOpacity: 1,
PlatformProperty.only(android: 1.0, windows: 1.0, other: 0.0) leading: BackButton(color: paletteColor.titleTextColor),
.resolve(platform ?? Theme.of(context).platform),
leading: PlatformBackButton(
color: PlatformProperty.only(
macos: Colors.black,
other: paletteColor.titleTextColor,
).resolve(platform!),
),
), ),
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
body: Container( body: Container(
@ -91,7 +83,6 @@ class PlayerView extends HookConsumerWidget {
child: BackdropFilter( child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
child: Material( child: Material(
textStyle: PlatformTheme.of(context).textTheme!.body!,
color: paletteColor.color.withOpacity(.5), color: paletteColor.color.withOpacity(.5),
child: SafeArea( child: SafeArea(
child: Column( child: Column(
@ -104,8 +95,10 @@ class PlayerView extends HookConsumerWidget {
height: 30, height: 30,
child: SpotubeMarqueeText( child: SpotubeMarqueeText(
text: currentTrack?.name ?? "Not playing", text: currentTrack?.name ?? "Not playing",
style: style: Theme.of(context)
Theme.of(context).textTheme.headline5?.copyWith( .textTheme
.headlineSmall
?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: paletteColor.titleTextColor, color: paletteColor.titleTextColor,
), ),
@ -117,8 +110,10 @@ class PlayerView extends HookConsumerWidget {
TypeConversionUtils.artists_X_String<Artist>( TypeConversionUtils.artists_X_String<Artist>(
currentTrack?.artists ?? [], currentTrack?.artists ?? [],
), ),
style: style: Theme.of(context)
Theme.of(context).textTheme.headline6!.copyWith( .textTheme
.titleLarge!
.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: paletteColor.bodyTextColor, color: paletteColor.bodyTextColor,
), ),
@ -126,8 +121,10 @@ class PlayerView extends HookConsumerWidget {
else else
TypeConversionUtils.artists_X_ClickableArtists( TypeConversionUtils.artists_X_ClickableArtists(
currentTrack?.artists ?? [], currentTrack?.artists ?? [],
textStyle: textStyle: Theme.of(context)
Theme.of(context).textTheme.headline6!.copyWith( .textTheme
.titleLarge!
.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: paletteColor.bodyTextColor, color: paletteColor.bodyTextColor,
), ),
@ -193,7 +190,7 @@ class PlayerView extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
floatingQueue: false, floatingQueue: false,
extraActions: [ extraActions: [
PlatformIconButton( IconButton(
tooltip: "Open Lyrics", tooltip: "Open Lyrics",
icon: const Icon(SpotubeIcons.music), icon: const Icon(SpotubeIcons.music),
onPressed: () { onPressed: () {

View File

@ -3,12 +3,10 @@ import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.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/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';
@ -35,8 +33,8 @@ class RootApp extends HookConsumerWidget {
useEffect(() { useEffect(() {
downloader.onFileExists = (track) async { downloader.onFileExists = (track) async {
if (!isMounted()) return false; if (!isMounted()) return false;
return await showPlatformAlertDialog<bool>( return await showDialog<bool>(
context, context: context,
builder: (context) => ReplaceDownloadedDialog( builder: (context) => ReplaceDownloadedDialog(
track: track, track: track,
), ),
@ -49,7 +47,7 @@ class RootApp extends HookConsumerWidget {
// checks for latest version of the application // checks for latest version of the application
useUpdateChecker(ref); useUpdateChecker(ref);
final backgroundColor = PlatformTheme.of(context).scaffoldBackgroundColor!; final backgroundColor = Theme.of(context).scaffoldBackgroundColor;
useEffect(() { useEffect(() {
SystemChrome.setSystemUIOverlayStyle( SystemChrome.setSystemUIOverlayStyle(
@ -63,10 +61,7 @@ class RootApp extends HookConsumerWidget {
return null; return null;
}, [backgroundColor]); }, [backgroundColor]);
return PlatformScaffold( return Scaffold(
appBar: platform == TargetPlatform.windows
? PageWindowTitleBar(hideWhenWindows: false) as PreferredSizeWidget?
: null,
body: Sidebar( body: Sidebar(
selectedIndex: index.value, selectedIndex: index.value,
onSelectedIndexChanged: (i) { onSelectedIndexChanged: (i) {

View File

@ -4,7 +4,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' hide Page; import 'package:flutter/material.dart' hide Page;
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/album/album_card.dart'; import 'package:spotube/components/album/album_card.dart';
@ -62,7 +62,7 @@ class SearchPage extends HookConsumerWidget {
} }
return SafeArea( return SafeArea(
child: PlatformScaffold( child: Scaffold(
appBar: kIsDesktop && !kIsMacOS ? PageWindowTitleBar() : null, appBar: kIsDesktop && !kIsMacOS ? PageWindowTitleBar() : null,
body: !authenticationNotifier.isLoggedIn body: !authenticationNotifier.isLoggedIn
? const AnonymousFallback() ? const AnonymousFallback()
@ -73,15 +73,12 @@ class SearchPage extends HookConsumerWidget {
horizontal: 20, horizontal: 20,
vertical: 10, vertical: 10,
), ),
color: PlatformTheme.of(context).scaffoldBackgroundColor, color: Theme.of(context).scaffoldBackgroundColor,
child: PlatformTextField( child: TextField(
prefixIcon: SpotubeIcons.search, decoration: const InputDecoration(
prefixIconColor: PlatformProperty.only( prefixIcon: Icon(SpotubeIcons.search),
ios: hintText: "Search...",
PlatformTheme.of(context).textTheme?.caption?.color, ),
other: null,
).resolve(platform!),
placeholder: "Search...",
onSubmitted: (value) async { onSubmitted: (value) async {
ref.read(searchTermStateProvider.notifier).state = ref.read(searchTermStateProvider.notifier).state =
value; value;
@ -133,11 +130,15 @@ class SearchPage extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (tracks.isNotEmpty) if (tracks.isNotEmpty)
PlatformText.headline("Songs"), Text(
"Songs",
style:
Theme.of(context).textTheme.titleLarge!,
),
if (searchTrack.isLoadingPage) if (searchTrack.isLoadingPage)
const PlatformCircularProgressIndicator() const CircularProgressIndicator()
else if (searchTrack.hasPageError) else if (searchTrack.hasPageError)
PlatformText(searchTrack.errors.lastOrNull Text(searchTrack.errors.lastOrNull
?.toString() ?? ?.toString() ??
"") "")
else else
@ -182,17 +183,21 @@ class SearchPage extends HookConsumerWidget {
if (searchTrack.hasNextPage && if (searchTrack.hasNextPage &&
tracks.isNotEmpty) tracks.isNotEmpty)
Center( Center(
child: PlatformTextButton( child: TextButton(
onPressed: searchTrack.isRefreshingPage onPressed: searchTrack.isRefreshingPage
? null ? null
: () => searchTrack.fetchNext(), : () => searchTrack.fetchNext(),
child: searchTrack.isRefreshingPage child: searchTrack.isRefreshingPage
? const PlatformCircularProgressIndicator() ? const CircularProgressIndicator()
: const PlatformText("Load more"), : const Text("Load more"),
), ),
), ),
if (playlists.isNotEmpty) if (playlists.isNotEmpty)
PlatformText.headline("Playlists"), Text(
"Playlists",
style:
Theme.of(context).textTheme.titleLarge!,
),
const SizedBox(height: 10), const SizedBox(height: 10),
ScrollConfiguration( ScrollConfiguration(
behavior: behavior:
@ -236,16 +241,20 @@ class SearchPage extends HookConsumerWidget {
), ),
), ),
if (searchPlaylist.isLoadingPage) if (searchPlaylist.isLoadingPage)
const PlatformCircularProgressIndicator(), const CircularProgressIndicator(),
if (searchPlaylist.hasPageError) if (searchPlaylist.hasPageError)
PlatformText( Text(
searchPlaylist.errors.lastOrNull searchPlaylist.errors.lastOrNull
?.toString() ?? ?.toString() ??
"", "",
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
if (artists.isNotEmpty) if (artists.isNotEmpty)
PlatformText.headline("Artists"), Text(
"Artists",
style:
Theme.of(context).textTheme.titleLarge!,
),
const SizedBox(height: 10), const SizedBox(height: 10),
ScrollConfiguration( ScrollConfiguration(
behavior: behavior:
@ -289,16 +298,21 @@ class SearchPage extends HookConsumerWidget {
), ),
), ),
if (searchArtist.isLoadingPage) if (searchArtist.isLoadingPage)
const PlatformCircularProgressIndicator(), const CircularProgressIndicator(),
if (searchArtist.hasPageError) if (searchArtist.hasPageError)
PlatformText( Text(
searchArtist.errors.lastOrNull searchArtist.errors.lastOrNull
?.toString() ?? ?.toString() ??
"", "",
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
if (albums.isNotEmpty) if (albums.isNotEmpty)
PlatformText.subheading("Albums"), Text(
"Albums",
style: Theme.of(context)
.textTheme
.titleMedium!,
),
const SizedBox(height: 10), const SizedBox(height: 10),
ScrollConfiguration( ScrollConfiguration(
behavior: behavior:
@ -340,9 +354,9 @@ class SearchPage extends HookConsumerWidget {
), ),
), ),
if (searchAlbum.isLoadingPage) if (searchAlbum.isLoadingPage)
const PlatformCircularProgressIndicator(), const CircularProgressIndicator(),
if (searchAlbum.hasPageError) if (searchAlbum.hasPageError)
PlatformText( Text(
searchAlbum.errors.lastOrNull?.toString() ?? searchAlbum.errors.lastOrNull?.toString() ??
"", "",
), ),

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart';
@ -17,10 +16,10 @@ class AboutSpotube extends HookConsumerWidget {
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final packageInfo = usePackageInfo(); final packageInfo = usePackageInfo();
return PlatformScaffold( return Scaffold(
appBar: PageWindowTitleBar( appBar: PageWindowTitleBar(
leading: const PlatformBackButton(), leading: const BackButton(),
center: const PlatformText("About Spotube"), title: const Text("About Spotube"),
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Padding( child: Padding(
@ -35,14 +34,15 @@ class AboutSpotube extends HookConsumerWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
PlatformText.headline( Text(
"Spotube, a light-weight, cross-platform, free-for-all spotify client", "Spotube, a light-weight, cross-platform, free-for-all spotify client",
style: Theme.of(context).textTheme.titleLarge,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const PlatformText( const Text(
"Founder: Kingkor Roy Tirtho", "Founder: Kingkor Roy Tirtho",
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -60,11 +60,11 @@ class AboutSpotube extends HookConsumerWidget {
], ],
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
PlatformText( Text(
"Version: v${packageInfo.version}", "Version: v${packageInfo.version}",
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
PlatformText( Text(
"Build Number: ${packageInfo.buildNumber.replaceAll(".", " ")}", "Build Number: ${packageInfo.buildNumber.replaceAll(".", " ")}",
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
@ -75,7 +75,7 @@ class AboutSpotube extends HookConsumerWidget {
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
); );
}, },
child: const PlatformText( child: const Text(
"Repository: https://github.com/KRTirtho/spotube", "Repository: https://github.com/KRTirtho/spotube",
), ),
), ),
@ -87,7 +87,7 @@ class AboutSpotube extends HookConsumerWidget {
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
); );
}, },
child: const PlatformText( child: const Text(
"License: BSD-4-Clause", "License: BSD-4-Clause",
), ),
), ),
@ -99,7 +99,7 @@ class AboutSpotube extends HookConsumerWidget {
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
); );
}, },
child: const PlatformText( child: const Text(
"Bugs+Issues: https://github.com/KRTirtho/spotube/issues", "Bugs+Issues: https://github.com/KRTirtho/spotube/issues",
), ),
), ),
@ -178,21 +178,24 @@ class AboutSpotube extends HookConsumerWidget {
], ],
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
PlatformText.caption( Text(
"Made with ❤️ in Bangladesh🇧🇩", "Made with ❤️ in Bangladesh🇧🇩",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
), ),
PlatformText.caption( Text(
"© 2021-${DateTime.now().year} Kingkor Roy Tirtho", "© 2021-${DateTime.now().year} Kingkor Roy Tirtho",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
ConstrainedBox( ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 750), constraints: const BoxConstraints(maxWidth: 750),
child: SafeArea( child: SafeArea(
child: PlatformText.caption( child: Text(
licenseText, licenseText,
textAlign: TextAlign.justify, textAlign: TextAlign.justify,
style: Theme.of(context).textTheme.bodySmall,
), ),
), ),
), ),

View File

@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/provider/blacklist_provider.dart'; import 'package:spotube/provider/blacklist_provider.dart';
@ -35,23 +35,23 @@ class BlackListPage extends HookConsumerWidget {
[blacklist, searchText.value], [blacklist, searchText.value],
); );
return PlatformScaffold( return Scaffold(
appBar: PageWindowTitleBar( appBar: PageWindowTitleBar(
center: const PlatformText("Blacklist"), title: const Text("Blacklist"),
centerTitle: true, centerTitle: true,
leading: const PlatformBackButton(), leading: const BackButton(),
), ),
body: Column( body: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Padding( Padding(
padding: platform == TargetPlatform.windows padding: const EdgeInsets.all(8.0),
? const EdgeInsets.all(8.0).copyWith(left: 45) child: TextField(
: const EdgeInsets.all(8.0),
child: PlatformTextField(
onChanged: (value) => searchText.value = value, onChanged: (value) => searchText.value = value,
placeholder: "Search", decoration: const InputDecoration(
prefixIcon: SpotubeIcons.search, hintText: "Search",
prefixIcon: Icon(SpotubeIcons.search),
),
), ),
), ),
ListView.builder( ListView.builder(
@ -59,11 +59,11 @@ class BlackListPage extends HookConsumerWidget {
itemCount: filteredBlacklist.length, itemCount: filteredBlacklist.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = filteredBlacklist.elementAt(index); final item = filteredBlacklist.elementAt(index);
return PlatformListTile( return ListTile(
leading: PlatformText("${index + 1}."), leading: Text("${index + 1}."),
title: PlatformText("${item.name} (${item.type.name})"), title: Text("${item.name} (${item.type.name})"),
subtitle: PlatformText.caption(item.id), subtitle: Text(item.id),
trailing: PlatformIconButton( trailing: IconButton(
icon: Icon(SpotubeIcons.trash, color: Colors.red[400]), icon: Icon(SpotubeIcons.trash, color: Colors.red[400]),
onPressed: () { onPressed: () {
ref ref

View File

@ -4,12 +4,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart'; import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
import 'package:spotube/components/shared/adaptive/adaptive_list_tile.dart'; import 'package:spotube/components/shared/adaptive/adaptive_list_tile.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart'; import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/main.dart';
import 'package:spotube/collections/spotify_markets.dart'; import 'package:spotube/collections/spotify_markets.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences_provider.dart';
@ -24,7 +23,9 @@ class SettingsPage extends HookConsumerWidget {
final auth = ref.watch(AuthenticationNotifier.provider); final auth = ref.watch(AuthenticationNotifier.provider);
final pickColorScheme = useCallback((ColorSchemeType schemeType) { final pickColorScheme = useCallback((ColorSchemeType schemeType) {
return () => showPlatformAlertDialog(context, builder: (context) { return () => showDialog(
context: context,
builder: (context) {
return ColorSchemePickerDialog( return ColorSchemePickerDialog(
schemeType: schemeType, schemeType: schemeType,
); );
@ -40,9 +41,9 @@ class SettingsPage extends HookConsumerWidget {
}, [preferences.downloadLocation]); }, [preferences.downloadLocation]);
return SafeArea( return SafeArea(
child: PlatformScaffold( child: Scaffold(
appBar: PageWindowTitleBar( appBar: PageWindowTitleBar(
center: PlatformText.headline("Settings"), title: const Text("Settings"),
centerTitle: true, centerTitle: true,
), ),
body: Row( body: Row(
@ -53,17 +54,18 @@ class SettingsPage extends HookConsumerWidget {
constraints: const BoxConstraints(maxWidth: 1366), constraints: const BoxConstraints(maxWidth: 1366),
child: ListView( child: ListView(
children: [ children: [
PlatformText( Text(
" Account", " Account",
style: PlatformTextTheme.of(context) style: Theme.of(context)
.headline .textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
), ),
if (auth == null) if (auth == null)
AdaptiveListTile( AdaptiveListTile(
leading: Icon( leading: Icon(
SpotubeIcons.login, SpotubeIcons.login,
color: PlatformTheme.of(context).primaryColor, color: Theme.of(context).primaryColor,
), ),
title: SizedBox( title: SizedBox(
height: 50, height: 50,
@ -74,12 +76,12 @@ class SettingsPage extends HookConsumerWidget {
"Login with your Spotify account", "Login with your Spotify account",
maxLines: 1, maxLines: 1,
style: TextStyle( style: TextStyle(
color: PlatformTheme.of(context).primaryColor, color: Theme.of(context).primaryColor,
), ),
), ),
), ),
), ),
trailing: (context, update) => PlatformFilledButton( trailing: (context, update) => FilledButton(
onPressed: () { onPressed: () {
GoRouter.of(context).push("/login"); GoRouter.of(context).push("/login");
}, },
@ -90,15 +92,14 @@ class SettingsPage extends HookConsumerWidget {
), ),
), ),
), ),
child: PlatformText( child: Text("Connect with Spotify".toUpperCase()),
"Connect with Spotify".toUpperCase()),
), ),
) )
else else
Builder(builder: (context) { Builder(builder: (context) {
return PlatformListTile( return ListTile(
leading: const Icon(SpotubeIcons.logout), leading: const Icon(SpotubeIcons.logout),
title: SizedBox( title: const SizedBox(
height: 50, height: 50,
width: 180, width: 180,
child: Align( child: Align(
@ -106,11 +107,10 @@ class SettingsPage extends HookConsumerWidget {
child: AutoSizeText( child: AutoSizeText(
"Log out of this account", "Log out of this account",
maxLines: 1, maxLines: 1,
style: PlatformTextTheme.of(context).body,
), ),
), ),
), ),
trailing: PlatformFilledButton( trailing: FilledButton(
style: ButtonStyle( style: ButtonStyle(
backgroundColor: backgroundColor:
MaterialStateProperty.all(Colors.red), MaterialStateProperty.all(Colors.red),
@ -124,44 +124,40 @@ class SettingsPage extends HookConsumerWidget {
.logout(); .logout();
GoRouter.of(context).pop(); GoRouter.of(context).pop();
}, },
child: const PlatformText("Logout"), child: const Text("Logout"),
), ),
); );
}), }),
PlatformText( Text(
" Appearance", " Appearance",
style: PlatformTextTheme.of(context) style: Theme.of(context)
.headline .textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
), ),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon(SpotubeIcons.dashboard), leading: const Icon(SpotubeIcons.dashboard),
title: const PlatformText("Layout Mode"), title: const Text("Layout Mode"),
subtitle: const PlatformText( subtitle: const Text(
"Override responsive layout mode settings", "Override responsive layout mode settings",
), ),
trailing: (context, update) => trailing: (context, update) => DropdownMenu<LayoutMode>(
PlatformDropDownMenu<LayoutMode>( dropdownMenuEntries: const [
value: preferences.layoutMode, DropdownMenuEntry(
items: [
PlatformDropDownMenuItem(
value: LayoutMode.adaptive, value: LayoutMode.adaptive,
child: const PlatformText( label: "Adaptive",
"Adaptive",
), ),
), DropdownMenuEntry(
PlatformDropDownMenuItem(
value: LayoutMode.compact, value: LayoutMode.compact,
child: const PlatformText( label: "Compact",
"Compact",
), ),
), DropdownMenuEntry(
PlatformDropDownMenuItem(
value: LayoutMode.extended, value: LayoutMode.extended,
child: const PlatformText("Extended"), label: "Extended",
), ),
], ],
onChanged: (value) { initialSelection: preferences.layoutMode,
onSelected: (value) {
if (value != null) { if (value != null) {
preferences.setLayoutMode(value); preferences.setLayoutMode(value);
update?.call(() {}); update?.call(() {});
@ -171,25 +167,24 @@ class SettingsPage extends HookConsumerWidget {
), ),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon(SpotubeIcons.darkMode), leading: const Icon(SpotubeIcons.darkMode),
title: const PlatformText("Theme"), title: const Text("Theme"),
trailing: (context, update) => trailing: (context, update) => DropdownMenu<ThemeMode>(
PlatformDropDownMenu<ThemeMode>( initialSelection: preferences.themeMode,
value: preferences.themeMode, dropdownMenuEntries: const [
items: [ DropdownMenuEntry(
PlatformDropDownMenuItem(
value: ThemeMode.dark, value: ThemeMode.dark,
child: const PlatformText("Dark"), label: "Dark",
), ),
PlatformDropDownMenuItem( DropdownMenuEntry(
value: ThemeMode.light, value: ThemeMode.light,
child: const PlatformText("Light"), label: "Light",
), ),
PlatformDropDownMenuItem( DropdownMenuEntry(
value: ThemeMode.system, value: ThemeMode.system,
child: const PlatformText("System"), label: "System",
), ),
], ],
onChanged: (value) { onSelected: (value) {
if (value != null) { if (value != null) {
preferences.setThemeMode(value); preferences.setThemeMode(value);
update?.call(() {}); update?.call(() {});
@ -197,45 +192,9 @@ class SettingsPage extends HookConsumerWidget {
}, },
), ),
), ),
AdaptiveListTile( ListTile(
leading: const Icon(SpotubeIcons.platform),
title: const PlatformText("Mimic Platform"),
trailing: (context, update) =>
DropdownButton<TargetPlatform>(
value: Spotube.of(context).appPlatform,
items: const [
DropdownMenuItem(
value: TargetPlatform.android,
child: PlatformText("Android (Material You)"),
),
DropdownMenuItem(
value: TargetPlatform.iOS,
child: PlatformText("iOS (Cupertino)"),
),
DropdownMenuItem(
value: TargetPlatform.macOS,
child: PlatformText("macOS (Aqua)"),
),
DropdownMenuItem(
value: TargetPlatform.linux,
child: PlatformText("Linux (GTK+Libadwaita)"),
),
DropdownMenuItem(
value: TargetPlatform.windows,
child: PlatformText("Windows 11 (Fluent UI)"),
),
],
onChanged: (value) {
if (value != null) {
Spotube.of(context).changePlatform(value);
update?.call(() {});
}
},
),
),
PlatformListTile(
leading: const Icon(SpotubeIcons.palette), leading: const Icon(SpotubeIcons.palette),
title: const PlatformText("Accent Color"), title: const Text("Accent Color"),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
horizontal: 15, horizontal: 15,
vertical: 5, vertical: 5,
@ -247,41 +206,37 @@ class SettingsPage extends HookConsumerWidget {
), ),
onTap: pickColorScheme(ColorSchemeType.accent), onTap: pickColorScheme(ColorSchemeType.accent),
), ),
PlatformListTile( SwitchListTile(
leading: const Icon(SpotubeIcons.album), secondary: const Icon(SpotubeIcons.album),
title: const PlatformText("Rotating Album Art"), title: const Text("Rotating Album Art"),
trailing: PlatformSwitch(
value: preferences.rotatingAlbumArt, value: preferences.rotatingAlbumArt,
onChanged: (state) { onChanged: (state) {
preferences.setRotatingAlbumArt(state); preferences.setRotatingAlbumArt(state);
}, },
), ),
), Text(
PlatformText(
" Playback", " Playback",
style: PlatformTextTheme.of(context) style: Theme.of(context)
.headline .textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
), ),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon(SpotubeIcons.audioQuality), leading: const Icon(SpotubeIcons.audioQuality),
title: const PlatformText("Audio Quality"), title: const Text("Audio Quality"),
trailing: (context, update) => trailing: (context, update) => DropdownMenu<AudioQuality>(
PlatformDropDownMenu<AudioQuality>( initialSelection: preferences.audioQuality,
value: preferences.audioQuality, dropdownMenuEntries: const [
items: [ DropdownMenuEntry(
PlatformDropDownMenuItem(
value: AudioQuality.high, value: AudioQuality.high,
child: const PlatformText( label: "High",
"High",
), ),
), DropdownMenuEntry(
PlatformDropDownMenuItem(
value: AudioQuality.low, value: AudioQuality.low,
child: const PlatformText("Low"), label: "Low",
), ),
], ],
onChanged: (value) { onSelected: (value) {
if (value != null) { if (value != null) {
preferences.setAudioQuality(value); preferences.setAudioQuality(value);
update?.call(() {}); update?.call(() {});
@ -289,37 +244,31 @@ class SettingsPage extends HookConsumerWidget {
}, },
), ),
), ),
PlatformListTile( SwitchListTile(
leading: const Icon(SpotubeIcons.download), secondary: const Icon(SpotubeIcons.download),
title: const PlatformText( title: const Text("Pre download and play"),
"Pre download and play", subtitle: const Text(
),
subtitle: const PlatformText(
"Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)", "Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)",
), ),
trailing: PlatformSwitch(
value: preferences.predownload, value: preferences.predownload,
onChanged: (state) { onChanged: (state) {
preferences.setPredownload(state); preferences.setPredownload(state);
}, },
), ),
), SwitchListTile(
PlatformListTile( secondary: const Icon(SpotubeIcons.fastForward),
leading: const Icon(SpotubeIcons.fastForward), title: const Text(
title: const PlatformText(
"Skip non-music segments (SponsorBlock)", "Skip non-music segments (SponsorBlock)",
), ),
trailing: PlatformSwitch(
value: preferences.skipSponsorSegments, value: preferences.skipSponsorSegments,
onChanged: (state) { onChanged: (state) {
preferences.setSkipSponsorSegments(state); preferences.setSkipSponsorSegments(state);
}, },
), ),
), ListTile(
PlatformListTile(
leading: const Icon(SpotubeIcons.playlistRemove), leading: const Icon(SpotubeIcons.playlistRemove),
title: const PlatformText("Blacklist"), title: const Text("Blacklist"),
subtitle: const PlatformText( subtitle: const Text(
"Blacklisted tracks and artists", "Blacklisted tracks and artists",
), ),
onTap: () { onTap: () {
@ -327,31 +276,30 @@ class SettingsPage extends HookConsumerWidget {
}, },
trailing: const Icon(SpotubeIcons.angleRight), trailing: const Icon(SpotubeIcons.angleRight),
), ),
PlatformText( Text(
" Search", " Search",
style: PlatformTextTheme.of(context) style: Theme.of(context)
.headline .textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
), ),
AdaptiveListTile( AdaptiveListTile(
leading: const Icon(SpotubeIcons.shoppingBag), leading: const Icon(SpotubeIcons.shoppingBag),
title: const PlatformText("Market Place"), title: const Text("Market Place"),
subtitle: PlatformText.caption( subtitle: const Text("Recommendation Country"),
"Recommendation Country",
),
trailing: (context, update) => ConstrainedBox( trailing: (context, update) => ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 350), constraints: const BoxConstraints(maxWidth: 350),
child: PlatformDropDownMenu( child: DropdownMenu(
value: preferences.recommendationMarket, initialSelection: preferences.recommendationMarket,
items: spotifyMarkets dropdownMenuEntries: spotifyMarkets
.map( .map(
(country) => (PlatformDropDownMenuItem( (country) => DropdownMenuEntry(
value: country.first, value: country.first,
child: PlatformText(country.last), label: country.last,
)), ),
) )
.toList(), .toList(),
onChanged: (value) { onSelected: (value) {
if (value == null) return; if (value == null) return;
preferences.setRecommendationMarket( preferences.setRecommendationMarket(
value as String, value as String,
@ -361,37 +309,36 @@ class SettingsPage extends HookConsumerWidget {
), ),
), ),
), ),
PlatformText( Text(
" Downloads", " Downloads",
style: PlatformTextTheme.of(context) style: Theme.of(context)
.headline .textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
), ),
PlatformListTile( ListTile(
leading: const Icon(SpotubeIcons.download), leading: const Icon(SpotubeIcons.download),
title: const PlatformText("Download Location"), title: const Text("Download Location"),
subtitle: PlatformText(preferences.downloadLocation), subtitle: Text(preferences.downloadLocation),
trailing: PlatformFilledButton( trailing: FilledButton(
onPressed: pickDownloadLocation, onPressed: pickDownloadLocation,
child: const Icon(SpotubeIcons.folder), child: const Icon(SpotubeIcons.folder),
), ),
onTap: pickDownloadLocation, onTap: pickDownloadLocation,
), ),
PlatformListTile( SwitchListTile(
leading: const Icon(SpotubeIcons.lyrics), secondary: const Icon(SpotubeIcons.lyrics),
title: const PlatformText( title: const Text("Download lyrics along with the Track"),
"Download lyrics along with the Track"),
trailing: PlatformSwitch(
value: preferences.saveTrackLyrics, value: preferences.saveTrackLyrics,
onChanged: (state) { onChanged: (state) {
preferences.setSaveTrackLyrics(state); preferences.setSaveTrackLyrics(state);
}, },
), ),
), Text(
PlatformText(
" About", " About",
style: PlatformTextTheme.of(context) style: Theme.of(context)
.headline .textTheme
.headlineSmall
?.copyWith(fontWeight: FontWeight.bold), ?.copyWith(fontWeight: FontWeight.bold),
), ),
AdaptiveListTile( AdaptiveListTile(
@ -414,7 +361,7 @@ class SettingsPage extends HookConsumerWidget {
), ),
), ),
), ),
trailing: (context, update) => PlatformFilledButton( trailing: (context, update) => FilledButton(
style: ButtonStyle( style: ButtonStyle(
backgroundColor: backgroundColor:
MaterialStatePropertyAll(Colors.red[100]), MaterialStatePropertyAll(Colors.red[100]),
@ -434,23 +381,21 @@ class SettingsPage extends HookConsumerWidget {
children: const [ children: const [
Icon(SpotubeIcons.heart), Icon(SpotubeIcons.heart),
SizedBox(width: 5), SizedBox(width: 5),
PlatformText("Please Sponsor/Donate"), Text("Please Sponsor/Donate"),
], ],
), ),
), ),
), ),
PlatformListTile( SwitchListTile(
leading: const Icon(SpotubeIcons.update), secondary: const Icon(SpotubeIcons.update),
title: const PlatformText("Check for Update"), title: const Text("Check for Update"),
trailing: PlatformSwitch(
value: preferences.checkUpdate, value: preferences.checkUpdate,
onChanged: (checked) => onChanged: (checked) =>
preferences.setCheckUpdate(checked), preferences.setCheckUpdate(checked),
), ),
), ListTile(
PlatformListTile(
leading: const Icon(SpotubeIcons.info), leading: const Icon(SpotubeIcons.info),
title: const PlatformText("About Spotube"), title: const Text("About Spotube"),
trailing: const Icon(SpotubeIcons.angleRight), trailing: const Icon(SpotubeIcons.angleRight),
onTap: () { onTap: () {
GoRouter.of(context).push("/settings/about"); GoRouter.of(context).push("/settings/about");

View File

@ -1,120 +0,0 @@
import 'package:flutter/material.dart';
import 'package:spotube/extensions/theme.dart';
ThemeData darkTheme({
required MaterialColor accentMaterialColor,
required MaterialColor backgroundMaterialColor,
}) {
return ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
extensions: [
ShimmerColorTheme(
shimmerBackgroundColor: backgroundMaterialColor[700],
shimmerColor: backgroundMaterialColor[800],
)
],
primaryColor: accentMaterialColor,
splashFactory: NoSplash.splashFactory,
primarySwatch: accentMaterialColor,
backgroundColor: backgroundMaterialColor[900],
scaffoldBackgroundColor: backgroundMaterialColor[900],
dialogBackgroundColor: backgroundMaterialColor[800],
shadowColor: Colors.black26,
buttonTheme: ButtonThemeData(
buttonColor: accentMaterialColor,
),
iconTheme: IconThemeData(size: 16, color: Colors.grey[50]),
inputDecorationTheme: InputDecorationTheme(
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: BorderSide(
color: accentMaterialColor[400]!,
width: 2.0,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide: BorderSide(
color: backgroundMaterialColor[800]!,
),
),
),
navigationRailTheme: NavigationRailThemeData(
backgroundColor: backgroundMaterialColor[800],
unselectedIconTheme: IconThemeData(
color: Colors.grey[300],
opacity: 1,
size: 18,
),
selectedIconTheme: IconThemeData(
color: backgroundMaterialColor[850],
size: 18,
),
selectedLabelTextStyle: TextStyle(color: accentMaterialColor[300]),
unselectedLabelTextStyle: TextStyle(color: Colors.grey[300]),
indicatorColor: accentMaterialColor[300],
),
navigationBarTheme: NavigationBarThemeData(
backgroundColor: backgroundMaterialColor[900],
height: 45,
indicatorColor: accentMaterialColor[300],
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
iconTheme: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return IconThemeData(
color: Colors.grey[900],
size: 18,
);
}
return IconThemeData(
color: Colors.grey[300],
size: 18,
);
}),
),
cardTheme: CardTheme(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
color: backgroundMaterialColor[900],
elevation: 20,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
foregroundColor: accentMaterialColor[300],
textStyle: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
popupMenuTheme: PopupMenuThemeData(
color: backgroundMaterialColor[800],
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
),
dialogTheme: DialogTheme(backgroundColor: backgroundMaterialColor[900]),
cardColor: backgroundMaterialColor[800],
canvasColor: backgroundMaterialColor[800],
listTileTheme: const ListTileThemeData(horizontalTitleGap: 0),
checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return accentMaterialColor[500];
}
return null;
}),
),
tabBarTheme: TabBarTheme(
indicator: const BoxDecoration(color: Colors.transparent),
labelColor: accentMaterialColor[500],
unselectedLabelColor: Colors.white,
labelStyle: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
unselectedLabelStyle:
const TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
),
appBarTheme: AppBarTheme(
backgroundColor: backgroundMaterialColor[900],
),
);
}

View File

@ -1,122 +0,0 @@
import 'package:adwaita/adwaita.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:macos_ui/macos_ui.dart';
import 'package:spotube/extensions/theme.dart';
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
ThemeData theme(Color seed, Brightness brightness) {
final scheme = ColorScheme.fromSeed(
seedColor: seed,
shadow: Colors.black12,
brightness: brightness,
);
return ThemeData(
useMaterial3: true,
colorScheme: scheme,
listTileTheme: ListTileThemeData(
horizontalTitleGap: 0,
iconColor: scheme.onSurface,
),
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
),
iconTheme: IconThemeData(size: 16, color: scheme.onSurface),
navigationBarTheme: const NavigationBarThemeData(
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
height: 50,
iconTheme: MaterialStatePropertyAll(
IconThemeData(size: 18),
),
),
tabBarTheme: TabBarTheme(
indicatorSize: TabBarIndicatorSize.tab,
labelStyle: const TextStyle(fontWeight: FontWeight.w600),
labelColor: scheme.primary,
overlayColor: MaterialStateProperty.all(Colors.transparent),
indicator: BoxDecoration(
color: scheme.secondaryContainer,
borderRadius: BorderRadius.circular(15),
),
),
);
}
final windowsTheme = fluent_ui.ThemeData.light().copyWith(
iconTheme: const IconThemeData(size: 16),
buttonTheme: fluent_ui.ButtonThemeData(
iconButtonStyle: fluent_ui.ButtonStyle(
iconSize: fluent_ui.ButtonState.all(20),
),
),
);
final windowsDarkTheme = fluent_ui.ThemeData.dark().copyWith(
iconTheme: const IconThemeData(size: 16),
buttonTheme: fluent_ui.ButtonThemeData(
iconButtonStyle: fluent_ui.ButtonStyle(
iconSize: fluent_ui.ButtonState.all(20),
),
),
);
final macosTheme = MacosThemeData.light().copyWith(
pushButtonTheme: const PushButtonThemeData(
secondaryColor: Colors.white,
),
iconTheme: const MacosIconThemeData(size: 16),
typography: MacosTypography(color: Colors.grey[900]!),
);
final macosDarkTheme = MacosThemeData.dark().copyWith(
pushButtonTheme: const PushButtonThemeData(
secondaryColor: Colors.white,
),
iconTheme: const MacosIconThemeData(size: 16),
typography: MacosTypography(color: MacosColors.textColor),
);
const iosTheme = CupertinoThemeData(brightness: Brightness.light);
const iosDarkTheme = CupertinoThemeData(
brightness: Brightness.dark,
);
final linuxTheme = AdwaitaThemeData.light().copyWith(
primaryColor: const Color(0xFF3582e5),
iconTheme: const IconThemeData(size: 16),
extensions: [
ShimmerColorTheme(
shimmerBackgroundColor: Colors.grey[300],
shimmerColor: Colors.grey[400],
)
],
switchTheme: SwitchThemeData(
trackColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return const Color(0xFF3582e5);
}
return Colors.grey[300];
}),
),
colorScheme: const ColorScheme.light(
primary: Color(0xFF3582e5),
),
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: Color(0xFF3582e5),
),
listTileTheme: ListTileThemeData(
iconColor: Colors.grey[900],
horizontalTitleGap: 0,
),
);
final linuxDarkTheme = AdwaitaThemeData.dark().copyWith(
iconTheme: IconThemeData(size: 16, color: Colors.grey[50]),
extensions: [
ShimmerColorTheme(
shimmerBackgroundColor: Colors.grey[800],
shimmerColor: Colors.grey[900],
)
],
listTileTheme: ListTileThemeData(
iconColor: Colors.grey[50],
horizontalTitleGap: 0,
),
);

40
lib/themes/theme.dart Normal file
View File

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
ThemeData theme(Color seed, Brightness brightness) {
final scheme = ColorScheme.fromSeed(
seedColor: seed,
shadow: Colors.black12,
brightness: brightness,
);
return ThemeData(
useMaterial3: true,
colorScheme: scheme,
listTileTheme: ListTileThemeData(
horizontalTitleGap: 0,
iconColor: scheme.onSurface,
),
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
),
iconTheme: IconThemeData(size: 16, color: scheme.onSurface),
navigationBarTheme: const NavigationBarThemeData(
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
height: 50,
iconTheme: MaterialStatePropertyAll(
IconThemeData(size: 18),
),
),
tabBarTheme: TabBarTheme(
indicatorSize: TabBarIndicatorSize.tab,
labelStyle: const TextStyle(fontWeight: FontWeight.w600),
labelColor: scheme.primary,
overlayColor: MaterialStateProperty.all(Colors.transparent),
indicator: BoxDecoration(
color: scheme.secondaryContainer,
borderRadius: BorderRadius.circular(15),
),
),
);
}

View File

@ -10,7 +10,6 @@ import audio_session
import audioplayers_darwin import audioplayers_darwin
import catcher import catcher
import device_info_plus import device_info_plus
import macos_ui
import metadata_god import metadata_god
import package_info_plus import package_info_plus
import path_provider_foundation import path_provider_foundation
@ -27,7 +26,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin")) CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin"))
MetadataGodPlugin.register(with: registry.registrar(forPlugin: "MetadataGodPlugin")) MetadataGodPlugin.register(with: registry.registrar(forPlugin: "MetadataGodPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))

View File

@ -9,14 +9,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "52.0.0" version: "52.0.0"
adwaita:
dependency: "direct main"
description:
name: adwaita
sha256: "535781747357779fa2830815e0a1b6b7888c8c538194ba73ac4fc8d82412baec"
url: "https://pub.dev"
source: hosted
version: "0.5.2"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
@ -546,14 +538,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0-alpha.1" version: "1.0.0-alpha.1"
fluent_ui:
dependency: "direct main"
description:
name: fluent_ui
sha256: ff172e78492faad2c9eca01b24ea8341f2bd6e1ce6986b15d4886fd2e7c3fb09
url: "https://pub.dev"
source: hosted
version: "4.3.0"
fluentui_system_icons: fluentui_system_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -671,11 +655,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_mailer: flutter_mailer:
dependency: transitive dependency: transitive
description: description:
@ -782,14 +761,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
gsettings:
dependency: transitive
description:
name: gsettings
sha256: fe90d719e09a6f36607021047e642068a0c98839d9633db00b91633420ae8b0d
url: "https://pub.dev"
source: hosted
version: "0.2.7"
hive: hive:
dependency: "direct main" dependency: "direct main"
description: description:
@ -910,22 +881,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.6.1" version: "6.6.1"
libadwaita:
dependency: "direct main"
description:
name: libadwaita
sha256: de4dba194e6ba468af70988891164cc616126dc7ccbc4f42756851be93aa6de6
url: "https://pub.dev"
source: hosted
version: "1.2.5"
libadwaita_core:
dependency: transitive
description:
name: libadwaita_core
sha256: "1a7689e4130e96be4b0ec615863fded25cd86c2b95a682e7cec9659d420de37c"
url: "https://pub.dev"
source: hosted
version: "0.5.4"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -950,14 +905,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
macos_ui:
dependency: "direct main"
description:
name: macos_ui
sha256: "2b31858b56d44e807cf192c58cf6660b584c997f4746496d4892a903c1c27a49"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
mailer: mailer:
dependency: transitive dependency: transitive
description: description:
@ -1206,15 +1153,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.0"
platform_ui:
dependency: "direct main"
description:
path: "."
ref: "339ea7922d2f881d9a72b565aeebc7e2c4b22509"
resolved-ref: "339ea7922d2f881d9a72b565aeebc7e2c4b22509"
url: "https://github.com/KRTirtho/platform_ui.git"
source: git
version: "0.1.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1255,14 +1193,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.8+1" version: "0.2.8+1"
popover_gtk:
dependency: transitive
description:
name: popover_gtk
sha256: c293bfa3bcb9436fe189f20f6397199ce996ba40d4b081e26793adcf3ceac45b
url: "https://pub.dev"
source: hosted
version: "0.2.6+3"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -1327,14 +1257,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.1" version: "3.2.1"
recase:
dependency: transitive
description:
name: recase
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
url: "https://pub.dev"
source: hosted
version: "4.1.0"
riverpod: riverpod:
dependency: transitive dependency: transitive
description: description:
@ -1359,14 +1281,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.6" version: "0.1.6"
scroll_pos:
dependency: transitive
description:
name: scroll_pos
sha256: cfca311b6b8d51538ff90e206fbe6ce3b36e7125ea6da4a40eb626c7f9f083b1
url: "https://pub.dev"
source: hosted
version: "0.3.0"
scroll_to_index: scroll_to_index:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1808,4 +1722,4 @@ packages:
version: "1.12.3" version: "1.12.3"
sdks: sdks:
dart: ">=2.19.0 <3.0.0" dart: ">=2.19.0 <3.0.0"
flutter: ">=3.7.0" flutter: ">=3.3.0"

View File

@ -11,7 +11,6 @@ environment:
sdk: ">=2.17.0 <3.0.0" sdk: ">=2.17.0 <3.0.0"
dependencies: dependencies:
adwaita: ^0.5.2
args: ^2.3.2 args: ^2.3.2
async: ^2.9.0 async: ^2.9.0
audio_service: ^0.18.9 audio_service: ^0.18.9
@ -29,7 +28,6 @@ dependencies:
file_picker: ^5.2.2 file_picker: ^5.2.2
fl_query: ^1.0.0-alpha.0 fl_query: ^1.0.0-alpha.0
fl_query_hooks: ^1.0.0-alpha.0 fl_query_hooks: ^1.0.0-alpha.0
fluent_ui: ^4.3.0
fluentui_system_icons: ^1.1.189 fluentui_system_icons: ^1.1.189
flutter: flutter:
sdk: flutter sdk: flutter
@ -50,9 +48,7 @@ dependencies:
introduction_screen: ^3.0.2 introduction_screen: ^3.0.2
json_annotation: ^4.8.0 json_annotation: ^4.8.0
json_serializable: ^6.6.0 json_serializable: ^6.6.0
libadwaita: ^1.2.5
logger: ^1.1.0 logger: ^1.1.0
macos_ui: ^1.9.0
marquee: ^2.2.3 marquee: ^2.2.3
metadata_god: ^0.3.2 metadata_god: ^0.3.2
mime: ^1.0.2 mime: ^1.0.2
@ -61,10 +57,6 @@ dependencies:
path: ^1.8.0 path: ^1.8.0
path_provider: ^2.0.8 path_provider: ^2.0.8
permission_handler: ^10.2.0 permission_handler: ^10.2.0
platform_ui:
git:
url: https://github.com/KRTirtho/platform_ui.git
ref: 339ea7922d2f881d9a72b565aeebc7e2c4b22509
pocketbase: ^0.7.1+1 pocketbase: ^0.7.1+1
popover: ^0.2.6+3 popover: ^0.2.6+3
queue: ^3.1.0+1 queue: ^3.1.0+1