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_feather_icons/flutter_feather_icons.dart';
abstract class SpotubeIcons {
static const home = FluentIcons.home;
static const home = FluentIcons.home_12_regular;
static const search = FeatherIcons.search;
static const library = FluentIcons.library;
static const library = FluentIcons.library_16_regular;
static const music = FeatherIcons.music;
static const play = FluentIcons.play;
static const play = FluentIcons.play_12_regular;
static const pause = FeatherIcons.pause;
static const skipForward = FeatherIcons.skipForward;
static const skipBack = FeatherIcons.skipBack;
@ -16,8 +16,8 @@ abstract class SpotubeIcons {
static const refresh = FeatherIcons.refreshCw;
static const settings = FeatherIcons.settings;
static const shuffle = FeatherIcons.shuffle;
static const repeat = FluentIcons.repeat_all;
static const repeatOne = FluentIcons.repeat_one;
static const repeat = FluentIcons.arrow_repeat_all_16_regular;
static const repeatOne = Icons.repeat_one_rounded;
static const remove = FeatherIcons.minus;
static const removeFilled = FeatherIcons.minusCircle;
static const add = FeatherIcons.plus;

View File

@ -1,12 +1,10 @@
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:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/hover_builder.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/utils/service_utils.dart';
import 'package:spotube/utils/type_conversion_utils.dart';
@ -30,75 +28,35 @@ 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(
height: 240,
width: 200,
child: InkWell(
splashFactory: splash,
onTap: () {
ServiceUtils.navigate(context, "/artist/${artist.id}");
},
customBorder: platform == TargetPlatform.windows
? Border.all(
color: FluentTheme.maybeOf(context)
?.micaBackgroundColor
.withOpacity(.7) ??
Colors.transparent,
width: 1,
)
: null,
borderRadius: BorderRadius.circular(
platform == TargetPlatform.windows ? 5 : 8,
),
borderRadius: BorderRadius.circular(8),
child: HoverBuilder(builder: (context, isHovering) {
return Ink(
width: 200,
decoration: BoxDecoration(
color: PlatformTheme.of(context).secondaryBackgroundColor,
borderRadius: BorderRadius.circular(
platform == TargetPlatform.windows ? 5 : 8,
),
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(8),
boxShadow: [
if (boxShadow != null) boxShadow,
BoxShadow(
blurRadius: 10,
offset: const Offset(0, 3),
spreadRadius: 5,
color: Theme.of(context).colorScheme.shadow,
),
],
border: isBlackListed
? Border.all(
color: Colors.red[400]!,
width: 2,
)
: [TargetPlatform.windows, TargetPlatform.macOS]
.contains(platform)
? Border.all(
color: PlatformTheme.of(context).borderColor ??
Colors.transparent,
width: 1,
)
: null,
: null,
),
child: Padding(
padding: const EdgeInsets.all(15),
@ -138,7 +96,7 @@ class ArtistCard extends HookConsumerWidget {
artist.name!,
maxLines: 2,
textAlign: TextAlign.center,
style: PlatformTextTheme.of(context).body?.copyWith(
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/provider/authentication_provider.dart';
class TokenLoginForm extends HookConsumerWidget {
@ -25,27 +25,31 @@ class TokenLoginForm extends HookConsumerWidget {
),
child: Column(
children: [
PlatformTextField(
TextField(
controller: directCodeController,
placeholder: "Spotify \"sp_dc\" Cookie",
label: "sp_dc Cookie",
decoration: const InputDecoration(
hintText: "Spotify \"sp_dc\" Cookie",
labelText: "sp_dc Cookie",
),
keyboardType: TextInputType.visiblePassword,
),
const SizedBox(height: 10),
PlatformTextField(
TextField(
controller: keyCodeController,
placeholder: "Spotify \"sp_key\" Cookie",
label: "sp_key Cookie",
decoration: const InputDecoration(
hintText: "Spotify \"sp_key\" Cookie",
labelText: "sp_key Cookie",
),
keyboardType: TextInputType.visiblePassword,
),
const SizedBox(height: 20),
PlatformFilledButton(
FilledButton(
onPressed: () async {
if (keyCodeController.text.isEmpty ||
directCodeController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: PlatformText("Please fill in all fields"),
content: Text("Please fill in all fields"),
behavior: SnackBarBehavior.floating,
),
);
@ -61,7 +65,7 @@ class TokenLoginForm extends HookConsumerWidget {
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_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
import 'package:spotube/components/shared/waypoint.dart';
@ -39,13 +39,15 @@ class CategoryCard extends HookConsumerWidget {
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
PlatformText.headline(category.name ?? "Unknown"),
Text(
category.name ?? "Unknown",
style: Theme.of(context).textTheme.titleLarge,
),
],
),
),
playlistQuery.hasPageError && !playlistQuery.hasPageData
? PlatformText(
"Something Went Wrong\n${playlistQuery.errors.first}")
? Text("Something Went Wrong\n${playlistQuery.errors.first}")
: SizedBox(
height: 245,
child: ScrollConfiguration(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,9 @@
import 'package:fl_query_hooks/fl_query_hooks.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/provider/spotify_provider.dart';
class PlaylistCreateDialog extends HookConsumerWidget {
@ -17,10 +15,10 @@ class PlaylistCreateDialog extends HookConsumerWidget {
return SizedBox(
width: 200,
child: PlatformTextButton(
child: TextButton(
onPressed: () {
showPlatformAlertDialog(
context,
showDialog(
context: context,
builder: (context) {
return HookBuilder(builder: (context) {
final playlistName = useTextEditingController();
@ -48,48 +46,18 @@ class PlaylistCreateDialog extends HookConsumerWidget {
navigator.pop();
}
return PlatformAlertDialog(
macosAppIcon: Sidebar.brandLogo(),
title: const PlatformText("Create a Playlist"),
primaryActions: [
PlatformBuilder(
fallback: PlatformBuilderFallback.android,
android: (context, _) {
return PlatformFilledButton(
onPressed: onCreate,
child: const Text("Create"),
);
},
ios: (context, data) {
return CupertinoDialogAction(
isDefaultAction: true,
onPressed: onCreate,
child: const Text("Create"),
);
return AlertDialog(
title: const Text("Create a Playlist"),
actions: [
OutlinedButton(
child: const Text("Cancel"),
onPressed: () {
Navigator.pop(context);
},
),
],
secondaryActions: [
PlatformBuilder(
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"),
);
},
FilledButton(
onPressed: onCreate,
child: const Text("Create"),
),
],
content: Container(
@ -98,28 +66,32 @@ class PlaylistCreateDialog extends HookConsumerWidget {
child: ListView(
shrinkWrap: true,
children: [
PlatformTextField(
TextField(
controller: playlistName,
placeholder: "Name of the playlist",
label: "Playlist Name",
decoration: const InputDecoration(
hintText: "Name of the playlist",
labelText: "Playlist Name",
),
),
const SizedBox(height: 10),
PlatformTextField(
TextField(
controller: description,
placeholder: "Description...",
decoration: const InputDecoration(
hintText: "Description...",
),
keyboardType: TextInputType.multiline,
maxLines: 5,
),
const SizedBox(height: 10),
PlatformCheckbox(
CheckboxListTile(
title: const Text("Public"),
value: public.value,
label: const PlatformText("Public"),
onChanged: (val) => public.value = val ?? false,
),
const SizedBox(height: 10),
PlatformCheckbox(
CheckboxListTile(
title: const Text("Collaborative"),
value: collaborative.value,
label: const PlatformText("Collaborative"),
onChanged: (val) =>
collaborative.value = val ?? false,
),
@ -141,7 +113,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: const [
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_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:macos_ui/macos_ui.dart';
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/player/player_actions.dart';
import 'package:spotube/components/player/player_overlay.dart';
import 'package:spotube/components/player/player_track_details.dart';
import 'package:spotube/components/player/player_controls.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/hooks/use_platform_property.dart';
import 'package:spotube/models/logger.dart';
import 'package:flutter/material.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(
decoration: BoxDecoration(
color: backgroundColor,
border: border,
color: Theme.of(context).colorScheme.background,
),
child: Material(
type: MaterialType.transparency,
textStyle: PlatformTheme.of(context).textTheme!.body!,
textStyle: Theme.of(context).textTheme.bodyMedium!,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
@ -141,7 +99,7 @@ class BottomPlayer extends HookConsumerWidget {
}
}
},
child: PlatformSlider(
child: Slider(
min: 0,
max: 1,
value: volume.value,

View File

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

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/side_bar_tiles.dart';
import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
@ -37,19 +37,30 @@ class SpotubeNavigationBar extends HookConsumerWidget {
if (layoutMode == LayoutMode.extended ||
(breakpoint.isMoreThan(Breakpoints.sm) &&
layoutMode == LayoutMode.adaptive)) return const SizedBox();
return PlatformBottomNavigationBar(
return BottomNavigationBar(
items: [
...navbarTileList.map(
(e) {
return PlatformBottomNavigationBarItem(
icon: e.icon,
return BottomNavigationBarItem(
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,
);
},
),
],
selectedIndex: insideSelectedIndex.value,
onSelectedIndexChanged: (i) {
currentIndex: insideSelectedIndex.value,
onTap: (i) {
insideSelectedIndex.value = i;
if (navbarTileList[i].title == "Settings") {
Sidebar.goToSettings(context);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,13 @@
import 'package:flutter/cupertino.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/root/sidebar.dart';
class ConfirmDownloadDialog extends StatelessWidget {
const ConfirmDownloadDialog({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return PlatformAlertDialog(
macosAppIcon: Sidebar.brandLogo(),
return AlertDialog(
title: Padding(
padding: const EdgeInsets.all(15),
child: Row(
@ -62,48 +59,21 @@ class ConfirmDownloadDialog extends StatelessWidget {
),
),
),
primaryActions: [
PlatformBuilder(
android: (context, _) {
return PlatformFilledButton(
style: const ButtonStyle(
foregroundColor: MaterialStatePropertyAll(Colors.white),
backgroundColor: MaterialStatePropertyAll(Colors.red),
),
onPressed: () => Navigator.of(context).pop(true),
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"),
);
actions: [
OutlinedButton(
child: const Text("Decline"),
onPressed: () {
Navigator.pop(context, false);
},
),
FilledButton(
style: FilledButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.red,
),
onPressed: () => Navigator.of(context).pop(true),
child: const Text("Accept"),
),
],
);
}

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/utils/service_utils.dart';
@ -20,10 +20,10 @@ class AnonymousFallback extends ConsumerWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const PlatformText("You're not logged in"),
const Text("You're not logged in"),
const SizedBox(height: 10),
PlatformFilledButton(
child: const PlatformText("Login with Spotify"),
FilledButton(
child: const Text("Login with Spotify"),
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_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/hooks/use_palette_color.dart';
@ -34,7 +34,7 @@ class HeartButton extends ConsumerWidget {
if (auth == null) return Container();
return PlatformIconButton(
return IconButton(
tooltip: tooltip,
icon: Icon(
icon ?? (!isLiked ? SpotubeIcons.heart : SpotubeIcons.heartFilled),
@ -95,7 +95,7 @@ class TrackHeartButton extends HookConsumerWidget {
useQueries.playlist.tracksOfQuery(ref, "user-liked-tracks");
final toggler = useTrackToggleLike(track, ref);
if (toggler.item3.isLoading || !toggler.item3.hasData) {
return const PlatformCircularProgressIndicator();
return const CircularProgressIndicator();
}
return HeartButton(
@ -150,7 +150,7 @@ class PlaylistHeartButton extends HookConsumerWidget {
).dominantColor;
if (me.isLoading || !me.hasData) {
return const PlatformCircularProgressIndicator();
return const CircularProgressIndicator();
}
return HeartButton(
@ -193,7 +193,7 @@ class AlbumHeartButton extends HookConsumerWidget {
);
if (me.isLoading || !me.hasData) {
return const PlatformCircularProgressIndicator();
return const CircularProgressIndicator();
}
return HeartButton(

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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/extensions/theme.dart';
@ -8,7 +8,7 @@ class ShimmerCategories extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isDark = PlatformTheme.of(context).brightness == Brightness.dark;
final isDark = Theme.of(context).brightness == Brightness.dark;
final shimmerTheme = ShimmerColorTheme(
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,27 +1,5 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:go_router/go_router.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:flutter/material.dart';
class SpotubePage extends CustomTransitionPage {
SpotubePage({
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);
}
class SpotubePage<T> extends MaterialPage<T> {
const SpotubePage({required super.child});
}

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:platform_ui/platform_ui.dart';
void useCustomStatusBarColor(
Color color,
@ -9,7 +8,7 @@ void useCustomStatusBarColor(
bool noSetBGColor = false,
}) {
final context = useContext();
final backgroundColor = PlatformTheme.of(context).scaffoldBackgroundColor!;
final backgroundColor = Theme.of(context).scaffoldBackgroundColor!;
resetStatusbar() => SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
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_riverpod/flutter_riverpod.dart';
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/hooks/use_package_info.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)) return;
if (latestVersion <= currentVersion) return;
showPlatformAlertDialog(
context,
showDialog(
context: context,
barrierDismissible: true,
barrierColor: Colors.black26,
builder: (context) {
const url =
"https://spotube.netlify.app/other-downloads/stable-downloads";
return PlatformAlertDialog(
macosAppIcon: Sidebar.brandLogo(),
title: const PlatformText("Spotube has an update"),
primaryActions: [
PlatformFilledButton(
return AlertDialog(
title: const Text("Spotube has an update"),
actions: [
FilledButton(
child: const Text("Download Now"),
onPressed: () => download(url),
),
@ -79,7 +77,7 @@ void useUpdateChecker(WidgetRef ref) {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const PlatformText("Read the latest "),
const Text("Read the latest "),
AnchorButton(
"release notes",
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:hooks_riverpod/hooks_riverpod.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:spotube/collections/cache_keys.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/pocketbase.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:window_manager/window_manager.dart';
import 'package:window_size/window_size.dart';
@ -157,8 +156,8 @@ void main(List<String> rawArgs) async {
logger.v(
"[onFileExists] download confirmation for ${track.name}",
);
return showPlatformAlertDialog<bool>(
context,
return showDialog<bool>(
context: context,
builder: (_) =>
ReplaceDownloadedDialog(track: track),
).then((s) => s ?? false);
@ -206,14 +205,6 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
super.initState();
SharedPreferences.getInstance().then(((value) => localStorage = value));
WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
appPlatform = Theme.of(context).platform;
if (appPlatform == TargetPlatform.macOS) {
appPlatform = TargetPlatform.android;
}
});
});
}
@override
@ -243,16 +234,6 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
prevSize = size;
}
TargetPlatform appPlatform = TargetPlatform.android;
void changePlatform(TargetPlatform targetPlatform) {
appPlatform = targetPlatform;
if (appPlatform == TargetPlatform.macOS) {
appPlatform = TargetPlatform.android;
}
setState(() {});
}
@override
Widget build(BuildContext context) {
final themeMode =
@ -268,9 +249,7 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
};
}, []);
platform = appPlatform;
return PlatformApp.router(
return MaterialApp.router(
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
routeInformationProvider: router.routeInformationProvider,
@ -279,17 +258,10 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
builder: (context, 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,
shortcuts: PlatformProperty.all({
theme: theme(accentMaterialColor, Brightness.light),
darkTheme: theme(accentMaterialColor, Brightness.dark),
shortcuts: {
...WidgetsApp.defaultShortcuts.map((key, value) {
return MapEntry(
LogicalKeySet.fromSet(key.triggers?.toSet() ?? {}),
@ -324,14 +296,14 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
LogicalKeyboardKey.control,
LogicalKeyboardKey.shift,
): CloseAppIntent(),
}),
actions: PlatformProperty.all({
},
actions: {
...WidgetsApp.defaultActions,
PlayPauseIntent: PlayPauseAction(),
NavigationIntent: NavigationAction(),
HomeTabIntent: HomeTabAction(),
CloseAppIntent: CloseAppAction(),
}),
},
);
}
}

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/shimmers/shimmer_artist_profile.dart';
@ -33,13 +33,13 @@ class ArtistPage extends HookConsumerWidget {
Widget build(BuildContext context, ref) {
SpotifyApi spotify = ref.watch(spotifyProvider);
final parentScrollController = useScrollController();
final textTheme = PlatformTheme.of(context).textTheme;
final textTheme = Theme.of(context).textTheme;
final chipTextVariant = useBreakpointValue(
sm: textTheme!.caption,
md: textTheme.body,
lg: textTheme.subheading,
xl: textTheme.headline,
xxl: textTheme.headline,
sm: textTheme.bodySmall,
md: textTheme.bodyMedium,
lg: textTheme.bodyLarge,
xl: textTheme.titleSmall,
xxl: textTheme.titleMedium,
);
final avatarWidth = useBreakpointValue(
@ -58,9 +58,9 @@ class ArtistPage extends HookConsumerWidget {
final auth = ref.watch(AuthenticationNotifier.provider);
return SafeArea(
child: PlatformScaffold(
appBar: PageWindowTitleBar(
leading: const PlatformBackButton(),
child: Scaffold(
appBar: const PageWindowTitleBar(
leading: BackButton(),
),
body: HookBuilder(
builder: (context) {
@ -70,7 +70,7 @@ class ArtistPage extends HookConsumerWidget {
return const ShimmerArtistProfile();
} else if (artistsQuery.hasError) {
return Center(
child: PlatformText(artistsQuery.error.toString()),
child: Text(artistsQuery.error.toString()),
);
}
@ -116,7 +116,7 @@ class ArtistPage extends HookConsumerWidget {
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(50)),
child: PlatformText(
child: Text(
data.type!.toUpperCase(),
style: chipTextVariant?.copyWith(
color: Colors.white,
@ -132,7 +132,7 @@ class ArtistPage extends HookConsumerWidget {
color: Colors.red[400],
borderRadius:
BorderRadius.circular(50)),
child: PlatformText(
child: Text(
"Blacklisted",
style: chipTextVariant?.copyWith(
color: Colors.white,
@ -142,18 +142,18 @@ class ArtistPage extends HookConsumerWidget {
]
],
),
PlatformText(
Text(
data.name!,
style: breakpoint.isSm
? textTheme.subheading
: textTheme.headline,
? textTheme.headlineSmall
: textTheme.headlineMedium,
),
PlatformText(
Text(
"${PrimitiveUtils.toReadableNumber(data.followers!.total!.toDouble())} followers",
style: breakpoint.isSm
? textTheme.body
: textTheme.body
?.copyWith(fontWeight: FontWeight.bold),
style: textTheme.bodyMedium?.copyWith(
fontWeight:
breakpoint.isSm ? null : FontWeight.bold,
),
),
const SizedBox(height: 20),
Row(
@ -170,14 +170,13 @@ class ArtistPage extends HookConsumerWidget {
return const SizedBox(
height: 20,
width: 20,
child:
PlatformCircularProgressIndicator(),
child: CircularProgressIndicator(),
);
}
final queryBowl = QueryClient.of(context);
return PlatformFilledButton(
return FilledButton(
onPressed: () async {
try {
isFollowingQuery.data!
@ -199,7 +198,7 @@ class ArtistPage extends HookConsumerWidget {
"user-follows-artists-query/$artistId");
}
},
child: PlatformText(
child: Text(
isFollowingQuery.data!
? "Following"
: "Follow",
@ -208,7 +207,7 @@ class ArtistPage extends HookConsumerWidget {
},
),
const SizedBox(width: 5),
PlatformIconButton(
IconButton(
tooltip: "Add to blacklisted artists",
icon: Icon(
SpotubeIcons.userRemove,
@ -216,8 +215,10 @@ class ArtistPage extends HookConsumerWidget {
? Colors.red[400]
: Colors.white,
),
backgroundColor:
isBlackListed ? Colors.red[400] : null,
style: IconButton.styleFrom(
backgroundColor:
isBlackListed ? Colors.red[400] : null,
),
onPressed: () async {
if (isBlackListed) {
ref
@ -238,7 +239,7 @@ class ArtistPage extends HookConsumerWidget {
}
},
),
PlatformIconButton(
IconButton(
icon: const Icon(SpotubeIcons.share),
onPressed: () async {
await Clipboard.setData(
@ -250,7 +251,7 @@ class ArtistPage extends HookConsumerWidget {
const SnackBar(
width: 300,
behavior: SnackBarBehavior.floating,
content: PlatformText(
content: Text(
"Artist URL copied to clipboard",
textAlign: TextAlign.center,
),
@ -279,10 +280,10 @@ class ArtistPage extends HookConsumerWidget {
);
if (topTracksQuery.isLoading || !topTracksQuery.hasData) {
return const PlatformCircularProgressIndicator();
return const CircularProgressIndicator();
} else if (topTracksQuery.hasError) {
return Center(
child: PlatformText(topTracksQuery.error.toString()),
child: Text(topTracksQuery.error.toString()),
);
}
@ -305,13 +306,12 @@ class ArtistPage extends HookConsumerWidget {
return Column(children: [
Row(
children: [
PlatformText(
Text(
"Top Tracks",
style:
PlatformTheme.of(context).textTheme?.headline,
style: Theme.of(context).textTheme.headlineSmall,
),
if (!isPlaylistPlaying)
PlatformIconButton(
IconButton(
icon: const Icon(
SpotubeIcons.queueAdd,
),
@ -321,7 +321,7 @@ class ArtistPage extends HookConsumerWidget {
SnackBar(
width: 300,
behavior: SnackBarBehavior.floating,
content: PlatformText(
content: Text(
"Added ${topTracks.length} tracks to queue",
textAlign: TextAlign.center,
),
@ -329,17 +329,17 @@ class ArtistPage extends HookConsumerWidget {
);
},
),
if (platform != TargetPlatform.linux)
const SizedBox(width: 5),
PlatformIconButton(
const SizedBox(width: 5),
IconButton(
icon: Icon(
isPlaylistPlaying
? SpotubeIcons.stop
: SpotubeIcons.play,
color: Colors.white,
),
backgroundColor:
PlatformTheme.of(context).primaryColor,
style: IconButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
),
onPressed: () => playPlaylist(topTracks.toList()),
)
],
@ -364,16 +364,16 @@ class ArtistPage extends HookConsumerWidget {
},
),
const SizedBox(height: 50),
PlatformText(
Text(
"Albums",
style: PlatformTheme.of(context).textTheme?.headline,
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 10),
ArtistAlbumList(artistId),
const SizedBox(height: 20),
PlatformText(
Text(
"Fans also likes",
style: PlatformTheme.of(context).textTheme?.headline,
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 10),
HookBuilder(
@ -384,10 +384,10 @@ class ArtistPage extends HookConsumerWidget {
);
if (relatedArtists.isLoading || !relatedArtists.hasData) {
return const PlatformCircularProgressIndicator();
return const CircularProgressIndicator();
} else if (relatedArtists.hasError) {
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:go_router/go_router.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/components/desktop_login/login_form.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
@ -15,10 +15,9 @@ class DesktopLoginPage extends HookConsumerWidget {
final breakpoint = useBreakpoints();
return SafeArea(
child: PlatformScaffold(
appBar: PageWindowTitleBar(
leading: const PlatformBackButton(),
hideWhenWindows: false,
child: Scaffold(
appBar: const PageWindowTitleBar(
leading: BackButton(),
),
body: SingleChildScrollView(
child: Center(
@ -26,7 +25,7 @@ class DesktopLoginPage extends HookConsumerWidget {
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: PlatformTheme.of(context).secondaryBackgroundColor,
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(10),
),
child: Column(
@ -35,11 +34,13 @@ class DesktopLoginPage extends HookConsumerWidget {
width: MediaQuery.of(context).size.width *
(breakpoint <= Breakpoints.md ? .5 : .3),
),
PlatformText.subheading(
Text(
"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",
style: Theme.of(context).textTheme.labelMedium,
),
const SizedBox(height: 10),
TokenLoginForm(
@ -50,9 +51,9 @@ class DesktopLoginPage extends HookConsumerWidget {
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
const PlatformText("Don't know how to do this?"),
PlatformTextButton(
child: const PlatformText(
const Text("Don't know how to do this?"),
TextButton(
child: const Text(
"Follow along the Step by Step guide",
),
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:go_router/go_router.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/components/desktop_login/login_form.dart';
import 'package:spotube/components/shared/links/hyper_link.dart';
@ -21,14 +21,13 @@ class LoginTutorial extends ConsumerWidget {
final key = GlobalKey<State<IntroductionScreen>>();
final pageDecoration = PageDecoration(
bodyTextStyle: PlatformTheme.of(context).textTheme!.body!,
titleTextStyle: PlatformTheme.of(context).textTheme!.subheading!,
bodyTextStyle: Theme.of(context).textTheme.bodyMedium!,
titleTextStyle: Theme.of(context).textTheme.headlineMedium!,
);
return PlatformScaffold(
return Scaffold(
appBar: PageWindowTitleBar(
hideWhenWindows: false,
leading: PlatformTextButton(
child: const PlatformText("Exit"),
leading: TextButton(
child: const Text("Exit"),
onPressed: () {
Navigator.of(context).pop();
},
@ -36,29 +35,27 @@ class LoginTutorial extends ConsumerWidget {
),
body: IntroductionScreen(
key: key,
globalBackgroundColor:
PlatformTheme.of(context).scaffoldBackgroundColor,
overrideBack: PlatformFilledButton(
isSecondary: true,
child: const Center(child: PlatformText("Previous")),
globalBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
overrideBack: OutlinedButton(
child: const Center(child: Text("Previous")),
onPressed: () {
(key.currentState as IntroductionScreenState).previous();
},
),
overrideNext: PlatformFilledButton(
child: const Center(child: PlatformText("Next")),
overrideNext: FilledButton(
child: const Center(child: Text("Next")),
onPressed: () {
(key.currentState as IntroductionScreenState).next();
},
),
showBackButton: true,
overrideDone: PlatformFilledButton(
overrideDone: FilledButton(
onPressed: authenticationNotifier.isLoggedIn
? () {
ServiceUtils.navigate(context, "/");
}
: null,
child: const Center(child: PlatformText("Done")),
child: const Center(child: Text("Done")),
),
pages: [
PageViewModel(
@ -67,14 +64,14 @@ class LoginTutorial extends ConsumerWidget {
image: Assets.tutorial.step1.image(),
bodyWidget: Wrap(
children: const [
PlatformText(
Text(
"First, Go to ",
),
Hyperlink(
"accounts.spotify.com ",
"https://accounts.spotify.com",
),
PlatformText(
Text(
"and Login/Sign up if you're not logged in",
),
],
@ -84,7 +81,7 @@ class LoginTutorial extends ConsumerWidget {
decoration: pageDecoration,
title: "Step 2",
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",
textAlign: TextAlign.left,
),
@ -93,7 +90,7 @@ class LoginTutorial extends ConsumerWidget {
decoration: pageDecoration,
title: "Step 3",
image: Assets.tutorial.step3.image(),
bodyWidget: const PlatformText(
bodyWidget: const Text(
"Copy the values of \"sp_dc\" and \"sp_key\" Cookies",
textAlign: TextAlign.left,
),
@ -114,8 +111,9 @@ class LoginTutorial extends ConsumerWidget {
title: "Step 5",
bodyWidget: Column(
children: [
PlatformText.label(
Text(
"Paste the copied \"sp_dc\" and \"sp_key\" values in the respective fields",
style: Theme.of(context).textTheme.labelMedium,
),
const SizedBox(height: 10),
TokenLoginForm(

View File

@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/pages/home/genres.dart';
import 'package:spotube/pages/home/personalized.dart';
@ -12,57 +10,38 @@ class HomePage extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final index = useState(0);
final tabbar = PlatformTabBar(
androidIsScrollable: true,
selectedIndex: index.value,
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,
centerTitle: true,
center: tabbar,
),
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, animation) => SlideTransition(
position: animation.drive(
Tween<Offset>(
begin: const Offset(1, 0),
end: const Offset(0, 0),
),
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: const PageWindowTitleBar(
titleWidth: 347,
centerTitle: true,
title: TabBar(
isScrollable: true,
tabs: [
Tab(text: 'Genres'),
Tab(text: 'Personalized'),
],
),
),
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, animation) => SlideTransition(
position: animation.drive(
Tween<Offset>(
begin: const Offset(1, 0),
end: const Offset(0, 0),
),
),
child: child,
),
child: const TabBarView(
children: [
GenrePage(),
PersonalizedPage(),
],
),
child: child,
),
child: [
const GenrePage(),
const PersonalizedPage(),
][index.value],
),
);
}

View File

@ -2,7 +2,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' hide Page;
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/album/album_card.dart';
import 'package:spotube/components/playlist/playlist_card.dart';
@ -53,7 +53,10 @@ class PersonalizedItemCard extends HookWidget {
padding: const EdgeInsets.all(8.0),
child: Row(
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_hooks/flutter_hooks.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/shared/page_window_title_bar.dart';
import 'package:spotube/components/library/user_albums.dart';
@ -13,43 +12,34 @@ class LibraryPage extends HookConsumerWidget {
const LibraryPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, ref) {
final index = useState(0);
final body = [
const UserPlaylists(),
const UserLocalTracks(),
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,
centerTitle: true,
center: tabbar,
),
body: body,
return const SafeArea(
child: DefaultTabController(
length: 5,
child: Scaffold(
appBar: PageWindowTitleBar(
titleWidth: 347,
centerTitle: true,
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(),
],
),
),
),
);
}

View File

@ -3,7 +3,7 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
@ -31,7 +31,6 @@ class LyricsPage extends HookConsumerWidget {
[playlist?.activeTrack.album?.images],
);
final palette = usePaletteColor(albumArt, ref);
final index = useState(0);
useCustomStatusBarColor(
palette.color,
@ -39,102 +38,101 @@ class LyricsPage extends HookConsumerWidget {
noSetBGColor: true,
);
Widget body = [
SyncedLyrics(palette: palette, isModal: isModal),
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: [
PlatformTab(
label: "Synced",
icon: const SizedBox.shrink(),
color: PlatformTextTheme.of(context).caption?.color,
),
PlatformTab(
label: "Plain",
icon: const SizedBox.shrink(),
color: PlatformTextTheme.of(context).caption?.color,
),
],
),
const tabbar = TabBar(
isScrollable: true,
tabs: [
Tab(text: "Synced"),
Tab(text: "Plain"),
],
);
if (isModal) {
return SafeArea(
child: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background.withOpacity(.4),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
return DefaultTabController(
length: 2,
child: SafeArea(
child: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background.withOpacity(.4),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
child: Column(
children: [
const SizedBox(height: 5),
Container(
height: 7,
width: 150,
decoration: BoxDecoration(
color: palette.titleTextColor,
borderRadius: BorderRadius.circular(10),
),
),
PlatformAppBar(
title: tabbar,
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
toolbarOpacity: platform == TargetPlatform.iOS ? 0 : 1,
actions: [
PlatformIconButton(
icon: const Icon(SpotubeIcons.minimize),
onPressed: () => Navigator.of(context).pop(),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
child: Column(
children: [
const SizedBox(height: 5),
Container(
height: 7,
width: 150,
decoration: BoxDecoration(
color: palette.titleTextColor,
borderRadius: BorderRadius.circular(10),
),
const SizedBox(width: 5),
],
),
Expanded(child: body),
],
),
AppBar(
title: tabbar,
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
toolbarOpacity: 1,
actions: [
IconButton(
icon: const Icon(SpotubeIcons.minimize),
onPressed: () => Navigator.of(context).pop(),
),
const SizedBox(width: 5),
],
),
Expanded(
child: TabBarView(
children: [
SyncedLyrics(palette: palette, isModal: isModal),
PlainLyrics(palette: palette, isModal: isModal),
],
),
),
],
),
),
),
),
);
}
return PlatformScaffold(
extendBodyBehindAppBar: true,
appBar: !kIsMacOS
? (platform != TargetPlatform.windows && !isModal
? PageWindowTitleBar(
toolbarOpacity: 0,
backgroundColor: Colors.transparent,
center: tabbar,
)
: tabbar)
: null,
body: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
image: DecorationImage(
image: UniversalImage.imageProvider(albumArt),
fit: BoxFit.cover,
return DefaultTabController(
length: 2,
child: Scaffold(
extendBodyBehindAppBar: true,
appBar: !kIsMacOS
? const PageWindowTitleBar(
toolbarOpacity: 0,
backgroundColor: Colors.transparent,
title: tabbar,
)
: tabbar as PreferredSizeWidget?,
body: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
image: DecorationImage(
image: UniversalImage.imageProvider(albumArt),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(8),
),
borderRadius: BorderRadius.circular(8),
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: ColoredBox(
color: palette.color.withOpacity(.7),
child: SafeArea(child: body),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: ColoredBox(
color: palette.color.withOpacity(.7),
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:go_router/go_router.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/utils/platform.dart';
@ -24,7 +24,7 @@ class WebViewLogin extends HookConsumerWidget {
);
}
return PlatformScaffold(
return Scaffold(
body: SafeArea(
child: InAppWebView(
initialOptions: InAppWebViewGroupOptions(

View File

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:platform_ui/platform_ui.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
@ -17,10 +16,10 @@ class AboutSpotube extends HookConsumerWidget {
Widget build(BuildContext context, ref) {
final packageInfo = usePackageInfo();
return PlatformScaffold(
return Scaffold(
appBar: PageWindowTitleBar(
leading: const PlatformBackButton(),
center: const PlatformText("About Spotube"),
leading: const BackButton(),
title: const Text("About Spotube"),
),
body: SingleChildScrollView(
child: Padding(
@ -35,14 +34,15 @@ class AboutSpotube extends HookConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PlatformText.headline(
Text(
"Spotube, a light-weight, cross-platform, free-for-all spotify client",
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 20),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const PlatformText(
const Text(
"Founder: Kingkor Roy Tirtho",
style: TextStyle(
fontWeight: FontWeight.bold,
@ -60,11 +60,11 @@ class AboutSpotube extends HookConsumerWidget {
],
),
const SizedBox(height: 5),
PlatformText(
Text(
"Version: v${packageInfo.version}",
),
const SizedBox(height: 5),
PlatformText(
Text(
"Build Number: ${packageInfo.buildNumber.replaceAll(".", " ")}",
),
const SizedBox(height: 5),
@ -75,7 +75,7 @@ class AboutSpotube extends HookConsumerWidget {
mode: LaunchMode.externalApplication,
);
},
child: const PlatformText(
child: const Text(
"Repository: https://github.com/KRTirtho/spotube",
),
),
@ -87,7 +87,7 @@ class AboutSpotube extends HookConsumerWidget {
mode: LaunchMode.externalApplication,
);
},
child: const PlatformText(
child: const Text(
"License: BSD-4-Clause",
),
),
@ -99,7 +99,7 @@ class AboutSpotube extends HookConsumerWidget {
mode: LaunchMode.externalApplication,
);
},
child: const PlatformText(
child: const Text(
"Bugs+Issues: https://github.com/KRTirtho/spotube/issues",
),
),
@ -178,21 +178,24 @@ class AboutSpotube extends HookConsumerWidget {
],
),
const SizedBox(height: 20),
PlatformText.caption(
Text(
"Made with ❤️ in Bangladesh🇧🇩",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
),
PlatformText.caption(
Text(
"© 2021-${DateTime.now().year} Kingkor Roy Tirtho",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 20),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 750),
child: SafeArea(
child: PlatformText.caption(
child: Text(
licenseText,
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:fuzzywuzzy/fuzzywuzzy.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/provider/blacklist_provider.dart';
@ -35,23 +35,23 @@ class BlackListPage extends HookConsumerWidget {
[blacklist, searchText.value],
);
return PlatformScaffold(
return Scaffold(
appBar: PageWindowTitleBar(
center: const PlatformText("Blacklist"),
title: const Text("Blacklist"),
centerTitle: true,
leading: const PlatformBackButton(),
leading: const BackButton(),
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: platform == TargetPlatform.windows
? const EdgeInsets.all(8.0).copyWith(left: 45)
: const EdgeInsets.all(8.0),
child: PlatformTextField(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) => searchText.value = value,
placeholder: "Search",
prefixIcon: SpotubeIcons.search,
decoration: const InputDecoration(
hintText: "Search",
prefixIcon: Icon(SpotubeIcons.search),
),
),
),
ListView.builder(
@ -59,11 +59,11 @@ class BlackListPage extends HookConsumerWidget {
itemCount: filteredBlacklist.length,
itemBuilder: (context, index) {
final item = filteredBlacklist.elementAt(index);
return PlatformListTile(
leading: PlatformText("${index + 1}."),
title: PlatformText("${item.name} (${item.type.name})"),
subtitle: PlatformText.caption(item.id),
trailing: PlatformIconButton(
return ListTile(
leading: Text("${index + 1}."),
title: Text("${item.name} (${item.type.name})"),
subtitle: Text(item.id),
trailing: IconButton(
icon: Icon(SpotubeIcons.trash, color: Colors.red[400]),
onPressed: () {
ref

View File

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

View File

@ -9,14 +9,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "52.0.0"
adwaita:
dependency: "direct main"
description:
name: adwaita
sha256: "535781747357779fa2830815e0a1b6b7888c8c538194ba73ac4fc8d82412baec"
url: "https://pub.dev"
source: hosted
version: "0.5.2"
analyzer:
dependency: transitive
description:
@ -546,14 +538,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: "direct main"
description:
@ -671,11 +655,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_mailer:
dependency: transitive
description:
@ -782,14 +761,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
gsettings:
dependency: transitive
description:
name: gsettings
sha256: fe90d719e09a6f36607021047e642068a0c98839d9633db00b91633420ae8b0d
url: "https://pub.dev"
source: hosted
version: "0.2.7"
hive:
dependency: "direct main"
description:
@ -910,22 +881,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -950,14 +905,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -1206,15 +1153,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -1255,14 +1193,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -1327,14 +1257,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.2.1"
recase:
dependency: transitive
description:
name: recase
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
url: "https://pub.dev"
source: hosted
version: "4.1.0"
riverpod:
dependency: transitive
description:
@ -1359,14 +1281,6 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: "direct main"
description:
@ -1808,4 +1722,4 @@ packages:
version: "1.12.3"
sdks:
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"
dependencies:
adwaita: ^0.5.2
args: ^2.3.2
async: ^2.9.0
audio_service: ^0.18.9
@ -29,7 +28,6 @@ dependencies:
file_picker: ^5.2.2
fl_query: ^1.0.0-alpha.0
fl_query_hooks: ^1.0.0-alpha.0
fluent_ui: ^4.3.0
fluentui_system_icons: ^1.1.189
flutter:
sdk: flutter
@ -50,9 +48,7 @@ dependencies:
introduction_screen: ^3.0.2
json_annotation: ^4.8.0
json_serializable: ^6.6.0
libadwaita: ^1.2.5
logger: ^1.1.0
macos_ui: ^1.9.0
marquee: ^2.2.3
metadata_god: ^0.3.2
mime: ^1.0.2
@ -61,10 +57,6 @@ dependencies:
path: ^1.8.0
path_provider: ^2.0.8
permission_handler: ^10.2.0
platform_ui:
git:
url: https://github.com/KRTirtho/platform_ui.git
ref: 339ea7922d2f881d9a72b565aeebc7e2c4b22509
pocketbase: ^0.7.1+1
popover: ^0.2.6+3
queue: ^3.1.0+1