mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55: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/material.dart';
|
||||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||||
|
|
||||||
abstract class SpotubeIcons {
|
abstract class SpotubeIcons {
|
||||||
static const home = FluentIcons.home;
|
static const home = FluentIcons.home_12_regular;
|
||||||
static const search = FeatherIcons.search;
|
static const search = FeatherIcons.search;
|
||||||
static const library = FluentIcons.library;
|
static const library = FluentIcons.library_16_regular;
|
||||||
static const music = FeatherIcons.music;
|
static const music = FeatherIcons.music;
|
||||||
static const play = FluentIcons.play;
|
static const play = FluentIcons.play_12_regular;
|
||||||
static const pause = FeatherIcons.pause;
|
static const pause = FeatherIcons.pause;
|
||||||
static const skipForward = FeatherIcons.skipForward;
|
static const skipForward = FeatherIcons.skipForward;
|
||||||
static const skipBack = FeatherIcons.skipBack;
|
static const skipBack = FeatherIcons.skipBack;
|
||||||
@ -16,8 +16,8 @@ abstract class SpotubeIcons {
|
|||||||
static const refresh = FeatherIcons.refreshCw;
|
static const refresh = FeatherIcons.refreshCw;
|
||||||
static const settings = FeatherIcons.settings;
|
static const settings = FeatherIcons.settings;
|
||||||
static const shuffle = FeatherIcons.shuffle;
|
static const shuffle = FeatherIcons.shuffle;
|
||||||
static const repeat = FluentIcons.repeat_all;
|
static const repeat = FluentIcons.arrow_repeat_all_16_regular;
|
||||||
static const repeatOne = FluentIcons.repeat_one;
|
static const repeatOne = Icons.repeat_one_rounded;
|
||||||
static const remove = FeatherIcons.minus;
|
static const remove = FeatherIcons.minus;
|
||||||
static const removeFilled = FeatherIcons.minusCircle;
|
static const removeFilled = FeatherIcons.minusCircle;
|
||||||
static const add = FeatherIcons.plus;
|
static const add = FeatherIcons.plus;
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart' hide Colors;
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/shared/hover_builder.dart';
|
import 'package:spotube/components/shared/hover_builder.dart';
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/hooks/use_platform_property.dart';
|
|
||||||
import 'package:spotube/provider/blacklist_provider.dart';
|
import 'package:spotube/provider/blacklist_provider.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
@ -30,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(
|
return SizedBox(
|
||||||
height: 240,
|
height: 240,
|
||||||
width: 200,
|
width: 200,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
splashFactory: splash,
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
ServiceUtils.navigate(context, "/artist/${artist.id}");
|
ServiceUtils.navigate(context, "/artist/${artist.id}");
|
||||||
},
|
},
|
||||||
customBorder: platform == TargetPlatform.windows
|
borderRadius: BorderRadius.circular(8),
|
||||||
? Border.all(
|
|
||||||
color: FluentTheme.maybeOf(context)
|
|
||||||
?.micaBackgroundColor
|
|
||||||
.withOpacity(.7) ??
|
|
||||||
Colors.transparent,
|
|
||||||
width: 1,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
platform == TargetPlatform.windows ? 5 : 8,
|
|
||||||
),
|
|
||||||
child: HoverBuilder(builder: (context, isHovering) {
|
child: HoverBuilder(builder: (context, isHovering) {
|
||||||
return Ink(
|
return Ink(
|
||||||
width: 200,
|
width: 200,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: PlatformTheme.of(context).secondaryBackgroundColor,
|
color: Theme.of(context).cardColor,
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(8),
|
||||||
platform == TargetPlatform.windows ? 5 : 8,
|
|
||||||
),
|
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
if (boxShadow != null) boxShadow,
|
BoxShadow(
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 3),
|
||||||
|
spreadRadius: 5,
|
||||||
|
color: Theme.of(context).colorScheme.shadow,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
border: isBlackListed
|
border: isBlackListed
|
||||||
? Border.all(
|
? Border.all(
|
||||||
color: Colors.red[400]!,
|
color: Colors.red[400]!,
|
||||||
width: 2,
|
width: 2,
|
||||||
)
|
)
|
||||||
: [TargetPlatform.windows, TargetPlatform.macOS]
|
: null,
|
||||||
.contains(platform)
|
|
||||||
? Border.all(
|
|
||||||
color: PlatformTheme.of(context).borderColor ??
|
|
||||||
Colors.transparent,
|
|
||||||
width: 1,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(15),
|
padding: const EdgeInsets.all(15),
|
||||||
@ -138,7 +96,7 @@ class ArtistCard extends HookConsumerWidget {
|
|||||||
artist.name!,
|
artist.name!,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: PlatformTextTheme.of(context).body?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
|
|
||||||
class TokenLoginForm extends HookConsumerWidget {
|
class TokenLoginForm extends HookConsumerWidget {
|
||||||
@ -25,27 +25,31 @@ class TokenLoginForm extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
PlatformTextField(
|
TextField(
|
||||||
controller: directCodeController,
|
controller: directCodeController,
|
||||||
placeholder: "Spotify \"sp_dc\" Cookie",
|
decoration: const InputDecoration(
|
||||||
label: "sp_dc Cookie",
|
hintText: "Spotify \"sp_dc\" Cookie",
|
||||||
|
labelText: "sp_dc Cookie",
|
||||||
|
),
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
PlatformTextField(
|
TextField(
|
||||||
controller: keyCodeController,
|
controller: keyCodeController,
|
||||||
placeholder: "Spotify \"sp_key\" Cookie",
|
decoration: const InputDecoration(
|
||||||
label: "sp_key Cookie",
|
hintText: "Spotify \"sp_key\" Cookie",
|
||||||
|
labelText: "sp_key Cookie",
|
||||||
|
),
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
PlatformFilledButton(
|
FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (keyCodeController.text.isEmpty ||
|
if (keyCodeController.text.isEmpty ||
|
||||||
directCodeController.text.isEmpty) {
|
directCodeController.text.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: PlatformText("Please fill in all fields"),
|
content: Text("Please fill in all fields"),
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -61,7 +65,7 @@ class TokenLoginForm extends HookConsumerWidget {
|
|||||||
onDone?.call();
|
onDone?.call();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const PlatformText("Submit"),
|
child: const Text("Submit"),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:flutter/material.dart' hide Page;
|
import 'package:flutter/material.dart' hide Page;
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||||
import 'package:spotube/components/shared/waypoint.dart';
|
import 'package:spotube/components/shared/waypoint.dart';
|
||||||
@ -39,13 +39,15 @@ class CategoryCard extends HookConsumerWidget {
|
|||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
PlatformText.headline(category.name ?? "Unknown"),
|
Text(
|
||||||
|
category.name ?? "Unknown",
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
playlistQuery.hasPageError && !playlistQuery.hasPageData
|
playlistQuery.hasPageError && !playlistQuery.hasPageData
|
||||||
? PlatformText(
|
? Text("Something Went Wrong\n${playlistQuery.errors.first}")
|
||||||
"Something Went Wrong\n${playlistQuery.errors.first}")
|
|
||||||
: SizedBox(
|
: SizedBox(
|
||||||
height: 245,
|
height: 245,
|
||||||
child: ScrollConfiguration(
|
child: ScrollConfiguration(
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/album/album_card.dart';
|
import 'package:spotube/components/album/album_card.dart';
|
||||||
import 'package:spotube/components/shared/playbutton_card.dart';
|
import 'package:spotube/components/shared/playbutton_card.dart';
|
||||||
@ -65,16 +65,17 @@ class UserAlbums extends HookConsumerWidget {
|
|||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
color: PlatformTheme.of(context).scaffoldBackgroundColor,
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
PlatformTextField(
|
TextField(
|
||||||
onChanged: (value) => searchText.value = value,
|
onChanged: (value) => searchText.value = value,
|
||||||
prefixIcon: SpotubeIcons.filter,
|
decoration: const InputDecoration(
|
||||||
placeholder: 'Filter Albums...',
|
prefixIcon: Icon(SpotubeIcons.filter),
|
||||||
|
hintText: 'Filter albums...',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Wrap(
|
Wrap(
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||||
@ -56,25 +56,27 @@ class UserArtists extends HookConsumerWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
child: ColoredBox(
|
child: ColoredBox(
|
||||||
color: PlatformTheme.of(context).scaffoldBackgroundColor!,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
child: PlatformTextField(
|
child: TextField(
|
||||||
onChanged: (value) => searchText.value = value,
|
onChanged: (value) => searchText.value = value,
|
||||||
prefixIcon: SpotubeIcons.filter,
|
decoration: const InputDecoration(
|
||||||
placeholder: 'Filter artists...',
|
prefixIcon: Icon(SpotubeIcons.filter),
|
||||||
|
hintText: 'Filter artists...',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
backgroundColor: PlatformTheme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
body: artistQuery.pages.isEmpty
|
body: artistQuery.pages.isEmpty
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: const [
|
children: const [
|
||||||
PlatformCircularProgressIndicator(),
|
CircularProgressIndicator(),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
PlatformText("Loading..."),
|
Text("Loading..."),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/provider/downloader_provider.dart';
|
import 'package:spotube/provider/downloader_provider.dart';
|
||||||
@ -26,20 +26,19 @@ class UserDownloads extends HookConsumerWidget {
|
|||||||
child: AutoSizeText(
|
child: AutoSizeText(
|
||||||
"Currently downloading (${downloader.currentlyRunning})",
|
"Currently downloading (${downloader.currentlyRunning})",
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: PlatformTextTheme.of(context).headline,
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
PlatformFilledButton(
|
FilledButton(
|
||||||
style: ButtonStyle(
|
style: FilledButton.styleFrom(
|
||||||
backgroundColor: MaterialStatePropertyAll(Colors.red[50]),
|
backgroundColor: Colors.red[50],
|
||||||
foregroundColor: MaterialStatePropertyAll(Colors.red[400]),
|
foregroundColor: Colors.red[400],
|
||||||
),
|
),
|
||||||
onPressed: downloader.currentlyRunning > 0
|
onPressed: downloader.currentlyRunning > 0
|
||||||
? downloader.cancelAll
|
? downloader.cancelAll
|
||||||
: null,
|
: null,
|
||||||
isSecondary: true,
|
child: const Text("Cancel All"),
|
||||||
child: const PlatformText("Cancel All"),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -49,7 +48,7 @@ class UserDownloads extends HookConsumerWidget {
|
|||||||
itemCount: downloader.inQueue.length,
|
itemCount: downloader.inQueue.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final track = downloader.inQueue.elementAt(index);
|
final track = downloader.inQueue.elementAt(index);
|
||||||
return PlatformListTile(
|
return ListTile(
|
||||||
title: Text(track.name ?? ''),
|
title: Text(track.name ?? ''),
|
||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||||
@ -68,7 +67,7 @@ class UserDownloads extends HookConsumerWidget {
|
|||||||
trailing: const SizedBox(
|
trailing: const SizedBox(
|
||||||
width: 30,
|
width: 30,
|
||||||
height: 30,
|
height: 30,
|
||||||
child: PlatformCircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
TypeConversionUtils.artists_X_String(
|
TypeConversionUtils.artists_X_String(
|
||||||
|
@ -12,7 +12,7 @@ import 'package:mime/mime.dart';
|
|||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/compact_search.dart';
|
import 'package:spotube/components/shared/compact_search.dart';
|
||||||
@ -189,7 +189,7 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
PlatformFilledButton(
|
FilledButton(
|
||||||
onPressed: trackSnapshot.value != null
|
onPressed: trackSnapshot.value != null
|
||||||
? () {
|
? () {
|
||||||
if (trackSnapshot.value?.isNotEmpty == true) {
|
if (trackSnapshot.value?.isNotEmpty == true) {
|
||||||
@ -221,7 +221,7 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
PlatformFilledButton(
|
FilledButton(
|
||||||
child: const Icon(SpotubeIcons.refresh),
|
child: const Icon(SpotubeIcons.refresh),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.refresh(localTracksProvider);
|
ref.refresh(localTracksProvider);
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
import 'package:spotube/components/playlist/playlist_create_dialog.dart';
|
||||||
@ -94,15 +94,16 @@ class UserPlaylists extends HookConsumerWidget {
|
|||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
PlatformTextField(
|
TextField(
|
||||||
onChanged: (value) => searchText.value = value,
|
onChanged: (value) => searchText.value = value,
|
||||||
placeholder: "Filter your playlists...",
|
decoration: const InputDecoration(
|
||||||
prefixIcon: SpotubeIcons.filter,
|
hintText: "Filter your playlists...",
|
||||||
|
prefixIcon: Icon(SpotubeIcons.filter),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (playlistsQuery.isLoading || !playlistsQuery.hasData)
|
if (playlistsQuery.isLoading || !playlistsQuery.hasData)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
|
|
||||||
class ZoomControls extends HookWidget {
|
class ZoomControls extends HookWidget {
|
||||||
@ -32,15 +32,15 @@ class ZoomControls extends HookWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final actions = [
|
final actions = [
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
icon: decreaseIcon,
|
icon: decreaseIcon,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (value == min) return;
|
if (value == min) return;
|
||||||
onChanged(value - interval);
|
onChanged(value - interval);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PlatformText("$value$unit"),
|
Text("$value$unit"),
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
icon: increaseIcon,
|
icon: increaseIcon,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (value == max) return;
|
if (value == max) return;
|
||||||
@ -51,9 +51,7 @@ class ZoomControls extends HookWidget {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: PlatformTheme.of(context)
|
color: Theme.of(context).cardColor.withOpacity(0.7),
|
||||||
.secondaryBackgroundColor
|
|
||||||
?.withOpacity(0.7),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/player/player_queue.dart';
|
import 'package:spotube/components/player/player_queue.dart';
|
||||||
@ -54,7 +54,7 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: mainAxisAlignment,
|
mainAxisAlignment: mainAxisAlignment,
|
||||||
children: [
|
children: [
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
icon: const Icon(SpotubeIcons.queue),
|
icon: const Icon(SpotubeIcons.queue),
|
||||||
tooltip: 'Queue',
|
tooltip: 'Queue',
|
||||||
onPressed: playlist != null
|
onPressed: playlist != null
|
||||||
@ -80,7 +80,7 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
if (!isLocalTrack)
|
if (!isLocalTrack)
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
icon: const Icon(SpotubeIcons.alternativeRoute),
|
icon: const Icon(SpotubeIcons.alternativeRoute),
|
||||||
tooltip: "Alternative Track Sources",
|
tooltip: "Alternative Track Sources",
|
||||||
onPressed: playlist?.activeTrack != null
|
onPressed: playlist?.activeTrack != null
|
||||||
@ -115,7 +115,7 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: 'Download track',
|
tooltip: 'Download track',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isDownloaded ? SpotubeIcons.done : SpotubeIcons.download,
|
isDownloaded ? SpotubeIcons.done : SpotubeIcons.download,
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/collections/intents.dart';
|
import 'package:spotube/collections/intents.dart';
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
@ -106,9 +106,9 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
PlatformTooltip(
|
Tooltip(
|
||||||
message: "Slide to seek forward or backward",
|
message: "Slide to seek forward or backward",
|
||||||
child: PlatformSlider(
|
child: Slider(
|
||||||
// cannot divide by zero
|
// cannot divide by zero
|
||||||
// there's an edge case for value being bigger
|
// there's an edge case for value being bigger
|
||||||
// than total duration. Keeping it resolved
|
// than total duration. Keeping it resolved
|
||||||
@ -135,10 +135,10 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
PlatformText(
|
Text(
|
||||||
"$currentMinutes:$currentSeconds",
|
"$currentMinutes:$currentSeconds",
|
||||||
),
|
),
|
||||||
PlatformText("$totalMinutes:$totalSeconds"),
|
Text("$totalMinutes:$totalSeconds"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -149,14 +149,14 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: playlist?.isShuffled == true
|
tooltip: playlist?.isShuffled == true
|
||||||
? "Unshuffle playlist"
|
? "Unshuffle playlist"
|
||||||
: "Shuffle playlist",
|
: "Shuffle playlist",
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.shuffle,
|
SpotubeIcons.shuffle,
|
||||||
color: playlist?.isShuffled == true
|
color: playlist?.isShuffled == true
|
||||||
? PlatformTheme.of(context).primaryColor
|
? Theme.of(context).primaryColor
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
onPressed: playlist == null
|
onPressed: playlist == null
|
||||||
@ -169,7 +169,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: "Previous track",
|
tooltip: "Previous track",
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.skipBack,
|
SpotubeIcons.skipBack,
|
||||||
@ -177,13 +177,13 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
onPressed: playlistNotifier.previous,
|
onPressed: playlistNotifier.previous,
|
||||||
),
|
),
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: playing ? "Pause playback" : "Resume playback",
|
tooltip: playing ? "Pause playback" : "Resume playback",
|
||||||
icon: playlist?.isLoading == true
|
icon: playlist?.isLoading == true
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 20,
|
width: 20,
|
||||||
child: PlatformCircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
playing ? SpotubeIcons.pause : SpotubeIcons.play,
|
playing ? SpotubeIcons.pause : SpotubeIcons.play,
|
||||||
@ -194,7 +194,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
PlayPauseIntent(ref),
|
PlayPauseIntent(ref),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: "Next track",
|
tooltip: "Next track",
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.skipForward,
|
SpotubeIcons.skipForward,
|
||||||
@ -202,7 +202,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
onPressed: playlistNotifier.next,
|
onPressed: playlistNotifier.next,
|
||||||
),
|
),
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: playlist?.isLooping != true
|
tooltip: playlist?.isLooping != true
|
||||||
? "Loop Track"
|
? "Loop Track"
|
||||||
: "Repeat playlist",
|
: "Repeat playlist",
|
||||||
@ -211,7 +211,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
? SpotubeIcons.repeatOne
|
? SpotubeIcons.repeatOne
|
||||||
: SpotubeIcons.repeat,
|
: SpotubeIcons.repeat,
|
||||||
color: playlist?.isLooping == true
|
color: playlist?.isLooping == true
|
||||||
? PlatformTheme.of(context).primaryColor
|
? Theme.of(context).primaryColor
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
onPressed: playlist == null || playlist.isLoading
|
onPressed: playlist == null || playlist.isLoading
|
||||||
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/player/player_track_details.dart';
|
import 'package:spotube/components/player/player_track_details.dart';
|
||||||
import 'package:spotube/hooks/use_palette_color.dart';
|
import 'package:spotube/hooks/use_palette_color.dart';
|
||||||
@ -59,7 +59,6 @@ class PlayerOverlay extends HookConsumerWidget {
|
|||||||
duration: const Duration(milliseconds: 250),
|
duration: const Duration(milliseconds: 250),
|
||||||
opacity: canShow ? 1 : 0,
|
opacity: canShow ? 1 : 0,
|
||||||
child: Material(
|
child: Material(
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
@ -3,7 +3,7 @@ import 'dart:ui';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/fallbacks/not_found.dart';
|
import 'package:spotube/components/shared/fallbacks/not_found.dart';
|
||||||
@ -36,8 +36,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
topLeft: Radius.circular(10),
|
topLeft: Radius.circular(10),
|
||||||
topRight: Radius.circular(10),
|
topRight: Radius.circular(10),
|
||||||
);
|
);
|
||||||
final headlineColor =
|
final headlineColor = Theme.of(context).textTheme.headlineSmall?.color;
|
||||||
PlatformTheme.of(context).textTheme?.subheading?.color;
|
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (playlist == null) return null;
|
if (playlist == null) return null;
|
||||||
@ -61,9 +60,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
top: 5.0,
|
top: 5.0,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: PlatformTheme.of(context)
|
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5),
|
||||||
.scaffoldBackgroundColor
|
|
||||||
?.withOpacity(0.5),
|
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -80,7 +77,7 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
PlatformText(
|
Text(
|
||||||
"${tracks.length} tracks in Queue",
|
"${tracks.length} tracks in Queue",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: headlineColor,
|
color: headlineColor,
|
||||||
@ -89,14 +86,13 @@ class PlayerQueue extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
PlatformFilledButton(
|
FilledButton(
|
||||||
style: ButtonStyle(
|
style: FilledButton.styleFrom(
|
||||||
backgroundColor: MaterialStatePropertyAll(
|
backgroundColor: Theme.of(context)
|
||||||
PlatformTheme.of(context)
|
.scaffoldBackgroundColor
|
||||||
.scaffoldBackgroundColor
|
.withOpacity(0.5),
|
||||||
?.withOpacity(0.5)),
|
foregroundColor:
|
||||||
foregroundColor: MaterialStatePropertyAll(
|
Theme.of(context).textTheme.headlineSmall?.color,
|
||||||
PlatformTheme.of(context).textTheme?.subheading?.color),
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: const [
|
children: const [
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
@ -37,7 +37,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
if (breakpoint.isLessThanOrEqualTo(Breakpoints.md))
|
if (breakpoint.isLessThanOrEqualTo(Breakpoints.md))
|
||||||
Flexible(
|
Flexible(
|
||||||
child: PlatformText(
|
child: Text(
|
||||||
playback?.activeTrack.name ?? "Not playing",
|
playback?.activeTrack.name ?? "Not playing",
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, color: color),
|
style: TextStyle(fontWeight: FontWeight.bold, color: color),
|
||||||
@ -50,7 +50,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
PlatformText(
|
Text(
|
||||||
playback?.activeTrack.name ?? "Not playing",
|
playback?.activeTrack.name ?? "Not playing",
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, color: color),
|
style: TextStyle(fontWeight: FontWeight.bold, color: color),
|
||||||
|
@ -3,7 +3,7 @@ import 'dart:ui';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/models/spotube_track.dart';
|
import 'package:spotube/models/spotube_track.dart';
|
||||||
import 'package:spotube/provider/playlist_queue_provider.dart';
|
import 'package:spotube/provider/playlist_queue_provider.dart';
|
||||||
@ -50,16 +50,15 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
margin: const EdgeInsets.all(8.0),
|
margin: const EdgeInsets.all(8.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
color: PlatformTheme.of(context)
|
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.3),
|
||||||
.scaffoldBackgroundColor!
|
|
||||||
.withOpacity(.3),
|
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
appBar: PlatformAppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: PlatformText.subheading(
|
title: Text(
|
||||||
'Alternative Tracks Sources',
|
'Alternative Tracks Sources',
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
),
|
),
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
@ -71,8 +70,8 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
itemCount: siblings.length,
|
itemCount: siblings.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final video = siblings[index];
|
final video = siblings[index];
|
||||||
return PlatformListTile(
|
return ListTile(
|
||||||
title: PlatformText(video.title),
|
title: Text(video.title),
|
||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: UniversalImage(
|
child: UniversalImage(
|
||||||
@ -84,12 +83,12 @@ class SiblingTracksSheet extends HookConsumerWidget {
|
|||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
),
|
),
|
||||||
trailing: PlatformText(
|
trailing: Text(
|
||||||
PrimitiveUtils.toReadableDuration(
|
PrimitiveUtils.toReadableDuration(
|
||||||
video.duration ?? Duration.zero,
|
video.duration ?? Duration.zero,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: PlatformText(video.author),
|
subtitle: Text(video.author),
|
||||||
enabled: playlist?.isLoading != true,
|
enabled: playlist?.isLoading != true,
|
||||||
selected: playlist?.isLoading != true &&
|
selected: playlist?.isLoading != true &&
|
||||||
video.id.value ==
|
video.id.value ==
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
import 'package:fl_query_hooks/fl_query_hooks.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
|
|
||||||
class PlaylistCreateDialog extends HookConsumerWidget {
|
class PlaylistCreateDialog extends HookConsumerWidget {
|
||||||
@ -17,10 +15,10 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
|||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: PlatformTextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showPlatformAlertDialog(
|
showDialog(
|
||||||
context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return HookBuilder(builder: (context) {
|
return HookBuilder(builder: (context) {
|
||||||
final playlistName = useTextEditingController();
|
final playlistName = useTextEditingController();
|
||||||
@ -48,48 +46,18 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
|||||||
navigator.pop();
|
navigator.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAlertDialog(
|
return AlertDialog(
|
||||||
macosAppIcon: Sidebar.brandLogo(),
|
title: const Text("Create a Playlist"),
|
||||||
title: const PlatformText("Create a Playlist"),
|
actions: [
|
||||||
primaryActions: [
|
OutlinedButton(
|
||||||
PlatformBuilder(
|
child: const Text("Cancel"),
|
||||||
fallback: PlatformBuilderFallback.android,
|
onPressed: () {
|
||||||
android: (context, _) {
|
Navigator.pop(context);
|
||||||
return PlatformFilledButton(
|
|
||||||
onPressed: onCreate,
|
|
||||||
child: const Text("Create"),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
ios: (context, data) {
|
|
||||||
return CupertinoDialogAction(
|
|
||||||
isDefaultAction: true,
|
|
||||||
onPressed: onCreate,
|
|
||||||
child: const Text("Create"),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
FilledButton(
|
||||||
secondaryActions: [
|
onPressed: onCreate,
|
||||||
PlatformBuilder(
|
child: const Text("Create"),
|
||||||
fallback: PlatformBuilderFallback.android,
|
|
||||||
android: (context, _) {
|
|
||||||
return PlatformFilledButton(
|
|
||||||
isSecondary: true,
|
|
||||||
child: const Text("Cancel"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
ios: (context, data) {
|
|
||||||
return CupertinoDialogAction(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
isDestructiveAction: true,
|
|
||||||
child: const Text("Cancel"),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
content: Container(
|
content: Container(
|
||||||
@ -98,28 +66,32 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
children: [
|
children: [
|
||||||
PlatformTextField(
|
TextField(
|
||||||
controller: playlistName,
|
controller: playlistName,
|
||||||
placeholder: "Name of the playlist",
|
decoration: const InputDecoration(
|
||||||
label: "Playlist Name",
|
hintText: "Name of the playlist",
|
||||||
|
labelText: "Playlist Name",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
PlatformTextField(
|
TextField(
|
||||||
controller: description,
|
controller: description,
|
||||||
placeholder: "Description...",
|
decoration: const InputDecoration(
|
||||||
|
hintText: "Description...",
|
||||||
|
),
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
PlatformCheckbox(
|
CheckboxListTile(
|
||||||
|
title: const Text("Public"),
|
||||||
value: public.value,
|
value: public.value,
|
||||||
label: const PlatformText("Public"),
|
|
||||||
onChanged: (val) => public.value = val ?? false,
|
onChanged: (val) => public.value = val ?? false,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
PlatformCheckbox(
|
CheckboxListTile(
|
||||||
|
title: const Text("Collaborative"),
|
||||||
value: collaborative.value,
|
value: collaborative.value,
|
||||||
label: const PlatformText("Collaborative"),
|
|
||||||
onChanged: (val) =>
|
onChanged: (val) =>
|
||||||
collaborative.value = val ?? false,
|
collaborative.value = val ?? false,
|
||||||
),
|
),
|
||||||
@ -141,7 +113,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: const [
|
children: const [
|
||||||
Icon(SpotubeIcons.addFilled, size: 40),
|
Icon(SpotubeIcons.addFilled, size: 40),
|
||||||
PlatformText("Create Playlist", style: TextStyle(fontSize: 20)),
|
Text("Create Playlist", style: TextStyle(fontSize: 20)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:macos_ui/macos_ui.dart';
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
|
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/components/player/player_actions.dart';
|
import 'package:spotube/components/player/player_actions.dart';
|
||||||
import 'package:spotube/components/player/player_overlay.dart';
|
import 'package:spotube/components/player/player_overlay.dart';
|
||||||
import 'package:spotube/components/player/player_track_details.dart';
|
import 'package:spotube/components/player/player_track_details.dart';
|
||||||
import 'package:spotube/components/player/player_controls.dart';
|
import 'package:spotube/components/player/player_controls.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
import 'package:spotube/hooks/use_platform_property.dart';
|
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:spotube/provider/playlist_queue_provider.dart';
|
import 'package:spotube/provider/playlist_queue_provider.dart';
|
||||||
@ -52,51 +48,13 @@ class BottomPlayer extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final backgroundColor = usePlatformProperty<Color?>(
|
|
||||||
(context) => PlatformProperty(
|
|
||||||
android: Theme.of(context).backgroundColor,
|
|
||||||
ios: CupertinoTheme.of(context).scaffoldBackgroundColor,
|
|
||||||
macos: MacosTheme.of(context).brightness == Brightness.dark
|
|
||||||
? Colors.grey[800]
|
|
||||||
: Colors.blueGrey[50],
|
|
||||||
linux: Theme.of(context).backgroundColor,
|
|
||||||
windows: fluent_ui.FluentTheme.maybeOf(context)?.micaBackgroundColor,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final border = usePlatformProperty<BoxBorder?>(
|
|
||||||
(context) => PlatformProperty(
|
|
||||||
android: null,
|
|
||||||
ios: Border(
|
|
||||||
top: BorderSide(
|
|
||||||
color: PlatformTheme.of(context).borderColor ?? Colors.transparent,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
macos: Border(
|
|
||||||
top: BorderSide(
|
|
||||||
color: PlatformTheme.of(context).borderColor ?? Colors.transparent,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
linux: Border(
|
|
||||||
top: BorderSide(
|
|
||||||
color: PlatformTheme.of(context).borderColor ?? Colors.transparent,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
windows: null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return DecoratedBox(
|
return DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: backgroundColor,
|
color: Theme.of(context).colorScheme.background,
|
||||||
border: border,
|
|
||||||
),
|
),
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
textStyle: Theme.of(context).textTheme.bodyMedium!,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
@ -141,7 +99,7 @@ class BottomPlayer extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: PlatformSlider(
|
child: Slider(
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
value: volume.value,
|
value: volume.value,
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import 'package:badges/badges.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:flutter/material.dart' hide Badge;
|
import 'package:flutter/material.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/collections/side_bar_tiles.dart';
|
import 'package:spotube/collections/side_bar_tiles.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
@ -16,7 +15,6 @@ import 'package:spotube/provider/user_preferences_provider.dart';
|
|||||||
import 'package:spotube/services/queries/queries.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as fluent_ui;
|
|
||||||
|
|
||||||
class Sidebar extends HookConsumerWidget {
|
class Sidebar extends HookConsumerWidget {
|
||||||
final int selectedIndex;
|
final int selectedIndex;
|
||||||
@ -57,127 +55,64 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
final layoutMode =
|
final layoutMode =
|
||||||
ref.watch(userPreferencesProvider.select((s) => s.layoutMode));
|
ref.watch(userPreferencesProvider.select((s) => s.layoutMode));
|
||||||
|
|
||||||
if (breakpoints.isMd) {
|
|
||||||
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 ||
|
if (layoutMode == LayoutMode.compact ||
|
||||||
(breakpoints.isSm && layoutMode == LayoutMode.adaptive)) {
|
(breakpoints.isSm && layoutMode == LayoutMode.adaptive)) {
|
||||||
return PlatformScaffold(body: child);
|
return Scaffold(body: child);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SafeArea(
|
return Row(
|
||||||
top: false,
|
children: [
|
||||||
child: PlatformSidebar(
|
NavigationRail(
|
||||||
currentIndex: selectedIndex,
|
selectedIndex: selectedIndex,
|
||||||
onIndexChanged: onSelectedIndexChanged,
|
onDestinationSelected: onSelectedIndexChanged,
|
||||||
body: Map.fromEntries(
|
destinations: sidebarTileList.map(
|
||||||
sidebarTileList.map(
|
|
||||||
(e) {
|
(e) {
|
||||||
final icon = Icon(e.icon);
|
return NavigationRailDestination(
|
||||||
return MapEntry(
|
icon: Badge(
|
||||||
PlatformSidebarItem(
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
icon: Badge(
|
isLabelVisible: e.title == "Library" && downloadCount > 0,
|
||||||
badgeColor: PlatformTheme.of(context).primaryColor!,
|
label: Text(
|
||||||
showBadge: e.title == "Library" && downloadCount > 0,
|
downloadCount.toString(),
|
||||||
badgeContent: Text(
|
|
||||||
downloadCount.toString(),
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 10,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: icon,
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
e.title,
|
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
color: Colors.white,
|
||||||
fontSize: 16,
|
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,
|
Expanded(child: child)
|
||||||
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(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,7 +146,7 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (auth != null && data == null)
|
if (auth != null && data == null)
|
||||||
const Center(
|
const Center(
|
||||||
child: PlatformCircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
)
|
)
|
||||||
else if (data != null)
|
else if (data != null)
|
||||||
Flexible(
|
Flexible(
|
||||||
@ -236,16 +171,16 @@ class SidebarFooter extends HookConsumerWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
style: PlatformTheme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
?.body
|
.bodyMedium
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
icon: const Icon(SpotubeIcons.settings),
|
icon: const Icon(SpotubeIcons.settings),
|
||||||
onPressed: () => Sidebar.goToSettings(context)),
|
onPressed: () => Sidebar.goToSettings(context)),
|
||||||
],
|
],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/side_bar_tiles.dart';
|
import 'package:spotube/collections/side_bar_tiles.dart';
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
import 'package:spotube/components/root/sidebar.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
@ -37,19 +37,30 @@ class SpotubeNavigationBar extends HookConsumerWidget {
|
|||||||
if (layoutMode == LayoutMode.extended ||
|
if (layoutMode == LayoutMode.extended ||
|
||||||
(breakpoint.isMoreThan(Breakpoints.sm) &&
|
(breakpoint.isMoreThan(Breakpoints.sm) &&
|
||||||
layoutMode == LayoutMode.adaptive)) return const SizedBox();
|
layoutMode == LayoutMode.adaptive)) return const SizedBox();
|
||||||
return PlatformBottomNavigationBar(
|
return BottomNavigationBar(
|
||||||
items: [
|
items: [
|
||||||
...navbarTileList.map(
|
...navbarTileList.map(
|
||||||
(e) {
|
(e) {
|
||||||
return PlatformBottomNavigationBarItem(
|
return BottomNavigationBarItem(
|
||||||
icon: e.icon,
|
icon: Badge(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
isLabelVisible: e.title == "Library" && downloadCount > 0,
|
||||||
|
label: Text(
|
||||||
|
downloadCount.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Icon(e.icon),
|
||||||
|
),
|
||||||
label: e.title,
|
label: e.title,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
selectedIndex: insideSelectedIndex.value,
|
currentIndex: insideSelectedIndex.value,
|
||||||
onSelectedIndexChanged: (i) {
|
onTap: (i) {
|
||||||
insideSelectedIndex.value = i;
|
insideSelectedIndex.value = i;
|
||||||
if (navbarTileList[i].title == "Settings") {
|
if (navbarTileList[i].title == "Settings") {
|
||||||
Sidebar.goToSettings(context);
|
Sidebar.goToSettings(context);
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
|
||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
|
|
||||||
final colorsMap = {
|
final colorsMap = {
|
||||||
@ -59,48 +57,18 @@ class ColorSchemePickerDialog extends HookConsumerWidget {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAlertDialog(
|
return AlertDialog(
|
||||||
macosAppIcon: Sidebar.brandLogo(),
|
|
||||||
title: Text("Pick ${schemeType.name} color scheme"),
|
title: Text("Pick ${schemeType.name} color scheme"),
|
||||||
primaryActions: [
|
actions: [
|
||||||
PlatformBuilder(
|
OutlinedButton(
|
||||||
android: (context, data) {
|
child: const Text("Cancel"),
|
||||||
return PlatformFilledButton(
|
onPressed: () {
|
||||||
onPressed: onOk,
|
Navigator.pop(context);
|
||||||
child: const Text("Save"),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
ios: (context, data) {
|
|
||||||
return CupertinoDialogAction(
|
|
||||||
onPressed: onOk,
|
|
||||||
isDefaultAction: true,
|
|
||||||
child: const Text("Save"),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
fallback: PlatformBuilderFallback.android,
|
|
||||||
),
|
),
|
||||||
],
|
FilledButton(
|
||||||
secondaryActions: [
|
onPressed: onOk,
|
||||||
PlatformBuilder(
|
child: const Text("Save"),
|
||||||
fallback: PlatformBuilderFallback.android,
|
|
||||||
android: (context, _) {
|
|
||||||
return PlatformFilledButton(
|
|
||||||
isSecondary: true,
|
|
||||||
child: const Text("Cancel"),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
ios: (context, data) {
|
|
||||||
return CupertinoDialogAction(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
isDestructiveAction: true,
|
|
||||||
child: const Text("Cancel"),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
|
|
||||||
class AdaptiveListTile extends HookWidget {
|
class AdaptiveListTile extends HookWidget {
|
||||||
@ -26,7 +25,7 @@ class AdaptiveListTile extends HookWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
|
|
||||||
return PlatformListTile(
|
return ListTile(
|
||||||
title: title,
|
title: title,
|
||||||
subtitle: subtitle,
|
subtitle: subtitle,
|
||||||
trailing:
|
trailing:
|
||||||
@ -35,13 +34,12 @@ class AdaptiveListTile extends HookWidget {
|
|||||||
onTap: breakpoint.isLessThan(breakOn)
|
onTap: breakpoint.isLessThan(breakOn)
|
||||||
? () {
|
? () {
|
||||||
onTap?.call();
|
onTap?.call();
|
||||||
showPlatformAlertDialog(
|
showDialog(
|
||||||
context,
|
context: context,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return StatefulBuilder(builder: (context, update) {
|
return StatefulBuilder(builder: (context, update) {
|
||||||
return PlatformAlertDialog(
|
return AlertDialog(
|
||||||
macosAppIcon: Sidebar.brandLogo(),
|
|
||||||
title: title != null
|
title: title != null
|
||||||
? Row(
|
? Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:popover/popover.dart';
|
import 'package:popover/popover.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
@ -23,12 +23,14 @@ class Action extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (isExpanded != true) {
|
if (isExpanded != true) {
|
||||||
return PlatformIconButton(
|
return IconButton(
|
||||||
icon: icon,
|
icon: icon,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
backgroundColor: backgroundColor,
|
style: IconButton.styleFrom(
|
||||||
tooltip: text is PlatformText
|
backgroundColor: backgroundColor,
|
||||||
? (text as PlatformText).data
|
),
|
||||||
|
tooltip: text is Text
|
||||||
|
? (text as Text).data
|
||||||
: text.toStringShallow().split(",").last.replaceAll(
|
: text.toStringShallow().split(",").last.replaceAll(
|
||||||
"\"",
|
"\"",
|
||||||
"",
|
"",
|
||||||
@ -36,7 +38,7 @@ class Action extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformListTile(
|
return ListTile(
|
||||||
tileColor: backgroundColor,
|
tileColor: backgroundColor,
|
||||||
onTap: onPressed,
|
onTap: onPressed,
|
||||||
leading: icon,
|
leading: icon,
|
||||||
@ -59,7 +61,7 @@ class AdaptiveActions extends HookWidget {
|
|||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
|
|
||||||
if (breakpoint.isLessThan(breakOn)) {
|
if (breakpoint.isLessThan(breakOn)) {
|
||||||
return PlatformIconButton(
|
return IconButton(
|
||||||
icon: const Icon(SpotubeIcons.moreHorizontal),
|
icon: const Icon(SpotubeIcons.moreHorizontal),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showPopover(
|
showPopover(
|
||||||
@ -83,8 +85,7 @@ class AdaptiveActions extends HookWidget {
|
|||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
backgroundColor:
|
backgroundColor: Theme.of(context).cardColor,
|
||||||
PlatformTheme.of(context).secondaryBackgroundColor!,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:popover/popover.dart';
|
import 'package:popover/popover.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
|
|
||||||
@ -20,11 +20,11 @@ class CompactSearch extends HookWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PlatformIconButton(
|
return IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showPopover(
|
showPopover(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: PlatformTheme.of(context).secondaryBackgroundColor!,
|
backgroundColor: Theme.of(context).cardColor,
|
||||||
transitionDuration: const Duration(milliseconds: 100),
|
transitionDuration: const Duration(milliseconds: 100),
|
||||||
barrierColor: Colors.transparent,
|
barrierColor: Colors.transparent,
|
||||||
arrowDxOffset: -6,
|
arrowDxOffset: -6,
|
||||||
@ -32,14 +32,13 @@ class CompactSearch extends HookWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
width: 300,
|
width: 300,
|
||||||
child: PlatformTextField(
|
child: TextField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
placeholder: placeholder,
|
decoration: InputDecoration(
|
||||||
prefixIcon: icon,
|
hintText: placeholder,
|
||||||
padding: platform == TargetPlatform.android
|
prefixIcon: Icon(icon),
|
||||||
? const EdgeInsets.all(0)
|
),
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
|
||||||
|
|
||||||
class ConfirmDownloadDialog extends StatelessWidget {
|
class ConfirmDownloadDialog extends StatelessWidget {
|
||||||
const ConfirmDownloadDialog({Key? key}) : super(key: key);
|
const ConfirmDownloadDialog({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PlatformAlertDialog(
|
return AlertDialog(
|
||||||
macosAppIcon: Sidebar.brandLogo(),
|
|
||||||
title: Padding(
|
title: Padding(
|
||||||
padding: const EdgeInsets.all(15),
|
padding: const EdgeInsets.all(15),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -62,48 +59,21 @@ class ConfirmDownloadDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
primaryActions: [
|
actions: [
|
||||||
PlatformBuilder(
|
OutlinedButton(
|
||||||
android: (context, _) {
|
child: const Text("Decline"),
|
||||||
return PlatformFilledButton(
|
onPressed: () {
|
||||||
style: const ButtonStyle(
|
Navigator.pop(context, false);
|
||||||
foregroundColor: MaterialStatePropertyAll(Colors.white),
|
|
||||||
backgroundColor: MaterialStatePropertyAll(Colors.red),
|
|
||||||
),
|
|
||||||
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"),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
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:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/provider/spotify_provider.dart';
|
import 'package:spotube/provider/spotify_provider.dart';
|
||||||
import 'package:spotube/services/queries/queries.dart';
|
import 'package:spotube/services/queries/queries.dart';
|
||||||
@ -42,61 +42,32 @@ class PlaylistAddTrackDialog extends HookConsumerWidget {
|
|||||||
).then((_) => Navigator.pop(context));
|
).then((_) => Navigator.pop(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAlertDialog(
|
return AlertDialog(
|
||||||
title: const PlatformText("Add to Playlist"),
|
title: const Text("Add to Playlist"),
|
||||||
secondaryActions: [
|
actions: [
|
||||||
PlatformBuilder(
|
OutlinedButton(
|
||||||
fallback: PlatformBuilderFallback.android,
|
child: const Text("Cancel"),
|
||||||
android: (context, _) {
|
onPressed: () {
|
||||||
return PlatformFilledButton(
|
Navigator.pop(context);
|
||||||
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(
|
||||||
primaryActions: [
|
onPressed: onAdd,
|
||||||
PlatformBuilder(
|
child: const Text("Add"),
|
||||||
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"),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
height: 300,
|
height: 300,
|
||||||
width: 300,
|
width: 300,
|
||||||
child: !userPlaylists.hasData
|
child: !userPlaylists.hasData
|
||||||
? const Center(child: PlatformCircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: filteredPlaylists!.length,
|
itemCount: filteredPlaylists!.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final playlist = filteredPlaylists.elementAt(index);
|
final playlist = filteredPlaylists.elementAt(index);
|
||||||
return PlatformCheckbox(
|
return CheckboxListTile(
|
||||||
label: PlatformText(playlist.name!),
|
title: Text(playlist.name!),
|
||||||
value: playlistsCheck.value[playlist.id] ?? false,
|
value: playlistsCheck.value[playlist.id] ?? false,
|
||||||
onChanged: (val) {
|
onChanged: (val) {
|
||||||
playlistsCheck.value = {
|
playlistsCheck.value = {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
|
||||||
|
|
||||||
Future<bool> showPromptDialog({
|
Future<bool> showPromptDialog({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
@ -9,40 +7,21 @@ Future<bool> showPromptDialog({
|
|||||||
String okText = "Ok",
|
String okText = "Ok",
|
||||||
String cancelText = "Cancel",
|
String cancelText = "Cancel",
|
||||||
}) async {
|
}) async {
|
||||||
return showPlatformAlertDialog<bool>(
|
return showDialog<bool>(
|
||||||
context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return PlatformAlertDialog(
|
return AlertDialog(
|
||||||
title: PlatformText(title),
|
title: Text(title),
|
||||||
content: PlatformText(message),
|
content: Text(message),
|
||||||
macosAppIcon: Sidebar.brandLogo(),
|
actions: [
|
||||||
primaryActions: [
|
OutlinedButton(
|
||||||
if (platform == TargetPlatform.iOS)
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
CupertinoDialogAction(
|
child: Text(cancelText),
|
||||||
isDefaultAction: false,
|
),
|
||||||
isDestructiveAction: true,
|
FilledButton(
|
||||||
child: PlatformText(okText),
|
child: Text(okText),
|
||||||
onPressed: () => Navigator.of(context).pop(true),
|
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),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
|
||||||
|
|
||||||
final replaceDownloadedFileState = StateProvider<bool?>((ref) => null);
|
final replaceDownloadedFileState = StateProvider<bool?>((ref) => null);
|
||||||
|
|
||||||
@ -16,8 +14,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final groupValue = ref.watch(replaceDownloadedFileState);
|
final groupValue = ref.watch(replaceDownloadedFileState);
|
||||||
|
|
||||||
return PlatformAlertDialog(
|
return AlertDialog(
|
||||||
macosAppIcon: Sidebar.brandLogo(),
|
|
||||||
title: Text("Track ${track.name} Already Exists"),
|
title: Text("Track ${track.name} Already Exists"),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -26,7 +23,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
|
|||||||
RadioListTile<bool>(
|
RadioListTile<bool>(
|
||||||
dense: true,
|
dense: true,
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
activeColor: PlatformTheme.of(context).primaryColor,
|
activeColor: Theme.of(context).primaryColor,
|
||||||
value: true,
|
value: true,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -39,7 +36,7 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
|
|||||||
RadioListTile<bool>(
|
RadioListTile<bool>(
|
||||||
dense: true,
|
dense: true,
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
activeColor: PlatformTheme.of(context).primaryColor,
|
activeColor: Theme.of(context).primaryColor,
|
||||||
value: false,
|
value: false,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -51,48 +48,17 @@ class ReplaceDownloadedDialog extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
primaryActions: [
|
actions: [
|
||||||
PlatformBuilder(
|
OutlinedButton(
|
||||||
fallback: PlatformBuilderFallback.android,
|
child: const Text("No"),
|
||||||
android: (context, _) {
|
onPressed: () {
|
||||||
return PlatformFilledButton(
|
Navigator.pop(context, false);
|
||||||
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"),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
FilledButton(
|
||||||
secondaryActions: [
|
child: const Text("Yes"),
|
||||||
PlatformBuilder(
|
onPressed: () {
|
||||||
fallback: PlatformBuilderFallback.android,
|
Navigator.pop(context, true);
|
||||||
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"),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
import 'package:spotube/utils/service_utils.dart';
|
import 'package:spotube/utils/service_utils.dart';
|
||||||
|
|
||||||
@ -20,10 +20,10 @@ class AnonymousFallback extends ConsumerWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const PlatformText("You're not logged in"),
|
const Text("You're not logged in"),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
PlatformFilledButton(
|
FilledButton(
|
||||||
child: const PlatformText("Login with Spotify"),
|
child: const Text("Login with Spotify"),
|
||||||
onPressed: () => ServiceUtils.navigate(context, "/settings"),
|
onPressed: () => ServiceUtils.navigate(context, "/settings"),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -2,7 +2,7 @@ import 'package:fl_query/fl_query.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/hooks/use_palette_color.dart';
|
import 'package:spotube/hooks/use_palette_color.dart';
|
||||||
@ -34,7 +34,7 @@ class HeartButton extends ConsumerWidget {
|
|||||||
|
|
||||||
if (auth == null) return Container();
|
if (auth == null) return Container();
|
||||||
|
|
||||||
return PlatformIconButton(
|
return IconButton(
|
||||||
tooltip: tooltip,
|
tooltip: tooltip,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
icon ?? (!isLiked ? SpotubeIcons.heart : SpotubeIcons.heartFilled),
|
icon ?? (!isLiked ? SpotubeIcons.heart : SpotubeIcons.heartFilled),
|
||||||
@ -95,7 +95,7 @@ class TrackHeartButton extends HookConsumerWidget {
|
|||||||
useQueries.playlist.tracksOfQuery(ref, "user-liked-tracks");
|
useQueries.playlist.tracksOfQuery(ref, "user-liked-tracks");
|
||||||
final toggler = useTrackToggleLike(track, ref);
|
final toggler = useTrackToggleLike(track, ref);
|
||||||
if (toggler.item3.isLoading || !toggler.item3.hasData) {
|
if (toggler.item3.isLoading || !toggler.item3.hasData) {
|
||||||
return const PlatformCircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
return HeartButton(
|
return HeartButton(
|
||||||
@ -150,7 +150,7 @@ class PlaylistHeartButton extends HookConsumerWidget {
|
|||||||
).dominantColor;
|
).dominantColor;
|
||||||
|
|
||||||
if (me.isLoading || !me.hasData) {
|
if (me.isLoading || !me.hasData) {
|
||||||
return const PlatformCircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
return HeartButton(
|
return HeartButton(
|
||||||
@ -193,7 +193,7 @@ class AlbumHeartButton extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (me.isLoading || !me.hasData) {
|
if (me.isLoading || !me.hasData) {
|
||||||
return const PlatformCircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
return HeartButton(
|
return HeartButton(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
|
|
||||||
class AnchorButton<T> extends HookWidget {
|
class AnchorButton<T> extends HookWidget {
|
||||||
final String text;
|
final String text;
|
||||||
@ -29,7 +28,7 @@ class AnchorButton<T> extends HookWidget {
|
|||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
cursor: MaterialStateMouseCursor.clickable,
|
cursor: MaterialStateMouseCursor.clickable,
|
||||||
child: PlatformText(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
style: style.copyWith(
|
style: style.copyWith(
|
||||||
decoration:
|
decoration:
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
class PageWindowTitleBar extends StatefulHookWidget with PreferredSizeWidget {
|
class PageWindowTitleBar extends StatefulHookWidget
|
||||||
|
implements PreferredSizeWidget {
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
final bool automaticallyImplyLeading;
|
final bool automaticallyImplyLeading;
|
||||||
final List<Widget>? actions;
|
final List<Widget>? actions;
|
||||||
@ -18,13 +19,12 @@ class PageWindowTitleBar extends StatefulHookWidget with PreferredSizeWidget {
|
|||||||
final TextStyle? toolbarTextStyle;
|
final TextStyle? toolbarTextStyle;
|
||||||
final TextStyle? titleTextStyle;
|
final TextStyle? titleTextStyle;
|
||||||
final double? titleWidth;
|
final double? titleWidth;
|
||||||
final Widget? center;
|
final Widget? title;
|
||||||
final bool hideWhenWindows;
|
|
||||||
|
|
||||||
PageWindowTitleBar({
|
const PageWindowTitleBar({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.center,
|
this.title,
|
||||||
this.toolbarOpacity = 1,
|
this.toolbarOpacity = 1,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
this.actionsIconTheme,
|
this.actionsIconTheme,
|
||||||
@ -37,13 +37,10 @@ class PageWindowTitleBar extends StatefulHookWidget with PreferredSizeWidget {
|
|||||||
this.titleTextStyle,
|
this.titleTextStyle,
|
||||||
this.titleWidth,
|
this.titleWidth,
|
||||||
this.toolbarTextStyle,
|
this.toolbarTextStyle,
|
||||||
this.hideWhenWindows = true,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size get preferredSize => Size.fromHeight(
|
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||||
platform == TargetPlatform.windows ? 33 : kToolbarHeight,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PageWindowTitleBar> createState() => _PageWindowTitleBarState();
|
State<PageWindowTitleBar> createState() => _PageWindowTitleBarState();
|
||||||
@ -54,33 +51,6 @@ class _PageWindowTitleBarState extends State<PageWindowTitleBar> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isMaximized = useState<bool?>(null);
|
final isMaximized = useState<bool?>(null);
|
||||||
|
|
||||||
useEffect(() {
|
|
||||||
if (platform == TargetPlatform.windows &&
|
|
||||||
widget.hideWhenWindows &&
|
|
||||||
Navigator.of(context).canPop()) {
|
|
||||||
final entry = OverlayEntry(
|
|
||||||
builder: (context) => const Positioned(
|
|
||||||
left: 5,
|
|
||||||
top: 5,
|
|
||||||
child: PlatformBackButton(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
Overlay.of(context)?.insert(entry);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () {
|
|
||||||
entry.remove();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}, [platform, widget.hideWhenWindows]);
|
|
||||||
|
|
||||||
if (platform == TargetPlatform.windows && widget.hideWhenWindows) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
maximizeOrRestore() async {
|
maximizeOrRestore() async {
|
||||||
if (await windowManager.isMaximized()) {
|
if (await windowManager.isMaximized()) {
|
||||||
await windowManager.unmaximize();
|
await windowManager.unmaximize();
|
||||||
@ -91,42 +61,52 @@ class _PageWindowTitleBarState extends State<PageWindowTitleBar> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformAppBar(
|
return GestureDetector(
|
||||||
actions: [
|
onHorizontalDragStart: (details) {
|
||||||
...?widget.actions,
|
if (kIsDesktop) {
|
||||||
if (!kIsMacOS && !kIsMobile)
|
windowManager.startDragging();
|
||||||
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();
|
|
||||||
},
|
},
|
||||||
title: widget.center,
|
onVerticalDragStart: (details) {
|
||||||
toolbarOpacity: widget.toolbarOpacity,
|
if (kIsDesktop) {
|
||||||
backgroundColor: widget.backgroundColor,
|
windowManager.startDragging();
|
||||||
actionsIconTheme: widget.actionsIconTheme,
|
}
|
||||||
automaticallyImplyLeading: widget.automaticallyImplyLeading,
|
},
|
||||||
centerTitle: widget.centerTitle,
|
child: AppBar(
|
||||||
foregroundColor: widget.foregroundColor,
|
leading: widget.leading,
|
||||||
leading: widget.leading,
|
automaticallyImplyLeading: widget.automaticallyImplyLeading,
|
||||||
leadingWidth: widget.leadingWidth,
|
actions: [
|
||||||
titleSpacing: widget.titleSpacing,
|
...?widget.actions,
|
||||||
titleTextStyle: widget.titleTextStyle,
|
if (kIsDesktop && !kIsMacOS) ...[
|
||||||
titleWidth: widget.titleWidth,
|
IconButton(
|
||||||
toolbarTextStyle: widget.toolbarTextStyle,
|
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/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/hover_builder.dart';
|
import 'package:spotube/components/shared/hover_builder.dart';
|
||||||
import 'package:spotube/components/shared/spotube_marquee_text.dart';
|
import 'package:spotube/components/shared/spotube_marquee_text.dart';
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/hooks/use_platform_property.dart';
|
|
||||||
|
|
||||||
enum PlaybuttonCardViewType { square, list }
|
enum PlaybuttonCardViewType { square, list }
|
||||||
|
|
||||||
@ -38,32 +37,7 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final backgroundColor = PlatformTheme.of(context).secondaryBackgroundColor;
|
final backgroundColor = Theme.of(context).cardColor;
|
||||||
|
|
||||||
final boxShadow = usePlatformProperty<BoxShadow?>(
|
|
||||||
(context) => PlatformProperty(
|
|
||||||
android: BoxShadow(
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 3),
|
|
||||||
spreadRadius: 5,
|
|
||||||
color: Theme.of(context).colorScheme.shadow,
|
|
||||||
),
|
|
||||||
ios: null,
|
|
||||||
macos: null,
|
|
||||||
linux: BoxShadow(
|
|
||||||
blurRadius: 6,
|
|
||||||
color: Theme.of(context).shadowColor.withOpacity(0.3),
|
|
||||||
),
|
|
||||||
windows: null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final splash = usePlatformProperty<InteractiveInkFeatureFactory?>(
|
|
||||||
(context) => PlatformProperty.only(
|
|
||||||
android: InkRipple.splashFactory,
|
|
||||||
other: NoSplash.splashFactory,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final isSquare = viewType == PlaybuttonCardViewType.square;
|
final isSquare = viewType == PlaybuttonCardViewType.square;
|
||||||
|
|
||||||
@ -72,7 +46,6 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
splashFactory: splash,
|
|
||||||
highlightColor: Colors.black12,
|
highlightColor: Colors.black12,
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
@ -80,18 +53,19 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
maxHeight: !isSquare ? 60 : double.infinity,
|
maxHeight: !isSquare ? 60 : double.infinity,
|
||||||
),
|
),
|
||||||
child: HoverBuilder(builder: (context, isHovering) {
|
child: HoverBuilder(builder: (context, isHovering) {
|
||||||
final playButton = PlatformIconButton(
|
final playButton = IconButton(
|
||||||
onPressed: onPlaybuttonPressed,
|
onPressed: onPlaybuttonPressed,
|
||||||
backgroundColor: PlatformTheme.of(context).primaryColor,
|
style: IconButton.styleFrom(
|
||||||
hoverColor:
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
PlatformTheme.of(context).primaryColor?.withOpacity(0.5),
|
hoverColor: Theme.of(context).primaryColor.withOpacity(0.5),
|
||||||
|
),
|
||||||
icon: isLoading
|
icon: isLoading
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
height: 23,
|
height: 23,
|
||||||
width: 23,
|
width: 23,
|
||||||
child: PlatformCircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: ThemeData.estimateBrightnessForColor(
|
color: ThemeData.estimateBrightnessForColor(
|
||||||
PlatformTheme.of(context).primaryColor!,
|
Theme.of(context).primaryColor,
|
||||||
) ==
|
) ==
|
||||||
Brightness.dark
|
Brightness.dark
|
||||||
? Colors.white
|
? Colors.white
|
||||||
@ -103,31 +77,22 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final addToQueueButton = PlatformIconButton(
|
final addToQueueButton = IconButton(
|
||||||
onPressed: isLoading ? null : onAddToQueuePressed,
|
onPressed: isLoading ? null : onAddToQueuePressed,
|
||||||
backgroundColor:
|
style: IconButton.styleFrom(
|
||||||
PlatformTheme.of(context).secondaryBackgroundColor,
|
backgroundColor: Theme.of(context).cardColor,
|
||||||
hoverColor: PlatformTheme.of(context)
|
hoverColor: Theme.of(context)
|
||||||
.secondaryBackgroundColor
|
.cardColor
|
||||||
?.withOpacity(0.5),
|
.withOpacity(isLoading ? 1 : 0.5),
|
||||||
|
),
|
||||||
icon: const Icon(SpotubeIcons.queueAdd),
|
icon: const Icon(SpotubeIcons.queueAdd),
|
||||||
);
|
);
|
||||||
final image = Padding(
|
final image = ClipRRect(
|
||||||
padding: EdgeInsets.all(
|
borderRadius: BorderRadius.circular(8),
|
||||||
platform == TargetPlatform.windows ? 5 : 0,
|
child: UniversalImage(
|
||||||
),
|
path: imageUrl,
|
||||||
child: ClipRRect(
|
width: isSquare ? 200 : 60,
|
||||||
borderRadius: BorderRadius.circular(
|
placeholder: (context, url) => Assets.placeholder.image(),
|
||||||
[TargetPlatform.windows, TargetPlatform.linux]
|
|
||||||
.contains(platform)
|
|
||||||
? 5
|
|
||||||
: 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,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
if (!isPlaying) addToQueueButton,
|
if (!isPlaying) addToQueueButton,
|
||||||
if (platform != TargetPlatform.linux)
|
const SizedBox(height: 5),
|
||||||
const SizedBox(height: 5),
|
|
||||||
playButton,
|
playButton,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -178,7 +142,7 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
height: 30,
|
height: 30,
|
||||||
child: SpotubeMarqueeText(
|
child: SpotubeMarqueeText(
|
||||||
text: description!,
|
text: description!,
|
||||||
style: PlatformTextTheme.of(context).caption,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
isHovering: isHovering,
|
isHovering: isHovering,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -205,14 +169,15 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: title,
|
text: title,
|
||||||
style: PlatformTextTheme.of(context)
|
style: Theme.of(context)
|
||||||
.body
|
.textTheme
|
||||||
|
.bodyMedium
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
if (description != null)
|
if (description != null)
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '\n$description',
|
text: '\n$description',
|
||||||
style: PlatformTextTheme.of(context).caption,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -235,23 +200,15 @@ class PlaybuttonCard extends HookWidget {
|
|||||||
return Ink(
|
return Ink(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(8),
|
||||||
[TargetPlatform.windows, TargetPlatform.linux]
|
|
||||||
.contains(platform)
|
|
||||||
? 5
|
|
||||||
: 8,
|
|
||||||
),
|
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
if (boxShadow != null) boxShadow,
|
BoxShadow(
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 3),
|
||||||
|
spreadRadius: 5,
|
||||||
|
color: Theme.of(context).colorScheme.shadow,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
border: [TargetPlatform.windows, TargetPlatform.macOS]
|
|
||||||
.contains(platform)
|
|
||||||
? Border.all(
|
|
||||||
color: PlatformTheme.of(context).borderColor ??
|
|
||||||
Colors.transparent,
|
|
||||||
width: 1,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
child: isSquare ? square : list,
|
child: isSquare ? square : list,
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:skeleton_text/skeleton_text.dart';
|
import 'package:skeleton_text/skeleton_text.dart';
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_track_tile.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_track_tile.dart';
|
||||||
import 'package:spotube/extensions/theme.dart';
|
import 'package:spotube/extensions/theme.dart';
|
||||||
@ -11,7 +11,7 @@ class ShimmerArtistProfile extends HookWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = PlatformTheme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final shimmerTheme = ShimmerColorTheme(
|
final shimmerTheme = ShimmerColorTheme(
|
||||||
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
||||||
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_playbutton_card.dart';
|
||||||
import 'package:spotube/extensions/theme.dart';
|
import 'package:spotube/extensions/theme.dart';
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ class ShimmerCategories extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = PlatformTheme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final shimmerTheme = ShimmerColorTheme(
|
final shimmerTheme = ShimmerColorTheme(
|
||||||
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:skeleton_text/skeleton_text.dart';
|
import 'package:skeleton_text/skeleton_text.dart';
|
||||||
import 'package:spotube/extensions/theme.dart';
|
import 'package:spotube/extensions/theme.dart';
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
@ -12,7 +12,7 @@ class ShimmerLyrics extends HookWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = PlatformTheme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final shimmerTheme = ShimmerColorTheme(
|
final shimmerTheme = ShimmerColorTheme(
|
||||||
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
||||||
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/extensions/theme.dart';
|
import 'package:spotube/extensions/theme.dart';
|
||||||
|
|
||||||
class ShimmerPlaybuttonCardPainter extends CustomPainter {
|
class ShimmerPlaybuttonCardPainter extends CustomPainter {
|
||||||
@ -53,7 +53,7 @@ class ShimmerPlaybuttonCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = PlatformTheme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final shimmerTheme = ShimmerColorTheme(
|
final shimmerTheme = ShimmerColorTheme(
|
||||||
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
||||||
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/extensions/theme.dart';
|
import 'package:spotube/extensions/theme.dart';
|
||||||
|
|
||||||
class ShimmerTrackTilePainter extends CustomPainter {
|
class ShimmerTrackTilePainter extends CustomPainter {
|
||||||
@ -67,7 +67,7 @@ class ShimmerTrackTile extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDark = PlatformTheme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
final shimmerTheme = ShimmerColorTheme(
|
final shimmerTheme = ShimmerColorTheme(
|
||||||
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
shimmerBackgroundColor: isDark ? Colors.grey[700] : Colors.grey[200],
|
||||||
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
shimmerColor: isDark ? Colors.grey[800] : Colors.grey[300],
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/library/user_local_tracks.dart';
|
import 'package:spotube/components/library/user_local_tracks.dart';
|
||||||
|
|
||||||
@ -14,39 +14,41 @@ class SortTracksDropdown extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PlatformPopupMenuButton<SortBy>(
|
return PopupMenuButton<SortBy>(
|
||||||
items: [
|
itemBuilder: (context) {
|
||||||
PlatformPopupMenuItem(
|
return [
|
||||||
value: SortBy.none,
|
PopupMenuItem(
|
||||||
enabled: value != SortBy.none,
|
value: SortBy.none,
|
||||||
child: const Text("None"),
|
enabled: value != SortBy.none,
|
||||||
),
|
child: const Text("None"),
|
||||||
PlatformPopupMenuItem(
|
),
|
||||||
value: SortBy.ascending,
|
PopupMenuItem(
|
||||||
enabled: value != SortBy.ascending,
|
value: SortBy.ascending,
|
||||||
child: const Text("Sort by A-Z"),
|
enabled: value != SortBy.ascending,
|
||||||
),
|
child: const Text("Sort by A-Z"),
|
||||||
PlatformPopupMenuItem(
|
),
|
||||||
value: SortBy.descending,
|
PopupMenuItem(
|
||||||
enabled: value != SortBy.descending,
|
value: SortBy.descending,
|
||||||
child: const Text("Sort by Z-A"),
|
enabled: value != SortBy.descending,
|
||||||
),
|
child: const Text("Sort by Z-A"),
|
||||||
PlatformPopupMenuItem(
|
),
|
||||||
value: SortBy.dateAdded,
|
PopupMenuItem(
|
||||||
enabled: value != SortBy.dateAdded,
|
value: SortBy.dateAdded,
|
||||||
child: const Text("Sort by Date"),
|
enabled: value != SortBy.dateAdded,
|
||||||
),
|
child: const Text("Sort by Date"),
|
||||||
PlatformPopupMenuItem(
|
),
|
||||||
value: SortBy.artist,
|
PopupMenuItem(
|
||||||
enabled: value != SortBy.artist,
|
value: SortBy.artist,
|
||||||
child: const Text("Sort by Artist"),
|
enabled: value != SortBy.artist,
|
||||||
),
|
child: const Text("Sort by Artist"),
|
||||||
PlatformPopupMenuItem(
|
),
|
||||||
value: SortBy.album,
|
PopupMenuItem(
|
||||||
enabled: value != SortBy.album,
|
value: SortBy.album,
|
||||||
child: const Text("Sort by Album"),
|
enabled: value != SortBy.album,
|
||||||
),
|
child: const Text("Sort by Album"),
|
||||||
],
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
onSelected: onChanged,
|
onSelected: onChanged,
|
||||||
tooltip: "Sort tracks",
|
tooltip: "Sort tracks",
|
||||||
child: const Icon(SpotubeIcons.sort),
|
child: const Icon(SpotubeIcons.sort),
|
||||||
|
@ -1,27 +1,5 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
|
|
||||||
class SpotubePage extends CustomTransitionPage {
|
class SpotubePage<T> extends MaterialPage<T> {
|
||||||
SpotubePage({
|
const SpotubePage({required super.child});
|
||||||
required super.child,
|
|
||||||
}) : super(
|
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return child;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Route createRoute(BuildContext context) {
|
|
||||||
if (platform == TargetPlatform.windows) {
|
|
||||||
return FluentPageRoute(
|
|
||||||
builder: (context) => child,
|
|
||||||
settings: this,
|
|
||||||
maintainState: maintainState,
|
|
||||||
barrierLabel: barrierLabel,
|
|
||||||
fullscreenDialog: fullscreenDialog,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return super.createRoute(context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/compact_search.dart';
|
import 'package:spotube/components/shared/compact_search.dart';
|
||||||
@ -72,7 +72,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
|
|
||||||
final List<Widget> buttons = [
|
final List<Widget> buttons = [
|
||||||
if (showShare)
|
if (showShare)
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.share,
|
SpotubeIcons.share,
|
||||||
color: color?.titleTextColor,
|
color: color?.titleTextColor,
|
||||||
@ -80,7 +80,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
onPressed: onShare,
|
onPressed: onShare,
|
||||||
),
|
),
|
||||||
if (heartBtn != null && auth != null) heartBtn!,
|
if (heartBtn != null && auth != null) heartBtn!,
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: "Shuffle",
|
tooltip: "Shuffle",
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.shuffle,
|
SpotubeIcons.shuffle,
|
||||||
@ -91,7 +91,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
// add to queue playlist
|
// add to queue playlist
|
||||||
if (!isPlaying)
|
if (!isPlaying)
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
onPressed: tracksSnapshot.data != null ? onAddToQueue : null,
|
onPressed: tracksSnapshot.data != null ? onAddToQueue : null,
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.queueAdd,
|
SpotubeIcons.queueAdd,
|
||||||
@ -99,8 +99,10 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
// play playlist
|
// play playlist
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
backgroundColor: PlatformTheme.of(context).primaryColor,
|
style: IconButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
onPressed: tracksSnapshot.data != null ? onPlay : null,
|
onPressed: tracksSnapshot.data != null ? onPlay : null,
|
||||||
icon: Icon(isPlaying ? SpotubeIcons.stop : SpotubeIcons.play),
|
icon: Icon(isPlaying ? SpotubeIcons.stop : SpotubeIcons.play),
|
||||||
),
|
),
|
||||||
@ -136,7 +138,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
}, [tracksSnapshot.data, searchText.value]);
|
}, [tracksSnapshot.data, searchText.value]);
|
||||||
|
|
||||||
useCustomStatusBarColor(
|
useCustomStatusBarColor(
|
||||||
color?.color ?? PlatformTheme.of(context).scaffoldBackgroundColor!,
|
color?.color ?? Theme.of(context).scaffoldBackgroundColor,
|
||||||
GoRouter.of(context).location == routePath,
|
GoRouter.of(context).location == routePath,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -156,60 +158,38 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
|
|
||||||
final searchbar = ConstrainedBox(
|
final searchbar = ConstrainedBox(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
maxWidth: 400,
|
maxWidth: 300,
|
||||||
maxHeight: 50,
|
maxHeight: 50,
|
||||||
),
|
),
|
||||||
child: PlatformTextField(
|
child: TextField(
|
||||||
controller: searchController,
|
controller: searchController,
|
||||||
onChanged: (value) => searchText.value = value,
|
onChanged: (value) => searchText.value = value,
|
||||||
placeholder: "Search tracks...",
|
style: TextStyle(color: color?.titleTextColor),
|
||||||
backgroundColor: Colors.transparent,
|
decoration: InputDecoration(
|
||||||
focusedBackgroundColor: Colors.transparent,
|
hintText: "Search tracks...",
|
||||||
style: TextStyle(
|
hintStyle: TextStyle(color: color?.titleTextColor),
|
||||||
color: color?.titleTextColor,
|
border: Theme.of(context).inputDecorationTheme.border?.copyWith(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: color?.titleTextColor ?? Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
return SafeArea(
|
||||||
child: PlatformScaffold(
|
child: Scaffold(
|
||||||
appBar: kIsDesktop
|
appBar: kIsDesktop
|
||||||
? PageWindowTitleBar(
|
? PageWindowTitleBar(
|
||||||
backgroundColor: color?.color,
|
backgroundColor: color?.color,
|
||||||
foregroundColor: color?.titleTextColor,
|
foregroundColor: color?.titleTextColor,
|
||||||
|
leadingWidth: 400,
|
||||||
leading: Row(
|
leading: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
PlatformBackButton(color: color?.titleTextColor),
|
BackButton(color: color?.titleTextColor),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
searchbar,
|
searchbar,
|
||||||
],
|
],
|
||||||
@ -239,18 +219,19 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
expandedHeight: 400,
|
expandedHeight: 400,
|
||||||
automaticallyImplyLeading: kIsMobile,
|
automaticallyImplyLeading: kIsMobile,
|
||||||
leading: kIsMobile
|
leading: kIsMobile
|
||||||
? PlatformBackButton(color: color?.titleTextColor)
|
? BackButton(color: color?.titleTextColor)
|
||||||
: null,
|
: null,
|
||||||
iconTheme: IconThemeData(color: color?.titleTextColor),
|
iconTheme: IconThemeData(color: color?.titleTextColor),
|
||||||
primary: true,
|
primary: true,
|
||||||
backgroundColor: color?.color,
|
backgroundColor: color?.color,
|
||||||
title: collapsed.value
|
title: collapsed.value
|
||||||
? PlatformText.headline(
|
? Text(
|
||||||
title,
|
title,
|
||||||
style: TextStyle(
|
style:
|
||||||
color: color?.titleTextColor,
|
Theme.of(context).textTheme.titleLarge!.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
color: color?.titleTextColor,
|
||||||
),
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
@ -268,7 +249,6 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Material(
|
child: Material(
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@ -299,15 +279,18 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
PlatformText.headline(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: TextStyle(
|
style: Theme.of(context)
|
||||||
color: color?.titleTextColor,
|
.textTheme
|
||||||
fontWeight: FontWeight.w600,
|
.titleLarge!
|
||||||
),
|
.copyWith(
|
||||||
|
color: color?.titleTextColor,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (description != null)
|
if (description != null)
|
||||||
PlatformText(
|
Text(
|
||||||
description!,
|
description!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: color?.bodyTextColor,
|
color: color?.bodyTextColor,
|
||||||
@ -335,7 +318,7 @@ class TrackCollectionView<T> extends HookConsumerWidget {
|
|||||||
return const ShimmerTrackTile();
|
return const ShimmerTrackTile();
|
||||||
} else if (tracksSnapshot.hasError) {
|
} else if (tracksSnapshot.hasError) {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: PlatformText("Error ${tracksSnapshot.error}"));
|
child: Text("Error ${tracksSnapshot.error}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return TracksTableView(
|
return TracksTableView(
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart' hide Action;
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart' hide Image;
|
import 'package:spotify/spotify.dart' hide Image;
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
@ -11,7 +11,6 @@ import 'package:spotube/components/shared/adaptive/adaptive_popup_menu_button.da
|
|||||||
import 'package:spotube/components/shared/heart_button.dart';
|
import 'package:spotube/components/shared/heart_button.dart';
|
||||||
import 'package:spotube/components/shared/links/link_text.dart';
|
import 'package:spotube/components/shared/links/link_text.dart';
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
|
||||||
import 'package:spotube/hooks/use_breakpoints.dart';
|
import 'package:spotube/hooks/use_breakpoints.dart';
|
||||||
import 'package:spotube/models/logger.dart';
|
import 'package:spotube/models/logger.dart';
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
@ -85,7 +84,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
SnackBar(
|
SnackBar(
|
||||||
width: 300,
|
width: 300,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
content: PlatformText(
|
content: Text(
|
||||||
"Copied $data to clipboard",
|
"Copied $data to clipboard",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
@ -95,78 +94,77 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> actionAddToPlaylist() async {
|
Future<void> actionAddToPlaylist() async {
|
||||||
showPlatformAlertDialog(context, builder: (context) {
|
showDialog(
|
||||||
return FutureBuilder<Iterable<PlaylistSimple>>(
|
context: context,
|
||||||
future: spotify.playlists.me.all().then((playlists) async {
|
builder: (context) {
|
||||||
final me = await spotify.me.get();
|
return FutureBuilder<Iterable<PlaylistSimple>>(
|
||||||
return playlists.where((playlist) =>
|
future: spotify.playlists.me.all().then((playlists) async {
|
||||||
playlist.owner?.id != null && playlist.owner!.id == me.id);
|
final me = await spotify.me.get();
|
||||||
}),
|
return playlists.where((playlist) =>
|
||||||
builder: (context, snapshot) {
|
playlist.owner?.id != null &&
|
||||||
return HookBuilder(builder: (context) {
|
playlist.owner!.id == me.id);
|
||||||
final playlistsCheck = useState(<String, bool>{});
|
}),
|
||||||
return PlatformAlertDialog(
|
builder: (context, snapshot) {
|
||||||
macosAppIcon: Sidebar.brandLogo(),
|
return HookBuilder(builder: (context) {
|
||||||
title: PlatformText(
|
final playlistsCheck = useState(<String, bool>{});
|
||||||
"Add `${track.value.name}` to following Playlists",
|
return AlertDialog(
|
||||||
style: const TextStyle(
|
title: Text(
|
||||||
fontSize: 18,
|
"Add `${track.value.name}` to following Playlists",
|
||||||
fontWeight: FontWeight.bold,
|
style: const TextStyle(
|
||||||
),
|
fontSize: 18,
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
secondaryActions: [
|
),
|
||||||
PlatformFilledButton(
|
),
|
||||||
isSecondary: true,
|
actions: [
|
||||||
child: const PlatformText("Cancel"),
|
OutlinedButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
child: const Text("Cancel"),
|
||||||
),
|
onPressed: () => Navigator.pop(context),
|
||||||
],
|
),
|
||||||
primaryActions: [
|
FilledButton(
|
||||||
PlatformFilledButton(
|
child: const Text("Add"),
|
||||||
child: const PlatformText("Add"),
|
onPressed: () async {
|
||||||
onPressed: () async {
|
final selectedPlaylists = playlistsCheck
|
||||||
final selectedPlaylists = playlistsCheck.value.entries
|
.value.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
.map((entry) => entry.key);
|
.map((entry) => entry.key);
|
||||||
|
|
||||||
await Future.wait(
|
await Future.wait(
|
||||||
selectedPlaylists.map(
|
selectedPlaylists.map(
|
||||||
(playlistId) => spotify.playlists
|
(playlistId) => spotify.playlists
|
||||||
.addTrack(track.value.uri!, playlistId),
|
.addTrack(track.value.uri!, playlistId),
|
||||||
),
|
),
|
||||||
).then((_) => Navigator.pop(context));
|
).then((_) => Navigator.pop(context));
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
height: 300,
|
height: 300,
|
||||||
width: 300,
|
width: 300,
|
||||||
child: !snapshot.hasData
|
child: !snapshot.hasData
|
||||||
? const Center(
|
? const Center(child: CircularProgressIndicator())
|
||||||
child: PlatformCircularProgressIndicator())
|
: ListView.builder(
|
||||||
: ListView.builder(
|
shrinkWrap: true,
|
||||||
shrinkWrap: true,
|
itemCount: snapshot.data!.length,
|
||||||
itemCount: snapshot.data!.length,
|
itemBuilder: (context, index) {
|
||||||
itemBuilder: (context, index) {
|
final playlist =
|
||||||
final playlist = snapshot.data!.elementAt(index);
|
snapshot.data!.elementAt(index);
|
||||||
return PlatformCheckbox(
|
return Checkbox(
|
||||||
label: PlatformText(playlist.name!),
|
value: playlistsCheck.value[playlist.id] ??
|
||||||
value:
|
false,
|
||||||
playlistsCheck.value[playlist.id] ?? false,
|
onChanged: (val) {
|
||||||
onChanged: (val) {
|
playlistsCheck.value = {
|
||||||
playlistsCheck.value = {
|
...playlistsCheck.value,
|
||||||
...playlistsCheck.value,
|
playlist.id!: val == true
|
||||||
playlist.id!: val == true
|
};
|
||||||
};
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
);
|
||||||
),
|
});
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String thumbnailUrl = TypeConversionUtils.image_X_UrlString(
|
final String thumbnailUrl = TypeConversionUtils.image_X_UrlString(
|
||||||
@ -189,11 +187,10 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (showCheck)
|
if (showCheck)
|
||||||
PlatformCheckbox(
|
Checkbox(
|
||||||
value: isChecked,
|
value: isChecked,
|
||||||
onChanged: (s) => onCheckChange?.call(s),
|
onChanged: (s) => onCheckChange?.call(s),
|
||||||
)
|
)
|
||||||
@ -229,16 +226,17 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: PlatformIconButton(
|
child: IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
playlist?.activeTrack.id == track.value.id
|
playlist?.activeTrack.id == track.value.id
|
||||||
? SpotubeIcons.pause
|
? SpotubeIcons.pause
|
||||||
: SpotubeIcons.play,
|
: SpotubeIcons.play,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
backgroundColor: PlatformTheme.of(context).primaryColor,
|
style: IconButton.styleFrom(
|
||||||
hoverColor:
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
PlatformTheme.of(context).primaryColor?.withOpacity(0.5),
|
hoverColor: Theme.of(context).primaryColor.withOpacity(0.5),
|
||||||
|
),
|
||||||
onPressed: !isBlackListed
|
onPressed: !isBlackListed
|
||||||
? () => onTrackPlayButtonPressed?.call(
|
? () => onTrackPlayButtonPressed?.call(
|
||||||
track.value,
|
track.value,
|
||||||
@ -255,7 +253,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
child: PlatformText(
|
child: Text(
|
||||||
track.value.name ?? "",
|
track.value.name ?? "",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -266,7 +264,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
if (isBlackListed) ...[
|
if (isBlackListed) ...[
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
PlatformText(
|
Text(
|
||||||
"Blacklisted",
|
"Blacklisted",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.red[400],
|
color: Colors.red[400],
|
||||||
@ -278,7 +276,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
isLocal
|
isLocal
|
||||||
? PlatformText(
|
? Text(
|
||||||
TypeConversionUtils.artists_X_String<Artist>(
|
TypeConversionUtils.artists_X_String<Artist>(
|
||||||
track.value.artists ?? []),
|
track.value.artists ?? []),
|
||||||
)
|
)
|
||||||
@ -294,7 +292,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
if (breakpoint.isMoreThan(Breakpoints.md) && showAlbum)
|
if (breakpoint.isMoreThan(Breakpoints.md) && showAlbum)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: isLocal
|
child: isLocal
|
||||||
? PlatformText(track.value.album?.name ?? "")
|
? Text(track.value.album?.name ?? "")
|
||||||
: LinkText(
|
: LinkText(
|
||||||
track.value.album!.name!,
|
track.value.album!.name!,
|
||||||
"/album/${track.value.album?.id}",
|
"/album/${track.value.album?.id}",
|
||||||
@ -304,7 +302,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
if (!breakpoint.isSm) ...[
|
if (!breakpoint.isSm) ...[
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
PlatformText(duration),
|
Text(duration),
|
||||||
],
|
],
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
if (!isLocal)
|
if (!isLocal)
|
||||||
@ -313,13 +311,12 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
if (!playlistQueueNotifier.isTrackOnQueue(track.value))
|
if (!playlistQueueNotifier.isTrackOnQueue(track.value))
|
||||||
Action(
|
Action(
|
||||||
icon: const Icon(SpotubeIcons.queueAdd),
|
icon: const Icon(SpotubeIcons.queueAdd),
|
||||||
text: const PlatformText("Add to queue"),
|
text: const Text("Add to queue"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
playlistQueueNotifier.add([track.value]);
|
playlistQueueNotifier.add([track.value]);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: PlatformText(
|
content: Text("Added ${track.value.name} to queue"),
|
||||||
"Added ${track.value.name} to queue"),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -327,13 +324,13 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
else
|
else
|
||||||
Action(
|
Action(
|
||||||
icon: const Icon(SpotubeIcons.queueRemove),
|
icon: const Icon(SpotubeIcons.queueRemove),
|
||||||
text: const PlatformText("Remove from queue"),
|
text: const Text("Remove from queue"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
playlistQueueNotifier.remove([track.value]);
|
playlistQueueNotifier.remove([track.value]);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: PlatformText(
|
content:
|
||||||
"Removed ${track.value.name} from queue"),
|
Text("Removed ${track.value.name} from queue"),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -346,7 +343,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
color: Colors.pink,
|
color: Colors.pink,
|
||||||
)
|
)
|
||||||
: const Icon(SpotubeIcons.heart),
|
: const Icon(SpotubeIcons.heart),
|
||||||
text: const PlatformText("Save as favorite"),
|
text: const Text("Save as favorite"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
toggler.item2.mutate(toggler.item1);
|
toggler.item2.mutate(toggler.item1);
|
||||||
},
|
},
|
||||||
@ -354,7 +351,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
if (auth != null)
|
if (auth != null)
|
||||||
Action(
|
Action(
|
||||||
icon: const Icon(SpotubeIcons.playlistAdd),
|
icon: const Icon(SpotubeIcons.playlistAdd),
|
||||||
text: const PlatformText("Add To playlist"),
|
text: const Text("Add To playlist"),
|
||||||
onPressed: actionAddToPlaylist,
|
onPressed: actionAddToPlaylist,
|
||||||
),
|
),
|
||||||
if (userPlaylist && auth != null)
|
if (userPlaylist && auth != null)
|
||||||
@ -362,10 +359,10 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
icon: (removeTrack.isMutating || !removeTrack.hasData) &&
|
icon: (removeTrack.isMutating || !removeTrack.hasData) &&
|
||||||
removingTrack.value == track.value.uri
|
removingTrack.value == track.value.uri
|
||||||
? const Center(
|
? const Center(
|
||||||
child: PlatformCircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
)
|
)
|
||||||
: const Icon(SpotubeIcons.removeFilled),
|
: const Icon(SpotubeIcons.removeFilled),
|
||||||
text: const PlatformText("Remove from playlist"),
|
text: const Text("Remove from playlist"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
removingTrack.value = track.value.uri;
|
removingTrack.value = track.value.uri;
|
||||||
removeTrack.mutate(track.value.uri!);
|
removeTrack.mutate(track.value.uri!);
|
||||||
@ -373,7 +370,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
Action(
|
Action(
|
||||||
icon: const Icon(SpotubeIcons.share),
|
icon: const Icon(SpotubeIcons.share),
|
||||||
text: const PlatformText("Share"),
|
text: const Text("Share"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
actionShare(track.value);
|
actionShare(track.value);
|
||||||
},
|
},
|
||||||
@ -384,7 +381,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
color: isBlackListed ? Colors.white : Colors.red[400],
|
color: isBlackListed ? Colors.white : Colors.red[400],
|
||||||
),
|
),
|
||||||
backgroundColor: isBlackListed ? Colors.red[400] : null,
|
backgroundColor: isBlackListed ? Colors.red[400] : null,
|
||||||
text: PlatformText(
|
text: Text(
|
||||||
"${isBlackListed ? "Remove from" : "Add to"} blacklist",
|
"${isBlackListed ? "Remove from" : "Add to"} blacklist",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isBlackListed ? Colors.white : Colors.red[400],
|
color: isBlackListed ? Colors.white : Colors.red[400],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/dialogs/confirm_download_dialog.dart';
|
import 'package:spotube/components/shared/dialogs/confirm_download_dialog.dart';
|
||||||
@ -71,7 +71,7 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
if (heading != null) heading!,
|
if (heading != null) heading!,
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
PlatformCheckbox(
|
Checkbox(
|
||||||
value: selected.value.length == sortedTracks.length,
|
value: selected.value.length == sortedTracks.length,
|
||||||
onChanged: (checked) {
|
onChanged: (checked) {
|
||||||
if (!showCheck.value) showCheck.value = true;
|
if (!showCheck.value) showCheck.value = true;
|
||||||
@ -85,7 +85,7 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: PlatformText(
|
child: Text(
|
||||||
"#",
|
"#",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: tableHeadStyle,
|
style: tableHeadStyle,
|
||||||
@ -94,7 +94,7 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
PlatformText(
|
Text(
|
||||||
"Title",
|
"Title",
|
||||||
style: tableHeadStyle,
|
style: tableHeadStyle,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
@ -108,7 +108,7 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
PlatformText(
|
Text(
|
||||||
"Album",
|
"Album",
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: tableHeadStyle,
|
style: tableHeadStyle,
|
||||||
@ -119,7 +119,7 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
if (!breakpoint.isSm) ...[
|
if (!breakpoint.isSm) ...[
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
PlatformText("Time", style: tableHeadStyle),
|
Text("Time", style: tableHeadStyle),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
SortTracksDropdown(
|
SortTracksDropdown(
|
||||||
@ -131,44 +131,45 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
.state = value;
|
.state = value;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PlatformPopupMenuButton(
|
PopupMenuButton(
|
||||||
closeAfterClick: false,
|
|
||||||
tooltip: "More Actions",
|
tooltip: "More Actions",
|
||||||
items: [
|
itemBuilder: (context) {
|
||||||
PlatformPopupMenuItem(
|
return [
|
||||||
enabled: selectedTracks.isNotEmpty,
|
PopupMenuItem(
|
||||||
value: "download",
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Icon(SpotubeIcons.download),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
PlatformText(
|
|
||||||
"Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!userPlaylist)
|
|
||||||
PlatformPopupMenuItem(
|
|
||||||
enabled: selectedTracks.isNotEmpty,
|
enabled: selectedTracks.isNotEmpty,
|
||||||
value: "add-to-playlist",
|
value: "download",
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(SpotubeIcons.playlistAdd),
|
const Icon(SpotubeIcons.download),
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
PlatformText(
|
Text(
|
||||||
"Add (${selectedTracks.length}) to Playlist",
|
"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 {
|
onSelected: (action) async {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "download":
|
case "download":
|
||||||
{
|
{
|
||||||
final confirmed = await showPlatformAlertDialog(
|
final confirmed = await showDialog(
|
||||||
context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return const ConfirmDownloadDialog();
|
return const ConfirmDownloadDialog();
|
||||||
},
|
},
|
||||||
@ -183,8 +184,8 @@ class TracksTableView extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
case "add-to-playlist":
|
case "add-to-playlist":
|
||||||
{
|
{
|
||||||
await showPlatformAlertDialog(
|
await showDialog(
|
||||||
context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return PlaylistAddTrackDialog(
|
return PlaylistAddTrackDialog(
|
||||||
tracks: selectedTracks.toList(),
|
tracks: selectedTracks.toList(),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
|
|
||||||
void useCustomStatusBarColor(
|
void useCustomStatusBarColor(
|
||||||
Color color,
|
Color color,
|
||||||
@ -9,7 +8,7 @@ void useCustomStatusBarColor(
|
|||||||
bool noSetBGColor = false,
|
bool noSetBGColor = false,
|
||||||
}) {
|
}) {
|
||||||
final context = useContext();
|
final context = useContext();
|
||||||
final backgroundColor = PlatformTheme.of(context).scaffoldBackgroundColor!;
|
final backgroundColor = Theme.of(context).scaffoldBackgroundColor!;
|
||||||
resetStatusbar() => SystemChrome.setSystemUIOverlayStyle(
|
resetStatusbar() => SystemChrome.setSystemUIOverlayStyle(
|
||||||
SystemUiOverlayStyle(
|
SystemUiOverlayStyle(
|
||||||
statusBarColor: backgroundColor, // status bar color
|
statusBarColor: backgroundColor, // status bar color
|
||||||
|
@ -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_hooks/flutter_hooks.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
|
||||||
import 'package:spotube/components/shared/links/anchor_button.dart';
|
import 'package:spotube/components/shared/links/anchor_button.dart';
|
||||||
import 'package:spotube/hooks/use_package_info.dart';
|
import 'package:spotube/hooks/use_package_info.dart';
|
||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
@ -56,18 +55,17 @@ void useUpdateChecker(WidgetRef ref) {
|
|||||||
(latestVersion.isPreRelease && !currentVersion.isPreRelease) ||
|
(latestVersion.isPreRelease && !currentVersion.isPreRelease) ||
|
||||||
(!latestVersion.isPreRelease && currentVersion.isPreRelease)) return;
|
(!latestVersion.isPreRelease && currentVersion.isPreRelease)) return;
|
||||||
if (latestVersion <= currentVersion) return;
|
if (latestVersion <= currentVersion) return;
|
||||||
showPlatformAlertDialog(
|
showDialog(
|
||||||
context,
|
context: context,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
barrierColor: Colors.black26,
|
barrierColor: Colors.black26,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
const url =
|
const url =
|
||||||
"https://spotube.netlify.app/other-downloads/stable-downloads";
|
"https://spotube.netlify.app/other-downloads/stable-downloads";
|
||||||
return PlatformAlertDialog(
|
return AlertDialog(
|
||||||
macosAppIcon: Sidebar.brandLogo(),
|
title: const Text("Spotube has an update"),
|
||||||
title: const PlatformText("Spotube has an update"),
|
actions: [
|
||||||
primaryActions: [
|
FilledButton(
|
||||||
PlatformFilledButton(
|
|
||||||
child: const Text("Download Now"),
|
child: const Text("Download Now"),
|
||||||
onPressed: () => download(url),
|
onPressed: () => download(url),
|
||||||
),
|
),
|
||||||
@ -79,7 +77,7 @@ void useUpdateChecker(WidgetRef ref) {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const PlatformText("Read the latest "),
|
const Text("Read the latest "),
|
||||||
AnchorButton(
|
AnchorButton(
|
||||||
"release notes",
|
"release notes",
|
||||||
style: const TextStyle(color: Colors.blue),
|
style: const TextStyle(color: Colors.blue),
|
||||||
|
@ -11,7 +11,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:spotube/collections/cache_keys.dart';
|
import 'package:spotube/collections/cache_keys.dart';
|
||||||
import 'package:spotube/collections/env.dart';
|
import 'package:spotube/collections/env.dart';
|
||||||
@ -25,7 +24,7 @@ import 'package:spotube/provider/user_preferences_provider.dart';
|
|||||||
import 'package:spotube/services/audio_player.dart';
|
import 'package:spotube/services/audio_player.dart';
|
||||||
import 'package:spotube/services/pocketbase.dart';
|
import 'package:spotube/services/pocketbase.dart';
|
||||||
import 'package:spotube/services/youtube.dart';
|
import 'package:spotube/services/youtube.dart';
|
||||||
import 'package:spotube/themes/light_theme.dart';
|
import 'package:spotube/themes/theme.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:window_size/window_size.dart';
|
import 'package:window_size/window_size.dart';
|
||||||
@ -157,8 +156,8 @@ void main(List<String> rawArgs) async {
|
|||||||
logger.v(
|
logger.v(
|
||||||
"[onFileExists] download confirmation for ${track.name}",
|
"[onFileExists] download confirmation for ${track.name}",
|
||||||
);
|
);
|
||||||
return showPlatformAlertDialog<bool>(
|
return showDialog<bool>(
|
||||||
context,
|
context: context,
|
||||||
builder: (_) =>
|
builder: (_) =>
|
||||||
ReplaceDownloadedDialog(track: track),
|
ReplaceDownloadedDialog(track: track),
|
||||||
).then((s) => s ?? false);
|
).then((s) => s ?? false);
|
||||||
@ -206,14 +205,6 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
|
|||||||
super.initState();
|
super.initState();
|
||||||
SharedPreferences.getInstance().then(((value) => localStorage = value));
|
SharedPreferences.getInstance().then(((value) => localStorage = value));
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
|
||||||
setState(() {
|
|
||||||
appPlatform = Theme.of(context).platform;
|
|
||||||
if (appPlatform == TargetPlatform.macOS) {
|
|
||||||
appPlatform = TargetPlatform.android;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -243,16 +234,6 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
|
|||||||
prevSize = size;
|
prevSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetPlatform appPlatform = TargetPlatform.android;
|
|
||||||
|
|
||||||
void changePlatform(TargetPlatform targetPlatform) {
|
|
||||||
appPlatform = targetPlatform;
|
|
||||||
if (appPlatform == TargetPlatform.macOS) {
|
|
||||||
appPlatform = TargetPlatform.android;
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final themeMode =
|
final themeMode =
|
||||||
@ -268,9 +249,7 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
platform = appPlatform;
|
return MaterialApp.router(
|
||||||
|
|
||||||
return PlatformApp.router(
|
|
||||||
routeInformationParser: router.routeInformationParser,
|
routeInformationParser: router.routeInformationParser,
|
||||||
routerDelegate: router.routerDelegate,
|
routerDelegate: router.routerDelegate,
|
||||||
routeInformationProvider: router.routeInformationProvider,
|
routeInformationProvider: router.routeInformationProvider,
|
||||||
@ -279,17 +258,10 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
|
|||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return DragToResizeArea(child: child!);
|
return DragToResizeArea(child: child!);
|
||||||
},
|
},
|
||||||
androidTheme: theme(accentMaterialColor, Brightness.light),
|
|
||||||
androidDarkTheme: theme(accentMaterialColor, Brightness.dark),
|
|
||||||
linuxTheme: linuxTheme,
|
|
||||||
linuxDarkTheme: linuxDarkTheme,
|
|
||||||
iosTheme: themeMode == ThemeMode.dark ? iosDarkTheme : iosTheme,
|
|
||||||
windowsTheme: windowsTheme,
|
|
||||||
windowsDarkTheme: windowsDarkTheme,
|
|
||||||
macosTheme: macosTheme,
|
|
||||||
macosDarkTheme: macosDarkTheme,
|
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
shortcuts: PlatformProperty.all({
|
theme: theme(accentMaterialColor, Brightness.light),
|
||||||
|
darkTheme: theme(accentMaterialColor, Brightness.dark),
|
||||||
|
shortcuts: {
|
||||||
...WidgetsApp.defaultShortcuts.map((key, value) {
|
...WidgetsApp.defaultShortcuts.map((key, value) {
|
||||||
return MapEntry(
|
return MapEntry(
|
||||||
LogicalKeySet.fromSet(key.triggers?.toSet() ?? {}),
|
LogicalKeySet.fromSet(key.triggers?.toSet() ?? {}),
|
||||||
@ -324,14 +296,14 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
|
|||||||
LogicalKeyboardKey.control,
|
LogicalKeyboardKey.control,
|
||||||
LogicalKeyboardKey.shift,
|
LogicalKeyboardKey.shift,
|
||||||
): CloseAppIntent(),
|
): CloseAppIntent(),
|
||||||
}),
|
},
|
||||||
actions: PlatformProperty.all({
|
actions: {
|
||||||
...WidgetsApp.defaultActions,
|
...WidgetsApp.defaultActions,
|
||||||
PlayPauseIntent: PlayPauseAction(),
|
PlayPauseIntent: PlayPauseAction(),
|
||||||
NavigationIntent: NavigationAction(),
|
NavigationIntent: NavigationAction(),
|
||||||
HomeTabIntent: HomeTabAction(),
|
HomeTabIntent: HomeTabAction(),
|
||||||
CloseAppIntent: CloseAppAction(),
|
CloseAppIntent: CloseAppAction(),
|
||||||
}),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/shimmers/shimmer_artist_profile.dart';
|
import 'package:spotube/components/shared/shimmers/shimmer_artist_profile.dart';
|
||||||
@ -33,13 +33,13 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
SpotifyApi spotify = ref.watch(spotifyProvider);
|
SpotifyApi spotify = ref.watch(spotifyProvider);
|
||||||
final parentScrollController = useScrollController();
|
final parentScrollController = useScrollController();
|
||||||
final textTheme = PlatformTheme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
final chipTextVariant = useBreakpointValue(
|
final chipTextVariant = useBreakpointValue(
|
||||||
sm: textTheme!.caption,
|
sm: textTheme.bodySmall,
|
||||||
md: textTheme.body,
|
md: textTheme.bodyMedium,
|
||||||
lg: textTheme.subheading,
|
lg: textTheme.bodyLarge,
|
||||||
xl: textTheme.headline,
|
xl: textTheme.titleSmall,
|
||||||
xxl: textTheme.headline,
|
xxl: textTheme.titleMedium,
|
||||||
);
|
);
|
||||||
|
|
||||||
final avatarWidth = useBreakpointValue(
|
final avatarWidth = useBreakpointValue(
|
||||||
@ -58,9 +58,9 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: PlatformScaffold(
|
child: Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: const PageWindowTitleBar(
|
||||||
leading: const PlatformBackButton(),
|
leading: BackButton(),
|
||||||
),
|
),
|
||||||
body: HookBuilder(
|
body: HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
@ -70,7 +70,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
return const ShimmerArtistProfile();
|
return const ShimmerArtistProfile();
|
||||||
} else if (artistsQuery.hasError) {
|
} else if (artistsQuery.hasError) {
|
||||||
return Center(
|
return Center(
|
||||||
child: PlatformText(artistsQuery.error.toString()),
|
child: Text(artistsQuery.error.toString()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
borderRadius: BorderRadius.circular(50)),
|
borderRadius: BorderRadius.circular(50)),
|
||||||
child: PlatformText(
|
child: Text(
|
||||||
data.type!.toUpperCase(),
|
data.type!.toUpperCase(),
|
||||||
style: chipTextVariant?.copyWith(
|
style: chipTextVariant?.copyWith(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@ -132,7 +132,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
color: Colors.red[400],
|
color: Colors.red[400],
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.circular(50)),
|
BorderRadius.circular(50)),
|
||||||
child: PlatformText(
|
child: Text(
|
||||||
"Blacklisted",
|
"Blacklisted",
|
||||||
style: chipTextVariant?.copyWith(
|
style: chipTextVariant?.copyWith(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@ -142,18 +142,18 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
PlatformText(
|
Text(
|
||||||
data.name!,
|
data.name!,
|
||||||
style: breakpoint.isSm
|
style: breakpoint.isSm
|
||||||
? textTheme.subheading
|
? textTheme.headlineSmall
|
||||||
: textTheme.headline,
|
: textTheme.headlineMedium,
|
||||||
),
|
),
|
||||||
PlatformText(
|
Text(
|
||||||
"${PrimitiveUtils.toReadableNumber(data.followers!.total!.toDouble())} followers",
|
"${PrimitiveUtils.toReadableNumber(data.followers!.total!.toDouble())} followers",
|
||||||
style: breakpoint.isSm
|
style: textTheme.bodyMedium?.copyWith(
|
||||||
? textTheme.body
|
fontWeight:
|
||||||
: textTheme.body
|
breakpoint.isSm ? null : FontWeight.bold,
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Row(
|
Row(
|
||||||
@ -170,14 +170,13 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
return const SizedBox(
|
return const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 20,
|
width: 20,
|
||||||
child:
|
child: CircularProgressIndicator(),
|
||||||
PlatformCircularProgressIndicator(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final queryBowl = QueryClient.of(context);
|
final queryBowl = QueryClient.of(context);
|
||||||
|
|
||||||
return PlatformFilledButton(
|
return FilledButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
try {
|
try {
|
||||||
isFollowingQuery.data!
|
isFollowingQuery.data!
|
||||||
@ -199,7 +198,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
"user-follows-artists-query/$artistId");
|
"user-follows-artists-query/$artistId");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: PlatformText(
|
child: Text(
|
||||||
isFollowingQuery.data!
|
isFollowingQuery.data!
|
||||||
? "Following"
|
? "Following"
|
||||||
: "Follow",
|
: "Follow",
|
||||||
@ -208,7 +207,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: "Add to blacklisted artists",
|
tooltip: "Add to blacklisted artists",
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
SpotubeIcons.userRemove,
|
SpotubeIcons.userRemove,
|
||||||
@ -216,8 +215,10 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
? Colors.red[400]
|
? Colors.red[400]
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
),
|
),
|
||||||
backgroundColor:
|
style: IconButton.styleFrom(
|
||||||
isBlackListed ? Colors.red[400] : null,
|
backgroundColor:
|
||||||
|
isBlackListed ? Colors.red[400] : null,
|
||||||
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (isBlackListed) {
|
if (isBlackListed) {
|
||||||
ref
|
ref
|
||||||
@ -238,7 +239,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
icon: const Icon(SpotubeIcons.share),
|
icon: const Icon(SpotubeIcons.share),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await Clipboard.setData(
|
await Clipboard.setData(
|
||||||
@ -250,7 +251,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
const SnackBar(
|
const SnackBar(
|
||||||
width: 300,
|
width: 300,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
content: PlatformText(
|
content: Text(
|
||||||
"Artist URL copied to clipboard",
|
"Artist URL copied to clipboard",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
@ -279,10 +280,10 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (topTracksQuery.isLoading || !topTracksQuery.hasData) {
|
if (topTracksQuery.isLoading || !topTracksQuery.hasData) {
|
||||||
return const PlatformCircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
} else if (topTracksQuery.hasError) {
|
} else if (topTracksQuery.hasError) {
|
||||||
return Center(
|
return Center(
|
||||||
child: PlatformText(topTracksQuery.error.toString()),
|
child: Text(topTracksQuery.error.toString()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,13 +306,12 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
return Column(children: [
|
return Column(children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
PlatformText(
|
Text(
|
||||||
"Top Tracks",
|
"Top Tracks",
|
||||||
style:
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
PlatformTheme.of(context).textTheme?.headline,
|
|
||||||
),
|
),
|
||||||
if (!isPlaylistPlaying)
|
if (!isPlaylistPlaying)
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
SpotubeIcons.queueAdd,
|
SpotubeIcons.queueAdd,
|
||||||
),
|
),
|
||||||
@ -321,7 +321,7 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
SnackBar(
|
SnackBar(
|
||||||
width: 300,
|
width: 300,
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
content: PlatformText(
|
content: Text(
|
||||||
"Added ${topTracks.length} tracks to queue",
|
"Added ${topTracks.length} tracks to queue",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
@ -329,17 +329,17 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (platform != TargetPlatform.linux)
|
const SizedBox(width: 5),
|
||||||
const SizedBox(width: 5),
|
IconButton(
|
||||||
PlatformIconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isPlaylistPlaying
|
isPlaylistPlaying
|
||||||
? SpotubeIcons.stop
|
? SpotubeIcons.stop
|
||||||
: SpotubeIcons.play,
|
: SpotubeIcons.play,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
backgroundColor:
|
style: IconButton.styleFrom(
|
||||||
PlatformTheme.of(context).primaryColor,
|
backgroundColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
onPressed: () => playPlaylist(topTracks.toList()),
|
onPressed: () => playPlaylist(topTracks.toList()),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -364,16 +364,16 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 50),
|
const SizedBox(height: 50),
|
||||||
PlatformText(
|
Text(
|
||||||
"Albums",
|
"Albums",
|
||||||
style: PlatformTheme.of(context).textTheme?.headline,
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
ArtistAlbumList(artistId),
|
ArtistAlbumList(artistId),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
PlatformText(
|
Text(
|
||||||
"Fans also likes",
|
"Fans also likes",
|
||||||
style: PlatformTheme.of(context).textTheme?.headline,
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
HookBuilder(
|
HookBuilder(
|
||||||
@ -384,10 +384,10 @@ class ArtistPage extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (relatedArtists.isLoading || !relatedArtists.hasData) {
|
if (relatedArtists.isLoading || !relatedArtists.hasData) {
|
||||||
return const PlatformCircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
} else if (relatedArtists.hasError) {
|
} else if (relatedArtists.hasError) {
|
||||||
return Center(
|
return Center(
|
||||||
child: PlatformText(relatedArtists.error.toString()),
|
child: Text(relatedArtists.error.toString()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/components/desktop_login/login_form.dart';
|
import 'package:spotube/components/desktop_login/login_form.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
@ -15,10 +15,9 @@ class DesktopLoginPage extends HookConsumerWidget {
|
|||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: PlatformScaffold(
|
child: Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: const PageWindowTitleBar(
|
||||||
leading: const PlatformBackButton(),
|
leading: BackButton(),
|
||||||
hideWhenWindows: false,
|
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -26,7 +25,7 @@ class DesktopLoginPage extends HookConsumerWidget {
|
|||||||
margin: const EdgeInsets.all(10),
|
margin: const EdgeInsets.all(10),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: PlatformTheme.of(context).secondaryBackgroundColor,
|
color: Theme.of(context).cardColor,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -35,11 +34,13 @@ class DesktopLoginPage extends HookConsumerWidget {
|
|||||||
width: MediaQuery.of(context).size.width *
|
width: MediaQuery.of(context).size.width *
|
||||||
(breakpoint <= Breakpoints.md ? .5 : .3),
|
(breakpoint <= Breakpoints.md ? .5 : .3),
|
||||||
),
|
),
|
||||||
PlatformText.subheading(
|
Text(
|
||||||
"Add your spotify credentials to get started",
|
"Add your spotify credentials to get started",
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
PlatformText.label(
|
Text(
|
||||||
"Don't worry, any of your credentials won't be collected or shared with anyone",
|
"Don't worry, any of your credentials won't be collected or shared with anyone",
|
||||||
|
style: Theme.of(context).textTheme.labelMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TokenLoginForm(
|
TokenLoginForm(
|
||||||
@ -50,9 +51,9 @@ class DesktopLoginPage extends HookConsumerWidget {
|
|||||||
alignment: WrapAlignment.center,
|
alignment: WrapAlignment.center,
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const PlatformText("Don't know how to do this?"),
|
const Text("Don't know how to do this?"),
|
||||||
PlatformTextButton(
|
TextButton(
|
||||||
child: const PlatformText(
|
child: const Text(
|
||||||
"Follow along the Step by Step guide",
|
"Follow along the Step by Step guide",
|
||||||
),
|
),
|
||||||
onPressed: () => GoRouter.of(context).push(
|
onPressed: () => GoRouter.of(context).push(
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:introduction_screen/introduction_screen.dart';
|
import 'package:introduction_screen/introduction_screen.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/components/desktop_login/login_form.dart';
|
import 'package:spotube/components/desktop_login/login_form.dart';
|
||||||
import 'package:spotube/components/shared/links/hyper_link.dart';
|
import 'package:spotube/components/shared/links/hyper_link.dart';
|
||||||
@ -21,14 +21,13 @@ class LoginTutorial extends ConsumerWidget {
|
|||||||
final key = GlobalKey<State<IntroductionScreen>>();
|
final key = GlobalKey<State<IntroductionScreen>>();
|
||||||
|
|
||||||
final pageDecoration = PageDecoration(
|
final pageDecoration = PageDecoration(
|
||||||
bodyTextStyle: PlatformTheme.of(context).textTheme!.body!,
|
bodyTextStyle: Theme.of(context).textTheme.bodyMedium!,
|
||||||
titleTextStyle: PlatformTheme.of(context).textTheme!.subheading!,
|
titleTextStyle: Theme.of(context).textTheme.headlineMedium!,
|
||||||
);
|
);
|
||||||
return PlatformScaffold(
|
return Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: PageWindowTitleBar(
|
||||||
hideWhenWindows: false,
|
leading: TextButton(
|
||||||
leading: PlatformTextButton(
|
child: const Text("Exit"),
|
||||||
child: const PlatformText("Exit"),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
@ -36,29 +35,27 @@ class LoginTutorial extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
body: IntroductionScreen(
|
body: IntroductionScreen(
|
||||||
key: key,
|
key: key,
|
||||||
globalBackgroundColor:
|
globalBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
PlatformTheme.of(context).scaffoldBackgroundColor,
|
overrideBack: OutlinedButton(
|
||||||
overrideBack: PlatformFilledButton(
|
child: const Center(child: Text("Previous")),
|
||||||
isSecondary: true,
|
|
||||||
child: const Center(child: PlatformText("Previous")),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
(key.currentState as IntroductionScreenState).previous();
|
(key.currentState as IntroductionScreenState).previous();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
overrideNext: PlatformFilledButton(
|
overrideNext: FilledButton(
|
||||||
child: const Center(child: PlatformText("Next")),
|
child: const Center(child: Text("Next")),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
(key.currentState as IntroductionScreenState).next();
|
(key.currentState as IntroductionScreenState).next();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
overrideDone: PlatformFilledButton(
|
overrideDone: FilledButton(
|
||||||
onPressed: authenticationNotifier.isLoggedIn
|
onPressed: authenticationNotifier.isLoggedIn
|
||||||
? () {
|
? () {
|
||||||
ServiceUtils.navigate(context, "/");
|
ServiceUtils.navigate(context, "/");
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
child: const Center(child: PlatformText("Done")),
|
child: const Center(child: Text("Done")),
|
||||||
),
|
),
|
||||||
pages: [
|
pages: [
|
||||||
PageViewModel(
|
PageViewModel(
|
||||||
@ -67,14 +64,14 @@ class LoginTutorial extends ConsumerWidget {
|
|||||||
image: Assets.tutorial.step1.image(),
|
image: Assets.tutorial.step1.image(),
|
||||||
bodyWidget: Wrap(
|
bodyWidget: Wrap(
|
||||||
children: const [
|
children: const [
|
||||||
PlatformText(
|
Text(
|
||||||
"First, Go to ",
|
"First, Go to ",
|
||||||
),
|
),
|
||||||
Hyperlink(
|
Hyperlink(
|
||||||
"accounts.spotify.com ",
|
"accounts.spotify.com ",
|
||||||
"https://accounts.spotify.com",
|
"https://accounts.spotify.com",
|
||||||
),
|
),
|
||||||
PlatformText(
|
Text(
|
||||||
"and Login/Sign up if you're not logged in",
|
"and Login/Sign up if you're not logged in",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -84,7 +81,7 @@ class LoginTutorial extends ConsumerWidget {
|
|||||||
decoration: pageDecoration,
|
decoration: pageDecoration,
|
||||||
title: "Step 2",
|
title: "Step 2",
|
||||||
image: Assets.tutorial.step2.image(),
|
image: Assets.tutorial.step2.image(),
|
||||||
bodyWidget: const PlatformText(
|
bodyWidget: const Text(
|
||||||
"1. Once you're logged in, press F12 or Mouse Right Click > Inspect to Open the Browser devtools.\n2. Then go the \"Application\" Tab (Chrome, Edge, Brave etc..) or \"Storage\" Tab (Firefox, Palemoon etc..)\n3. Go to the \"Cookies\" section then the \"https://accounts.spotify.com\" subsection",
|
"1. Once you're logged in, press F12 or Mouse Right Click > Inspect to Open the Browser devtools.\n2. Then go the \"Application\" Tab (Chrome, Edge, Brave etc..) or \"Storage\" Tab (Firefox, Palemoon etc..)\n3. Go to the \"Cookies\" section then the \"https://accounts.spotify.com\" subsection",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
@ -93,7 +90,7 @@ class LoginTutorial extends ConsumerWidget {
|
|||||||
decoration: pageDecoration,
|
decoration: pageDecoration,
|
||||||
title: "Step 3",
|
title: "Step 3",
|
||||||
image: Assets.tutorial.step3.image(),
|
image: Assets.tutorial.step3.image(),
|
||||||
bodyWidget: const PlatformText(
|
bodyWidget: const Text(
|
||||||
"Copy the values of \"sp_dc\" and \"sp_key\" Cookies",
|
"Copy the values of \"sp_dc\" and \"sp_key\" Cookies",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
@ -114,8 +111,9 @@ class LoginTutorial extends ConsumerWidget {
|
|||||||
title: "Step 5",
|
title: "Step 5",
|
||||||
bodyWidget: Column(
|
bodyWidget: Column(
|
||||||
children: [
|
children: [
|
||||||
PlatformText.label(
|
Text(
|
||||||
"Paste the copied \"sp_dc\" and \"sp_key\" values in the respective fields",
|
"Paste the copied \"sp_dc\" and \"sp_key\" values in the respective fields",
|
||||||
|
style: Theme.of(context).textTheme.labelMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TokenLoginForm(
|
TokenLoginForm(
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/pages/home/genres.dart';
|
import 'package:spotube/pages/home/genres.dart';
|
||||||
import 'package:spotube/pages/home/personalized.dart';
|
import 'package:spotube/pages/home/personalized.dart';
|
||||||
@ -12,57 +10,38 @@ class HomePage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final index = useState(0);
|
return DefaultTabController(
|
||||||
final tabbar = PlatformTabBar(
|
length: 2,
|
||||||
androidIsScrollable: true,
|
child: Scaffold(
|
||||||
selectedIndex: index.value,
|
appBar: const PageWindowTitleBar(
|
||||||
onSelectedIndexChanged: (value) => index.value = value,
|
titleWidth: 347,
|
||||||
isNavigational:
|
centerTitle: true,
|
||||||
PlatformProperty.byPlatformGroup(mobile: false, desktop: true),
|
title: TabBar(
|
||||||
tabs: [
|
isScrollable: true,
|
||||||
PlatformTab(
|
tabs: [
|
||||||
label: 'Genres',
|
Tab(text: 'Genres'),
|
||||||
icon: PlatformProperty.only(
|
Tab(text: 'Personalized'),
|
||||||
android: const SizedBox.shrink(),
|
],
|
||||||
other: const Icon(SpotubeIcons.genres),
|
),
|
||||||
).resolve(platform!),
|
),
|
||||||
),
|
body: AnimatedSwitcher(
|
||||||
PlatformTab(
|
duration: const Duration(milliseconds: 300),
|
||||||
label: 'Personalized',
|
transitionBuilder: (child, animation) => SlideTransition(
|
||||||
icon: PlatformProperty.only(
|
position: animation.drive(
|
||||||
android: const SizedBox.shrink(),
|
Tween<Offset>(
|
||||||
other: const Icon(SpotubeIcons.personalized),
|
begin: const Offset(1, 0),
|
||||||
).resolve(platform!),
|
end: const Offset(0, 0),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
child: child,
|
||||||
|
),
|
||||||
return PlatformScaffold(
|
child: const TabBarView(
|
||||||
appBar: platform == TargetPlatform.windows
|
children: [
|
||||||
? PreferredSize(
|
GenrePage(),
|
||||||
preferredSize: const Size.fromHeight(40),
|
PersonalizedPage(),
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
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/material.dart' hide Page;
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/album/album_card.dart';
|
import 'package:spotube/components/album/album_card.dart';
|
||||||
import 'package:spotube/components/playlist/playlist_card.dart';
|
import 'package:spotube/components/playlist/playlist_card.dart';
|
||||||
@ -53,7 +53,10 @@ class PersonalizedItemCard extends HookWidget {
|
|||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
PlatformText.headline(title),
|
Text(
|
||||||
|
title,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart' hide Image;
|
import 'package:flutter/material.dart' hide Image;
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/library/user_local_tracks.dart';
|
import 'package:spotube/components/library/user_local_tracks.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/components/library/user_albums.dart';
|
import 'package:spotube/components/library/user_albums.dart';
|
||||||
@ -13,43 +12,34 @@ class LibraryPage extends HookConsumerWidget {
|
|||||||
const LibraryPage({Key? key}) : super(key: key);
|
const LibraryPage({Key? key}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final index = useState(0);
|
return const SafeArea(
|
||||||
|
child: DefaultTabController(
|
||||||
final body = [
|
length: 5,
|
||||||
const UserPlaylists(),
|
child: Scaffold(
|
||||||
const UserLocalTracks(),
|
appBar: PageWindowTitleBar(
|
||||||
const UserDownloads(),
|
titleWidth: 347,
|
||||||
const UserArtists(),
|
centerTitle: true,
|
||||||
const UserAlbums(),
|
title: TabBar(
|
||||||
][index.value];
|
isScrollable: true,
|
||||||
|
tabs: [
|
||||||
final tabbar = PlatformTabBar(
|
Tab(text: 'Playlists'),
|
||||||
androidIsScrollable: true,
|
Tab(text: 'Tracks'),
|
||||||
selectedIndex: index.value,
|
Tab(text: 'Downloads'),
|
||||||
onSelectedIndexChanged: (value) => index.value = value,
|
Tab(text: 'Artists'),
|
||||||
isNavigational:
|
Tab(text: 'Albums'),
|
||||||
PlatformProperty.byPlatformGroup(mobile: false, desktop: true),
|
],
|
||||||
tabs: [
|
),
|
||||||
PlatformTab(label: 'Playlists', icon: const SizedBox.shrink()),
|
),
|
||||||
PlatformTab(label: 'Tracks', icon: const SizedBox.shrink()),
|
body: TabBarView(
|
||||||
PlatformTab(label: 'Downloads', icon: const SizedBox.shrink()),
|
children: [
|
||||||
PlatformTab(label: 'Artists', icon: const SizedBox.shrink()),
|
UserPlaylists(),
|
||||||
PlatformTab(label: 'Albums', icon: const SizedBox.shrink()),
|
UserLocalTracks(),
|
||||||
],
|
UserDownloads(),
|
||||||
);
|
UserArtists(),
|
||||||
return SafeArea(
|
UserAlbums(),
|
||||||
child: PlatformScaffold(
|
],
|
||||||
appBar: platform == TargetPlatform.windows
|
),
|
||||||
? PreferredSize(
|
),
|
||||||
preferredSize: const Size.fromHeight(40),
|
|
||||||
child: tabbar,
|
|
||||||
)
|
|
||||||
: PageWindowTitleBar(
|
|
||||||
titleWidth: 347,
|
|
||||||
centerTitle: true,
|
|
||||||
center: tabbar,
|
|
||||||
),
|
|
||||||
body: body,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import 'dart:ui';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
@ -31,7 +31,6 @@ class LyricsPage extends HookConsumerWidget {
|
|||||||
[playlist?.activeTrack.album?.images],
|
[playlist?.activeTrack.album?.images],
|
||||||
);
|
);
|
||||||
final palette = usePaletteColor(albumArt, ref);
|
final palette = usePaletteColor(albumArt, ref);
|
||||||
final index = useState(0);
|
|
||||||
|
|
||||||
useCustomStatusBarColor(
|
useCustomStatusBarColor(
|
||||||
palette.color,
|
palette.color,
|
||||||
@ -39,102 +38,101 @@ class LyricsPage extends HookConsumerWidget {
|
|||||||
noSetBGColor: true,
|
noSetBGColor: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget body = [
|
const tabbar = TabBar(
|
||||||
SyncedLyrics(palette: palette, isModal: isModal),
|
isScrollable: true,
|
||||||
PlainLyrics(palette: palette, isModal: isModal),
|
tabs: [
|
||||||
][index.value];
|
Tab(text: "Synced"),
|
||||||
|
Tab(text: "Plain"),
|
||||||
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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isModal) {
|
if (isModal) {
|
||||||
return SafeArea(
|
return DefaultTabController(
|
||||||
child: Container(
|
length: 2,
|
||||||
clipBehavior: Clip.hardEdge,
|
child: SafeArea(
|
||||||
decoration: BoxDecoration(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.background.withOpacity(.4),
|
clipBehavior: Clip.hardEdge,
|
||||||
borderRadius: const BorderRadius.only(
|
decoration: BoxDecoration(
|
||||||
topLeft: Radius.circular(10),
|
color: Theme.of(context).colorScheme.background.withOpacity(.4),
|
||||||
topRight: Radius.circular(10),
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
topRight: Radius.circular(10),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
child: BackdropFilter(
|
||||||
child: BackdropFilter(
|
filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
|
||||||
filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
|
child: Column(
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
const SizedBox(height: 5),
|
||||||
const SizedBox(height: 5),
|
Container(
|
||||||
Container(
|
height: 7,
|
||||||
height: 7,
|
width: 150,
|
||||||
width: 150,
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: palette.titleTextColor,
|
||||||
color: palette.titleTextColor,
|
borderRadius: BorderRadius.circular(10),
|
||||||
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(),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 5),
|
),
|
||||||
],
|
AppBar(
|
||||||
),
|
title: tabbar,
|
||||||
Expanded(child: body),
|
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(
|
return DefaultTabController(
|
||||||
extendBodyBehindAppBar: true,
|
length: 2,
|
||||||
appBar: !kIsMacOS
|
child: Scaffold(
|
||||||
? (platform != TargetPlatform.windows && !isModal
|
extendBodyBehindAppBar: true,
|
||||||
? PageWindowTitleBar(
|
appBar: !kIsMacOS
|
||||||
toolbarOpacity: 0,
|
? const PageWindowTitleBar(
|
||||||
backgroundColor: Colors.transparent,
|
toolbarOpacity: 0,
|
||||||
center: tabbar,
|
backgroundColor: Colors.transparent,
|
||||||
)
|
title: tabbar,
|
||||||
: tabbar)
|
)
|
||||||
: null,
|
: tabbar as PreferredSizeWidget?,
|
||||||
body: Container(
|
body: Container(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: UniversalImage.imageProvider(albumArt),
|
image: UniversalImage.imageProvider(albumArt),
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(8),
|
child: BackdropFilter(
|
||||||
),
|
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||||
child: BackdropFilter(
|
child: ColoredBox(
|
||||||
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
color: palette.color.withOpacity(.7),
|
||||||
child: ColoredBox(
|
child: SafeArea(
|
||||||
color: palette.color.withOpacity(.7),
|
child: TabBarView(
|
||||||
child: SafeArea(child: body),
|
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:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ class WebViewLogin extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlatformScaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
|
@ -5,7 +5,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:palette_generator/palette_generator.dart';
|
import 'package:palette_generator/palette_generator.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/player/player_actions.dart';
|
import 'package:spotube/components/player/player_actions.dart';
|
||||||
@ -65,20 +65,12 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
noSetBGColor: true,
|
noSetBGColor: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
return PlatformScaffold(
|
return Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: PageWindowTitleBar(
|
||||||
hideWhenWindows: false,
|
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: paletteColor.titleTextColor,
|
foregroundColor: paletteColor.titleTextColor,
|
||||||
toolbarOpacity:
|
toolbarOpacity: 1,
|
||||||
PlatformProperty.only(android: 1.0, windows: 1.0, other: 0.0)
|
leading: BackButton(color: paletteColor.titleTextColor),
|
||||||
.resolve(platform ?? Theme.of(context).platform),
|
|
||||||
leading: PlatformBackButton(
|
|
||||||
color: PlatformProperty.only(
|
|
||||||
macos: Colors.black,
|
|
||||||
other: paletteColor.titleTextColor,
|
|
||||||
).resolve(platform!),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
body: Container(
|
body: Container(
|
||||||
@ -91,7 +83,6 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
child: BackdropFilter(
|
child: BackdropFilter(
|
||||||
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
|
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
|
||||||
child: Material(
|
child: Material(
|
||||||
textStyle: PlatformTheme.of(context).textTheme!.body!,
|
|
||||||
color: paletteColor.color.withOpacity(.5),
|
color: paletteColor.color.withOpacity(.5),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -104,11 +95,13 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
height: 30,
|
height: 30,
|
||||||
child: SpotubeMarqueeText(
|
child: SpotubeMarqueeText(
|
||||||
text: currentTrack?.name ?? "Not playing",
|
text: currentTrack?.name ?? "Not playing",
|
||||||
style:
|
style: Theme.of(context)
|
||||||
Theme.of(context).textTheme.headline5?.copyWith(
|
.textTheme
|
||||||
fontWeight: FontWeight.bold,
|
.headlineSmall
|
||||||
color: paletteColor.titleTextColor,
|
?.copyWith(
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
|
color: paletteColor.titleTextColor,
|
||||||
|
),
|
||||||
isHovering: true,
|
isHovering: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -117,20 +110,24 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
TypeConversionUtils.artists_X_String<Artist>(
|
TypeConversionUtils.artists_X_String<Artist>(
|
||||||
currentTrack?.artists ?? [],
|
currentTrack?.artists ?? [],
|
||||||
),
|
),
|
||||||
style:
|
style: Theme.of(context)
|
||||||
Theme.of(context).textTheme.headline6!.copyWith(
|
.textTheme
|
||||||
fontWeight: FontWeight.bold,
|
.titleLarge!
|
||||||
color: paletteColor.bodyTextColor,
|
.copyWith(
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
|
color: paletteColor.bodyTextColor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
TypeConversionUtils.artists_X_ClickableArtists(
|
TypeConversionUtils.artists_X_ClickableArtists(
|
||||||
currentTrack?.artists ?? [],
|
currentTrack?.artists ?? [],
|
||||||
textStyle:
|
textStyle: Theme.of(context)
|
||||||
Theme.of(context).textTheme.headline6!.copyWith(
|
.textTheme
|
||||||
fontWeight: FontWeight.bold,
|
.titleLarge!
|
||||||
color: paletteColor.bodyTextColor,
|
.copyWith(
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
|
color: paletteColor.bodyTextColor,
|
||||||
|
),
|
||||||
onRouteChange: (route) {
|
onRouteChange: (route) {
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
GoRouter.of(context).push(route);
|
GoRouter.of(context).push(route);
|
||||||
@ -193,7 +190,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
floatingQueue: false,
|
floatingQueue: false,
|
||||||
extraActions: [
|
extraActions: [
|
||||||
PlatformIconButton(
|
IconButton(
|
||||||
tooltip: "Open Lyrics",
|
tooltip: "Open Lyrics",
|
||||||
icon: const Icon(SpotubeIcons.music),
|
icon: const Icon(SpotubeIcons.music),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -3,12 +3,10 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
|
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
|
||||||
import 'package:spotube/components/root/bottom_player.dart';
|
import 'package:spotube/components/root/bottom_player.dart';
|
||||||
import 'package:spotube/components/root/sidebar.dart';
|
import 'package:spotube/components/root/sidebar.dart';
|
||||||
import 'package:spotube/components/root/spotube_navigation_bar.dart';
|
import 'package:spotube/components/root/spotube_navigation_bar.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
|
||||||
import 'package:spotube/hooks/use_update_checker.dart';
|
import 'package:spotube/hooks/use_update_checker.dart';
|
||||||
import 'package:spotube/provider/downloader_provider.dart';
|
import 'package:spotube/provider/downloader_provider.dart';
|
||||||
|
|
||||||
@ -35,8 +33,8 @@ class RootApp extends HookConsumerWidget {
|
|||||||
useEffect(() {
|
useEffect(() {
|
||||||
downloader.onFileExists = (track) async {
|
downloader.onFileExists = (track) async {
|
||||||
if (!isMounted()) return false;
|
if (!isMounted()) return false;
|
||||||
return await showPlatformAlertDialog<bool>(
|
return await showDialog<bool>(
|
||||||
context,
|
context: context,
|
||||||
builder: (context) => ReplaceDownloadedDialog(
|
builder: (context) => ReplaceDownloadedDialog(
|
||||||
track: track,
|
track: track,
|
||||||
),
|
),
|
||||||
@ -49,7 +47,7 @@ class RootApp extends HookConsumerWidget {
|
|||||||
// checks for latest version of the application
|
// checks for latest version of the application
|
||||||
useUpdateChecker(ref);
|
useUpdateChecker(ref);
|
||||||
|
|
||||||
final backgroundColor = PlatformTheme.of(context).scaffoldBackgroundColor!;
|
final backgroundColor = Theme.of(context).scaffoldBackgroundColor;
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
@ -63,10 +61,7 @@ class RootApp extends HookConsumerWidget {
|
|||||||
return null;
|
return null;
|
||||||
}, [backgroundColor]);
|
}, [backgroundColor]);
|
||||||
|
|
||||||
return PlatformScaffold(
|
return Scaffold(
|
||||||
appBar: platform == TargetPlatform.windows
|
|
||||||
? PageWindowTitleBar(hideWhenWindows: false) as PreferredSizeWidget?
|
|
||||||
: null,
|
|
||||||
body: Sidebar(
|
body: Sidebar(
|
||||||
selectedIndex: index.value,
|
selectedIndex: index.value,
|
||||||
onSelectedIndexChanged: (i) {
|
onSelectedIndexChanged: (i) {
|
||||||
|
@ -4,7 +4,7 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:flutter/material.dart' hide Page;
|
import 'package:flutter/material.dart' hide Page;
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/album/album_card.dart';
|
import 'package:spotube/components/album/album_card.dart';
|
||||||
@ -62,7 +62,7 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: PlatformScaffold(
|
child: Scaffold(
|
||||||
appBar: kIsDesktop && !kIsMacOS ? PageWindowTitleBar() : null,
|
appBar: kIsDesktop && !kIsMacOS ? PageWindowTitleBar() : null,
|
||||||
body: !authenticationNotifier.isLoggedIn
|
body: !authenticationNotifier.isLoggedIn
|
||||||
? const AnonymousFallback()
|
? const AnonymousFallback()
|
||||||
@ -73,15 +73,12 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
horizontal: 20,
|
horizontal: 20,
|
||||||
vertical: 10,
|
vertical: 10,
|
||||||
),
|
),
|
||||||
color: PlatformTheme.of(context).scaffoldBackgroundColor,
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
child: PlatformTextField(
|
child: TextField(
|
||||||
prefixIcon: SpotubeIcons.search,
|
decoration: const InputDecoration(
|
||||||
prefixIconColor: PlatformProperty.only(
|
prefixIcon: Icon(SpotubeIcons.search),
|
||||||
ios:
|
hintText: "Search...",
|
||||||
PlatformTheme.of(context).textTheme?.caption?.color,
|
),
|
||||||
other: null,
|
|
||||||
).resolve(platform!),
|
|
||||||
placeholder: "Search...",
|
|
||||||
onSubmitted: (value) async {
|
onSubmitted: (value) async {
|
||||||
ref.read(searchTermStateProvider.notifier).state =
|
ref.read(searchTermStateProvider.notifier).state =
|
||||||
value;
|
value;
|
||||||
@ -133,11 +130,15 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (tracks.isNotEmpty)
|
if (tracks.isNotEmpty)
|
||||||
PlatformText.headline("Songs"),
|
Text(
|
||||||
|
"Songs",
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.titleLarge!,
|
||||||
|
),
|
||||||
if (searchTrack.isLoadingPage)
|
if (searchTrack.isLoadingPage)
|
||||||
const PlatformCircularProgressIndicator()
|
const CircularProgressIndicator()
|
||||||
else if (searchTrack.hasPageError)
|
else if (searchTrack.hasPageError)
|
||||||
PlatformText(searchTrack.errors.lastOrNull
|
Text(searchTrack.errors.lastOrNull
|
||||||
?.toString() ??
|
?.toString() ??
|
||||||
"")
|
"")
|
||||||
else
|
else
|
||||||
@ -182,17 +183,21 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
if (searchTrack.hasNextPage &&
|
if (searchTrack.hasNextPage &&
|
||||||
tracks.isNotEmpty)
|
tracks.isNotEmpty)
|
||||||
Center(
|
Center(
|
||||||
child: PlatformTextButton(
|
child: TextButton(
|
||||||
onPressed: searchTrack.isRefreshingPage
|
onPressed: searchTrack.isRefreshingPage
|
||||||
? null
|
? null
|
||||||
: () => searchTrack.fetchNext(),
|
: () => searchTrack.fetchNext(),
|
||||||
child: searchTrack.isRefreshingPage
|
child: searchTrack.isRefreshingPage
|
||||||
? const PlatformCircularProgressIndicator()
|
? const CircularProgressIndicator()
|
||||||
: const PlatformText("Load more"),
|
: const Text("Load more"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (playlists.isNotEmpty)
|
if (playlists.isNotEmpty)
|
||||||
PlatformText.headline("Playlists"),
|
Text(
|
||||||
|
"Playlists",
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.titleLarge!,
|
||||||
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
ScrollConfiguration(
|
ScrollConfiguration(
|
||||||
behavior:
|
behavior:
|
||||||
@ -236,16 +241,20 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (searchPlaylist.isLoadingPage)
|
if (searchPlaylist.isLoadingPage)
|
||||||
const PlatformCircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
if (searchPlaylist.hasPageError)
|
if (searchPlaylist.hasPageError)
|
||||||
PlatformText(
|
Text(
|
||||||
searchPlaylist.errors.lastOrNull
|
searchPlaylist.errors.lastOrNull
|
||||||
?.toString() ??
|
?.toString() ??
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (artists.isNotEmpty)
|
if (artists.isNotEmpty)
|
||||||
PlatformText.headline("Artists"),
|
Text(
|
||||||
|
"Artists",
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.titleLarge!,
|
||||||
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
ScrollConfiguration(
|
ScrollConfiguration(
|
||||||
behavior:
|
behavior:
|
||||||
@ -289,16 +298,21 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (searchArtist.isLoadingPage)
|
if (searchArtist.isLoadingPage)
|
||||||
const PlatformCircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
if (searchArtist.hasPageError)
|
if (searchArtist.hasPageError)
|
||||||
PlatformText(
|
Text(
|
||||||
searchArtist.errors.lastOrNull
|
searchArtist.errors.lastOrNull
|
||||||
?.toString() ??
|
?.toString() ??
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (albums.isNotEmpty)
|
if (albums.isNotEmpty)
|
||||||
PlatformText.subheading("Albums"),
|
Text(
|
||||||
|
"Albums",
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium!,
|
||||||
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
ScrollConfiguration(
|
ScrollConfiguration(
|
||||||
behavior:
|
behavior:
|
||||||
@ -340,9 +354,9 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (searchAlbum.isLoadingPage)
|
if (searchAlbum.isLoadingPage)
|
||||||
const PlatformCircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
if (searchAlbum.hasPageError)
|
if (searchAlbum.hasPageError)
|
||||||
PlatformText(
|
Text(
|
||||||
searchAlbum.errors.lastOrNull?.toString() ??
|
searchAlbum.errors.lastOrNull?.toString() ??
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
@ -17,10 +16,10 @@ class AboutSpotube extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final packageInfo = usePackageInfo();
|
final packageInfo = usePackageInfo();
|
||||||
|
|
||||||
return PlatformScaffold(
|
return Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: PageWindowTitleBar(
|
||||||
leading: const PlatformBackButton(),
|
leading: const BackButton(),
|
||||||
center: const PlatformText("About Spotube"),
|
title: const Text("About Spotube"),
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -35,14 +34,15 @@ class AboutSpotube extends HookConsumerWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
PlatformText.headline(
|
Text(
|
||||||
"Spotube, a light-weight, cross-platform, free-for-all spotify client",
|
"Spotube, a light-weight, cross-platform, free-for-all spotify client",
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const PlatformText(
|
const Text(
|
||||||
"Founder: Kingkor Roy Tirtho",
|
"Founder: Kingkor Roy Tirtho",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -60,11 +60,11 @@ class AboutSpotube extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
PlatformText(
|
Text(
|
||||||
"Version: v${packageInfo.version}",
|
"Version: v${packageInfo.version}",
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
PlatformText(
|
Text(
|
||||||
"Build Number: ${packageInfo.buildNumber.replaceAll(".", " ")}",
|
"Build Number: ${packageInfo.buildNumber.replaceAll(".", " ")}",
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
@ -75,7 +75,7 @@ class AboutSpotube extends HookConsumerWidget {
|
|||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const PlatformText(
|
child: const Text(
|
||||||
"Repository: https://github.com/KRTirtho/spotube",
|
"Repository: https://github.com/KRTirtho/spotube",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -87,7 +87,7 @@ class AboutSpotube extends HookConsumerWidget {
|
|||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const PlatformText(
|
child: const Text(
|
||||||
"License: BSD-4-Clause",
|
"License: BSD-4-Clause",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -99,7 +99,7 @@ class AboutSpotube extends HookConsumerWidget {
|
|||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const PlatformText(
|
child: const Text(
|
||||||
"Bugs+Issues: https://github.com/KRTirtho/spotube/issues",
|
"Bugs+Issues: https://github.com/KRTirtho/spotube/issues",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -178,21 +178,24 @@ class AboutSpotube extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
PlatformText.caption(
|
Text(
|
||||||
"Made with ❤️ in Bangladesh🇧🇩",
|
"Made with ❤️ in Bangladesh🇧🇩",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
PlatformText.caption(
|
Text(
|
||||||
"© 2021-${DateTime.now().year} Kingkor Roy Tirtho",
|
"© 2021-${DateTime.now().year} Kingkor Roy Tirtho",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 750),
|
constraints: const BoxConstraints(maxWidth: 750),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: PlatformText.caption(
|
child: Text(
|
||||||
licenseText,
|
licenseText,
|
||||||
textAlign: TextAlign.justify,
|
textAlign: TextAlign.justify,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
import 'package:fuzzywuzzy/fuzzywuzzy.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/provider/blacklist_provider.dart';
|
import 'package:spotube/provider/blacklist_provider.dart';
|
||||||
@ -35,23 +35,23 @@ class BlackListPage extends HookConsumerWidget {
|
|||||||
[blacklist, searchText.value],
|
[blacklist, searchText.value],
|
||||||
);
|
);
|
||||||
|
|
||||||
return PlatformScaffold(
|
return Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: PageWindowTitleBar(
|
||||||
center: const PlatformText("Blacklist"),
|
title: const Text("Blacklist"),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
leading: const PlatformBackButton(),
|
leading: const BackButton(),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: platform == TargetPlatform.windows
|
padding: const EdgeInsets.all(8.0),
|
||||||
? const EdgeInsets.all(8.0).copyWith(left: 45)
|
child: TextField(
|
||||||
: const EdgeInsets.all(8.0),
|
|
||||||
child: PlatformTextField(
|
|
||||||
onChanged: (value) => searchText.value = value,
|
onChanged: (value) => searchText.value = value,
|
||||||
placeholder: "Search",
|
decoration: const InputDecoration(
|
||||||
prefixIcon: SpotubeIcons.search,
|
hintText: "Search",
|
||||||
|
prefixIcon: Icon(SpotubeIcons.search),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
@ -59,11 +59,11 @@ class BlackListPage extends HookConsumerWidget {
|
|||||||
itemCount: filteredBlacklist.length,
|
itemCount: filteredBlacklist.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = filteredBlacklist.elementAt(index);
|
final item = filteredBlacklist.elementAt(index);
|
||||||
return PlatformListTile(
|
return ListTile(
|
||||||
leading: PlatformText("${index + 1}."),
|
leading: Text("${index + 1}."),
|
||||||
title: PlatformText("${item.name} (${item.type.name})"),
|
title: Text("${item.name} (${item.type.name})"),
|
||||||
subtitle: PlatformText.caption(item.id),
|
subtitle: Text(item.id),
|
||||||
trailing: PlatformIconButton(
|
trailing: IconButton(
|
||||||
icon: Icon(SpotubeIcons.trash, color: Colors.red[400]),
|
icon: Icon(SpotubeIcons.trash, color: Colors.red[400]),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref
|
ref
|
||||||
|
@ -4,12 +4,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:platform_ui/platform_ui.dart';
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
|
import 'package:spotube/components/settings/color_scheme_picker_dialog.dart';
|
||||||
import 'package:spotube/components/shared/adaptive/adaptive_list_tile.dart';
|
import 'package:spotube/components/shared/adaptive/adaptive_list_tile.dart';
|
||||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
import 'package:spotube/main.dart';
|
|
||||||
import 'package:spotube/collections/spotify_markets.dart';
|
import 'package:spotube/collections/spotify_markets.dart';
|
||||||
import 'package:spotube/provider/authentication_provider.dart';
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||||
@ -24,7 +23,9 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||||
|
|
||||||
final pickColorScheme = useCallback((ColorSchemeType schemeType) {
|
final pickColorScheme = useCallback((ColorSchemeType schemeType) {
|
||||||
return () => showPlatformAlertDialog(context, builder: (context) {
|
return () => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
return ColorSchemePickerDialog(
|
return ColorSchemePickerDialog(
|
||||||
schemeType: schemeType,
|
schemeType: schemeType,
|
||||||
);
|
);
|
||||||
@ -40,9 +41,9 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
}, [preferences.downloadLocation]);
|
}, [preferences.downloadLocation]);
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: PlatformScaffold(
|
child: Scaffold(
|
||||||
appBar: PageWindowTitleBar(
|
appBar: PageWindowTitleBar(
|
||||||
center: PlatformText.headline("Settings"),
|
title: const Text("Settings"),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: Row(
|
body: Row(
|
||||||
@ -53,17 +54,18 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
constraints: const BoxConstraints(maxWidth: 1366),
|
constraints: const BoxConstraints(maxWidth: 1366),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
PlatformText(
|
Text(
|
||||||
" Account",
|
" Account",
|
||||||
style: PlatformTextTheme.of(context)
|
style: Theme.of(context)
|
||||||
.headline
|
.textTheme
|
||||||
|
.headlineSmall
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
if (auth == null)
|
if (auth == null)
|
||||||
AdaptiveListTile(
|
AdaptiveListTile(
|
||||||
leading: Icon(
|
leading: Icon(
|
||||||
SpotubeIcons.login,
|
SpotubeIcons.login,
|
||||||
color: PlatformTheme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
title: SizedBox(
|
title: SizedBox(
|
||||||
height: 50,
|
height: 50,
|
||||||
@ -74,12 +76,12 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
"Login with your Spotify account",
|
"Login with your Spotify account",
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: PlatformTheme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: (context, update) => PlatformFilledButton(
|
trailing: (context, update) => FilledButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
GoRouter.of(context).push("/login");
|
GoRouter.of(context).push("/login");
|
||||||
},
|
},
|
||||||
@ -90,15 +92,14 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: PlatformText(
|
child: Text("Connect with Spotify".toUpperCase()),
|
||||||
"Connect with Spotify".toUpperCase()),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
Builder(builder: (context) {
|
Builder(builder: (context) {
|
||||||
return PlatformListTile(
|
return ListTile(
|
||||||
leading: const Icon(SpotubeIcons.logout),
|
leading: const Icon(SpotubeIcons.logout),
|
||||||
title: SizedBox(
|
title: const SizedBox(
|
||||||
height: 50,
|
height: 50,
|
||||||
width: 180,
|
width: 180,
|
||||||
child: Align(
|
child: Align(
|
||||||
@ -106,11 +107,10 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
child: AutoSizeText(
|
child: AutoSizeText(
|
||||||
"Log out of this account",
|
"Log out of this account",
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: PlatformTextTheme.of(context).body,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: PlatformFilledButton(
|
trailing: FilledButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
MaterialStateProperty.all(Colors.red),
|
MaterialStateProperty.all(Colors.red),
|
||||||
@ -124,44 +124,40 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
.logout();
|
.logout();
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const PlatformText("Logout"),
|
child: const Text("Logout"),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
PlatformText(
|
Text(
|
||||||
" Appearance",
|
" Appearance",
|
||||||
style: PlatformTextTheme.of(context)
|
style: Theme.of(context)
|
||||||
.headline
|
.textTheme
|
||||||
|
.headlineSmall
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
AdaptiveListTile(
|
AdaptiveListTile(
|
||||||
leading: const Icon(SpotubeIcons.dashboard),
|
leading: const Icon(SpotubeIcons.dashboard),
|
||||||
title: const PlatformText("Layout Mode"),
|
title: const Text("Layout Mode"),
|
||||||
subtitle: const PlatformText(
|
subtitle: const Text(
|
||||||
"Override responsive layout mode settings",
|
"Override responsive layout mode settings",
|
||||||
),
|
),
|
||||||
trailing: (context, update) =>
|
trailing: (context, update) => DropdownMenu<LayoutMode>(
|
||||||
PlatformDropDownMenu<LayoutMode>(
|
dropdownMenuEntries: const [
|
||||||
value: preferences.layoutMode,
|
DropdownMenuEntry(
|
||||||
items: [
|
|
||||||
PlatformDropDownMenuItem(
|
|
||||||
value: LayoutMode.adaptive,
|
value: LayoutMode.adaptive,
|
||||||
child: const PlatformText(
|
label: "Adaptive",
|
||||||
"Adaptive",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PlatformDropDownMenuItem(
|
DropdownMenuEntry(
|
||||||
value: LayoutMode.compact,
|
value: LayoutMode.compact,
|
||||||
child: const PlatformText(
|
label: "Compact",
|
||||||
"Compact",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PlatformDropDownMenuItem(
|
DropdownMenuEntry(
|
||||||
value: LayoutMode.extended,
|
value: LayoutMode.extended,
|
||||||
child: const PlatformText("Extended"),
|
label: "Extended",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
initialSelection: preferences.layoutMode,
|
||||||
|
onSelected: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setLayoutMode(value);
|
preferences.setLayoutMode(value);
|
||||||
update?.call(() {});
|
update?.call(() {});
|
||||||
@ -171,25 +167,24 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
AdaptiveListTile(
|
AdaptiveListTile(
|
||||||
leading: const Icon(SpotubeIcons.darkMode),
|
leading: const Icon(SpotubeIcons.darkMode),
|
||||||
title: const PlatformText("Theme"),
|
title: const Text("Theme"),
|
||||||
trailing: (context, update) =>
|
trailing: (context, update) => DropdownMenu<ThemeMode>(
|
||||||
PlatformDropDownMenu<ThemeMode>(
|
initialSelection: preferences.themeMode,
|
||||||
value: preferences.themeMode,
|
dropdownMenuEntries: const [
|
||||||
items: [
|
DropdownMenuEntry(
|
||||||
PlatformDropDownMenuItem(
|
|
||||||
value: ThemeMode.dark,
|
value: ThemeMode.dark,
|
||||||
child: const PlatformText("Dark"),
|
label: "Dark",
|
||||||
),
|
),
|
||||||
PlatformDropDownMenuItem(
|
DropdownMenuEntry(
|
||||||
value: ThemeMode.light,
|
value: ThemeMode.light,
|
||||||
child: const PlatformText("Light"),
|
label: "Light",
|
||||||
),
|
),
|
||||||
PlatformDropDownMenuItem(
|
DropdownMenuEntry(
|
||||||
value: ThemeMode.system,
|
value: ThemeMode.system,
|
||||||
child: const PlatformText("System"),
|
label: "System",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onSelected: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setThemeMode(value);
|
preferences.setThemeMode(value);
|
||||||
update?.call(() {});
|
update?.call(() {});
|
||||||
@ -197,45 +192,9 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AdaptiveListTile(
|
ListTile(
|
||||||
leading: const Icon(SpotubeIcons.platform),
|
|
||||||
title: const PlatformText("Mimic Platform"),
|
|
||||||
trailing: (context, update) =>
|
|
||||||
DropdownButton<TargetPlatform>(
|
|
||||||
value: Spotube.of(context).appPlatform,
|
|
||||||
items: const [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: TargetPlatform.android,
|
|
||||||
child: PlatformText("Android (Material You)"),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: TargetPlatform.iOS,
|
|
||||||
child: PlatformText("iOS (Cupertino)"),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: TargetPlatform.macOS,
|
|
||||||
child: PlatformText("macOS (Aqua)"),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: TargetPlatform.linux,
|
|
||||||
child: PlatformText("Linux (GTK+Libadwaita)"),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: TargetPlatform.windows,
|
|
||||||
child: PlatformText("Windows 11 (Fluent UI)"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
Spotube.of(context).changePlatform(value);
|
|
||||||
update?.call(() {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PlatformListTile(
|
|
||||||
leading: const Icon(SpotubeIcons.palette),
|
leading: const Icon(SpotubeIcons.palette),
|
||||||
title: const PlatformText("Accent Color"),
|
title: const Text("Accent Color"),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 15,
|
horizontal: 15,
|
||||||
vertical: 5,
|
vertical: 5,
|
||||||
@ -247,41 +206,37 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
onTap: pickColorScheme(ColorSchemeType.accent),
|
onTap: pickColorScheme(ColorSchemeType.accent),
|
||||||
),
|
),
|
||||||
PlatformListTile(
|
SwitchListTile(
|
||||||
leading: const Icon(SpotubeIcons.album),
|
secondary: const Icon(SpotubeIcons.album),
|
||||||
title: const PlatformText("Rotating Album Art"),
|
title: const Text("Rotating Album Art"),
|
||||||
trailing: PlatformSwitch(
|
value: preferences.rotatingAlbumArt,
|
||||||
value: preferences.rotatingAlbumArt,
|
onChanged: (state) {
|
||||||
onChanged: (state) {
|
preferences.setRotatingAlbumArt(state);
|
||||||
preferences.setRotatingAlbumArt(state);
|
},
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PlatformText(
|
Text(
|
||||||
" Playback",
|
" Playback",
|
||||||
style: PlatformTextTheme.of(context)
|
style: Theme.of(context)
|
||||||
.headline
|
.textTheme
|
||||||
|
.headlineSmall
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
AdaptiveListTile(
|
AdaptiveListTile(
|
||||||
leading: const Icon(SpotubeIcons.audioQuality),
|
leading: const Icon(SpotubeIcons.audioQuality),
|
||||||
title: const PlatformText("Audio Quality"),
|
title: const Text("Audio Quality"),
|
||||||
trailing: (context, update) =>
|
trailing: (context, update) => DropdownMenu<AudioQuality>(
|
||||||
PlatformDropDownMenu<AudioQuality>(
|
initialSelection: preferences.audioQuality,
|
||||||
value: preferences.audioQuality,
|
dropdownMenuEntries: const [
|
||||||
items: [
|
DropdownMenuEntry(
|
||||||
PlatformDropDownMenuItem(
|
|
||||||
value: AudioQuality.high,
|
value: AudioQuality.high,
|
||||||
child: const PlatformText(
|
label: "High",
|
||||||
"High",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PlatformDropDownMenuItem(
|
DropdownMenuEntry(
|
||||||
value: AudioQuality.low,
|
value: AudioQuality.low,
|
||||||
child: const PlatformText("Low"),
|
label: "Low",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onSelected: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
preferences.setAudioQuality(value);
|
preferences.setAudioQuality(value);
|
||||||
update?.call(() {});
|
update?.call(() {});
|
||||||
@ -289,37 +244,31 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PlatformListTile(
|
SwitchListTile(
|
||||||
leading: const Icon(SpotubeIcons.download),
|
secondary: const Icon(SpotubeIcons.download),
|
||||||
title: const PlatformText(
|
title: const Text("Pre download and play"),
|
||||||
"Pre download and play",
|
subtitle: const Text(
|
||||||
),
|
|
||||||
subtitle: const PlatformText(
|
|
||||||
"Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)",
|
"Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)",
|
||||||
),
|
),
|
||||||
trailing: PlatformSwitch(
|
value: preferences.predownload,
|
||||||
value: preferences.predownload,
|
onChanged: (state) {
|
||||||
onChanged: (state) {
|
preferences.setPredownload(state);
|
||||||
preferences.setPredownload(state);
|
},
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PlatformListTile(
|
SwitchListTile(
|
||||||
leading: const Icon(SpotubeIcons.fastForward),
|
secondary: const Icon(SpotubeIcons.fastForward),
|
||||||
title: const PlatformText(
|
title: const Text(
|
||||||
"Skip non-music segments (SponsorBlock)",
|
"Skip non-music segments (SponsorBlock)",
|
||||||
),
|
),
|
||||||
trailing: PlatformSwitch(
|
value: preferences.skipSponsorSegments,
|
||||||
value: preferences.skipSponsorSegments,
|
onChanged: (state) {
|
||||||
onChanged: (state) {
|
preferences.setSkipSponsorSegments(state);
|
||||||
preferences.setSkipSponsorSegments(state);
|
},
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PlatformListTile(
|
ListTile(
|
||||||
leading: const Icon(SpotubeIcons.playlistRemove),
|
leading: const Icon(SpotubeIcons.playlistRemove),
|
||||||
title: const PlatformText("Blacklist"),
|
title: const Text("Blacklist"),
|
||||||
subtitle: const PlatformText(
|
subtitle: const Text(
|
||||||
"Blacklisted tracks and artists",
|
"Blacklisted tracks and artists",
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -327,31 +276,30 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
trailing: const Icon(SpotubeIcons.angleRight),
|
trailing: const Icon(SpotubeIcons.angleRight),
|
||||||
),
|
),
|
||||||
PlatformText(
|
Text(
|
||||||
" Search",
|
" Search",
|
||||||
style: PlatformTextTheme.of(context)
|
style: Theme.of(context)
|
||||||
.headline
|
.textTheme
|
||||||
|
.headlineSmall
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
AdaptiveListTile(
|
AdaptiveListTile(
|
||||||
leading: const Icon(SpotubeIcons.shoppingBag),
|
leading: const Icon(SpotubeIcons.shoppingBag),
|
||||||
title: const PlatformText("Market Place"),
|
title: const Text("Market Place"),
|
||||||
subtitle: PlatformText.caption(
|
subtitle: const Text("Recommendation Country"),
|
||||||
"Recommendation Country",
|
|
||||||
),
|
|
||||||
trailing: (context, update) => ConstrainedBox(
|
trailing: (context, update) => ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 350),
|
constraints: const BoxConstraints(maxWidth: 350),
|
||||||
child: PlatformDropDownMenu(
|
child: DropdownMenu(
|
||||||
value: preferences.recommendationMarket,
|
initialSelection: preferences.recommendationMarket,
|
||||||
items: spotifyMarkets
|
dropdownMenuEntries: spotifyMarkets
|
||||||
.map(
|
.map(
|
||||||
(country) => (PlatformDropDownMenuItem(
|
(country) => DropdownMenuEntry(
|
||||||
value: country.first,
|
value: country.first,
|
||||||
child: PlatformText(country.last),
|
label: country.last,
|
||||||
)),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (value) {
|
onSelected: (value) {
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
preferences.setRecommendationMarket(
|
preferences.setRecommendationMarket(
|
||||||
value as String,
|
value as String,
|
||||||
@ -361,37 +309,36 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PlatformText(
|
Text(
|
||||||
" Downloads",
|
" Downloads",
|
||||||
style: PlatformTextTheme.of(context)
|
style: Theme.of(context)
|
||||||
.headline
|
.textTheme
|
||||||
|
.headlineSmall
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
PlatformListTile(
|
ListTile(
|
||||||
leading: const Icon(SpotubeIcons.download),
|
leading: const Icon(SpotubeIcons.download),
|
||||||
title: const PlatformText("Download Location"),
|
title: const Text("Download Location"),
|
||||||
subtitle: PlatformText(preferences.downloadLocation),
|
subtitle: Text(preferences.downloadLocation),
|
||||||
trailing: PlatformFilledButton(
|
trailing: FilledButton(
|
||||||
onPressed: pickDownloadLocation,
|
onPressed: pickDownloadLocation,
|
||||||
child: const Icon(SpotubeIcons.folder),
|
child: const Icon(SpotubeIcons.folder),
|
||||||
),
|
),
|
||||||
onTap: pickDownloadLocation,
|
onTap: pickDownloadLocation,
|
||||||
),
|
),
|
||||||
PlatformListTile(
|
SwitchListTile(
|
||||||
leading: const Icon(SpotubeIcons.lyrics),
|
secondary: const Icon(SpotubeIcons.lyrics),
|
||||||
title: const PlatformText(
|
title: const Text("Download lyrics along with the Track"),
|
||||||
"Download lyrics along with the Track"),
|
value: preferences.saveTrackLyrics,
|
||||||
trailing: PlatformSwitch(
|
onChanged: (state) {
|
||||||
value: preferences.saveTrackLyrics,
|
preferences.setSaveTrackLyrics(state);
|
||||||
onChanged: (state) {
|
},
|
||||||
preferences.setSaveTrackLyrics(state);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PlatformText(
|
Text(
|
||||||
" About",
|
" About",
|
||||||
style: PlatformTextTheme.of(context)
|
style: Theme.of(context)
|
||||||
.headline
|
.textTheme
|
||||||
|
.headlineSmall
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
AdaptiveListTile(
|
AdaptiveListTile(
|
||||||
@ -414,7 +361,7 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: (context, update) => PlatformFilledButton(
|
trailing: (context, update) => FilledButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
MaterialStatePropertyAll(Colors.red[100]),
|
MaterialStatePropertyAll(Colors.red[100]),
|
||||||
@ -434,23 +381,21 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
children: const [
|
children: const [
|
||||||
Icon(SpotubeIcons.heart),
|
Icon(SpotubeIcons.heart),
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
PlatformText("Please Sponsor/Donate"),
|
Text("Please Sponsor/Donate"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PlatformListTile(
|
SwitchListTile(
|
||||||
leading: const Icon(SpotubeIcons.update),
|
secondary: const Icon(SpotubeIcons.update),
|
||||||
title: const PlatformText("Check for Update"),
|
title: const Text("Check for Update"),
|
||||||
trailing: PlatformSwitch(
|
value: preferences.checkUpdate,
|
||||||
value: preferences.checkUpdate,
|
onChanged: (checked) =>
|
||||||
onChanged: (checked) =>
|
preferences.setCheckUpdate(checked),
|
||||||
preferences.setCheckUpdate(checked),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PlatformListTile(
|
ListTile(
|
||||||
leading: const Icon(SpotubeIcons.info),
|
leading: const Icon(SpotubeIcons.info),
|
||||||
title: const PlatformText("About Spotube"),
|
title: const Text("About Spotube"),
|
||||||
trailing: const Icon(SpotubeIcons.angleRight),
|
trailing: const Icon(SpotubeIcons.angleRight),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).push("/settings/about");
|
GoRouter.of(context).push("/settings/about");
|
||||||
|
@ -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 audioplayers_darwin
|
||||||
import catcher
|
import catcher
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
import macos_ui
|
|
||||||
import metadata_god
|
import metadata_god
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
@ -27,7 +26,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||||
CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin"))
|
CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin"))
|
|
||||||
MetadataGodPlugin.register(with: registry.registrar(forPlugin: "MetadataGodPlugin"))
|
MetadataGodPlugin.register(with: registry.registrar(forPlugin: "MetadataGodPlugin"))
|
||||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
88
pubspec.lock
88
pubspec.lock
@ -9,14 +9,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "52.0.0"
|
version: "52.0.0"
|
||||||
adwaita:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: adwaita
|
|
||||||
sha256: "535781747357779fa2830815e0a1b6b7888c8c538194ba73ac4fc8d82412baec"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.5.2"
|
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -546,14 +538,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0-alpha.1"
|
version: "1.0.0-alpha.1"
|
||||||
fluent_ui:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: fluent_ui
|
|
||||||
sha256: ff172e78492faad2c9eca01b24ea8341f2bd6e1ce6986b15d4886fd2e7c3fb09
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.3.0"
|
|
||||||
fluentui_system_icons:
|
fluentui_system_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -671,11 +655,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
flutter_localizations:
|
|
||||||
dependency: transitive
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
flutter_mailer:
|
flutter_mailer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -782,14 +761,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
gsettings:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: gsettings
|
|
||||||
sha256: fe90d719e09a6f36607021047e642068a0c98839d9633db00b91633420ae8b0d
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.7"
|
|
||||||
hive:
|
hive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -910,22 +881,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.6.1"
|
version: "6.6.1"
|
||||||
libadwaita:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: libadwaita
|
|
||||||
sha256: de4dba194e6ba468af70988891164cc616126dc7ccbc4f42756851be93aa6de6
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.5"
|
|
||||||
libadwaita_core:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: libadwaita_core
|
|
||||||
sha256: "1a7689e4130e96be4b0ec615863fded25cd86c2b95a682e7cec9659d420de37c"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.5.4"
|
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -950,14 +905,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
macos_ui:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: macos_ui
|
|
||||||
sha256: "2b31858b56d44e807cf192c58cf6660b584c997f4746496d4892a903c1c27a49"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.9.1"
|
|
||||||
mailer:
|
mailer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1206,15 +1153,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
platform_ui:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
path: "."
|
|
||||||
ref: "339ea7922d2f881d9a72b565aeebc7e2c4b22509"
|
|
||||||
resolved-ref: "339ea7922d2f881d9a72b565aeebc7e2c4b22509"
|
|
||||||
url: "https://github.com/KRTirtho/platform_ui.git"
|
|
||||||
source: git
|
|
||||||
version: "0.1.0"
|
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1255,14 +1193,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.8+1"
|
version: "0.2.8+1"
|
||||||
popover_gtk:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: popover_gtk
|
|
||||||
sha256: c293bfa3bcb9436fe189f20f6397199ce996ba40d4b081e26793adcf3ceac45b
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.6+3"
|
|
||||||
process:
|
process:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1327,14 +1257,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
version: "3.2.1"
|
||||||
recase:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: recase
|
|
||||||
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.1.0"
|
|
||||||
riverpod:
|
riverpod:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1359,14 +1281,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.6"
|
version: "0.1.6"
|
||||||
scroll_pos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: scroll_pos
|
|
||||||
sha256: cfca311b6b8d51538ff90e206fbe6ce3b36e7125ea6da4a40eb626c7f9f083b1
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.3.0"
|
|
||||||
scroll_to_index:
|
scroll_to_index:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1808,4 +1722,4 @@ packages:
|
|||||||
version: "1.12.3"
|
version: "1.12.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.19.0 <3.0.0"
|
dart: ">=2.19.0 <3.0.0"
|
||||||
flutter: ">=3.7.0"
|
flutter: ">=3.3.0"
|
||||||
|
@ -11,7 +11,6 @@ environment:
|
|||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
adwaita: ^0.5.2
|
|
||||||
args: ^2.3.2
|
args: ^2.3.2
|
||||||
async: ^2.9.0
|
async: ^2.9.0
|
||||||
audio_service: ^0.18.9
|
audio_service: ^0.18.9
|
||||||
@ -29,7 +28,6 @@ dependencies:
|
|||||||
file_picker: ^5.2.2
|
file_picker: ^5.2.2
|
||||||
fl_query: ^1.0.0-alpha.0
|
fl_query: ^1.0.0-alpha.0
|
||||||
fl_query_hooks: ^1.0.0-alpha.0
|
fl_query_hooks: ^1.0.0-alpha.0
|
||||||
fluent_ui: ^4.3.0
|
|
||||||
fluentui_system_icons: ^1.1.189
|
fluentui_system_icons: ^1.1.189
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
@ -50,9 +48,7 @@ dependencies:
|
|||||||
introduction_screen: ^3.0.2
|
introduction_screen: ^3.0.2
|
||||||
json_annotation: ^4.8.0
|
json_annotation: ^4.8.0
|
||||||
json_serializable: ^6.6.0
|
json_serializable: ^6.6.0
|
||||||
libadwaita: ^1.2.5
|
|
||||||
logger: ^1.1.0
|
logger: ^1.1.0
|
||||||
macos_ui: ^1.9.0
|
|
||||||
marquee: ^2.2.3
|
marquee: ^2.2.3
|
||||||
metadata_god: ^0.3.2
|
metadata_god: ^0.3.2
|
||||||
mime: ^1.0.2
|
mime: ^1.0.2
|
||||||
@ -61,10 +57,6 @@ dependencies:
|
|||||||
path: ^1.8.0
|
path: ^1.8.0
|
||||||
path_provider: ^2.0.8
|
path_provider: ^2.0.8
|
||||||
permission_handler: ^10.2.0
|
permission_handler: ^10.2.0
|
||||||
platform_ui:
|
|
||||||
git:
|
|
||||||
url: https://github.com/KRTirtho/platform_ui.git
|
|
||||||
ref: 339ea7922d2f881d9a72b565aeebc7e2c4b22509
|
|
||||||
pocketbase: ^0.7.1+1
|
pocketbase: ^0.7.1+1
|
||||||
popover: ^0.2.6+3
|
popover: ^0.2.6+3
|
||||||
queue: ^3.1.0+1
|
queue: ^3.1.0+1
|
||||||
|
Loading…
Reference in New Issue
Block a user