mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
refactor: working dissect of platform_ui
This commit is contained in:
parent
cfa9e04377
commit
a4927c7013
@ -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;
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
@ -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"),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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..."),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 [
|
||||
|
@ -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),
|
||||
|
@ -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 ==
|
||||
|
@ -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)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -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,
|
||||
|
@ -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)),
|
||||
],
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -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),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -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"),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
@ -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);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
@ -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"),
|
||||
)
|
||||
],
|
||||
|
@ -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(
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
);
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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],
|
||||
|
@ -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),
|
||||
|
@ -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});
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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],
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
@ -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),
|
||||
|
@ -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(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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(
|
||||
|
@ -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: () {
|
||||
|
@ -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) {
|
||||
|
@ -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() ??
|
||||
"",
|
||||
),
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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],
|
||||
),
|
||||
);
|
||||
}
|
@ -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
40
lib/themes/theme.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
@ -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"))
|
||||
|
88
pubspec.lock
88
pubspec.lock
@ -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"
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user