mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
feat: initial platform_ui integration
What's changed: - Sidebar - Settings - UserLibrary (root) - Search (search field)
This commit is contained in:
parent
4b21cc8299
commit
9eee573ce9
@ -84,17 +84,13 @@ class Shell extends HookConsumerWidget {
|
|||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
body: Row(
|
body: Sidebar(
|
||||||
children: [
|
|
||||||
Sidebar(
|
|
||||||
selectedIndex: index.value,
|
selectedIndex: index.value,
|
||||||
onSelectedIndexChanged: (selectedIndex) {
|
onSelectedIndexChanged: (i) {
|
||||||
index.value = selectedIndex;
|
index.value = i;
|
||||||
GoRouter.of(context).go(_path[selectedIndex]!);
|
GoRouter.of(context).go(_path[index.value]!);
|
||||||
},
|
},
|
||||||
),
|
child: child,
|
||||||
Expanded(child: child),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
bottomNavigationBar: Column(
|
bottomNavigationBar: Column(
|
||||||
|
@ -5,6 +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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:spotube/components/Shared/UniversalImage.dart';
|
import 'package:spotube/components/Shared/UniversalImage.dart';
|
||||||
import 'package:spotube/hooks/useBreakpoints.dart';
|
import 'package:spotube/hooks/useBreakpoints.dart';
|
||||||
import 'package:spotube/models/sideBarTiles.dart';
|
import 'package:spotube/models/sideBarTiles.dart';
|
||||||
@ -15,16 +16,19 @@ import 'package:spotube/provider/SpotifyRequests.dart';
|
|||||||
import 'package:spotube/provider/UserPreferences.dart';
|
import 'package:spotube/provider/UserPreferences.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 FluentUI;
|
||||||
|
|
||||||
final sidebarExtendedStateProvider = StateProvider<bool?>((ref) => null);
|
final sidebarExtendedStateProvider = StateProvider<bool?>((ref) => null);
|
||||||
|
|
||||||
class Sidebar extends HookConsumerWidget {
|
class Sidebar extends HookConsumerWidget {
|
||||||
final int selectedIndex;
|
final int selectedIndex;
|
||||||
final void Function(int) onSelectedIndexChanged;
|
final void Function(int) onSelectedIndexChanged;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
const Sidebar({
|
const Sidebar({
|
||||||
required this.selectedIndex,
|
required this.selectedIndex,
|
||||||
required this.onSelectedIndexChanged,
|
required this.onSelectedIndexChanged,
|
||||||
|
required this.child,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -45,7 +49,6 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
final breakpoints = useBreakpoints();
|
final breakpoints = useBreakpoints();
|
||||||
final extended = useState(false);
|
final extended = useState(false);
|
||||||
|
|
||||||
final auth = ref.watch(authProvider);
|
|
||||||
final downloadCount = ref.watch(
|
final downloadCount = ref.watch(
|
||||||
downloaderProvider.select((s) => s.currentlyRunning),
|
downloaderProvider.select((s) => s.currentlyRunning),
|
||||||
);
|
);
|
||||||
@ -81,10 +84,31 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
child: Material(
|
child: PlatformSidebar(
|
||||||
color: Theme.of(context).navigationRailTheme.backgroundColor,
|
currentIndex: selectedIndex,
|
||||||
child: Column(
|
onIndexChanged: onSelectedIndexChanged,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
body: Map.fromEntries(
|
||||||
|
sidebarTileList.map(
|
||||||
|
(e) {
|
||||||
|
final icon = Icon(e.icon);
|
||||||
|
return MapEntry(
|
||||||
|
PlatformSidebarItem(
|
||||||
|
icon: icon,
|
||||||
|
title: Text(
|
||||||
|
e.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expanded: extended.value,
|
||||||
|
header: Column(
|
||||||
children: [
|
children: [
|
||||||
if (kIsDesktop)
|
if (kIsDesktop)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@ -126,43 +150,33 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
: _buildSmallLogo(),
|
: _buildSmallLogo(),
|
||||||
Expanded(
|
],
|
||||||
child: NavigationRail(
|
|
||||||
destinations: sidebarTileList.map(
|
|
||||||
(e) {
|
|
||||||
final icon = Icon(e.icon);
|
|
||||||
return NavigationRailDestination(
|
|
||||||
icon: e.title == "Library" && downloadCount > 0
|
|
||||||
? Badge(
|
|
||||||
badgeColor: Colors.red[100]!,
|
|
||||||
badgeContent: Text(
|
|
||||||
downloadCount.toString(),
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.red,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
),
|
||||||
|
windowsFooterItems: [
|
||||||
|
FluentUI.PaneItemAction(
|
||||||
|
icon: const FluentUI.Icon(FluentUI.FluentIcons.settings),
|
||||||
|
onTap: () => goToSettings(context),
|
||||||
),
|
),
|
||||||
animationType: BadgeAnimationType.fade,
|
],
|
||||||
child: icon,
|
footer: SidebarFooter(extended: extended.value),
|
||||||
)
|
|
||||||
: icon,
|
|
||||||
label: Text(
|
|
||||||
e.title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
).toList(),
|
}
|
||||||
selectedIndex: selectedIndex,
|
|
||||||
onDestinationSelected: onSelectedIndexChanged,
|
class SidebarFooter extends HookConsumerWidget {
|
||||||
extended: extended.value,
|
final bool extended;
|
||||||
),
|
const SidebarFooter({
|
||||||
),
|
Key? key,
|
||||||
SizedBox(
|
required this.extended,
|
||||||
width: extended.value ? 256 : 80,
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, ref) {
|
||||||
|
final auth = ref.watch(authProvider);
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
width: extended ? 256 : 80,
|
||||||
child: HookBuilder(
|
child: HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final me = useQuery(
|
final me = useQuery(
|
||||||
@ -185,7 +199,7 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
return;
|
return;
|
||||||
}, [auth.isLoggedIn, me.hasData]);
|
}, [auth.isLoggedIn, me.hasData]);
|
||||||
|
|
||||||
if (extended.value) {
|
if (extended) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16).copyWith(left: 0),
|
padding: const EdgeInsets.all(16).copyWith(left: 0),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -198,15 +212,12 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
else if (data != null)
|
else if (data != null)
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
UniversalImage.imageProvider(
|
UniversalImage.imageProvider(avatarImg),
|
||||||
avatarImg),
|
onBackgroundImageError: (exception, stackTrace) =>
|
||||||
onBackgroundImageError:
|
|
||||||
(exception, stackTrace) =>
|
|
||||||
Image.asset(
|
Image.asset(
|
||||||
"assets/user-placeholder.png",
|
"assets/user-placeholder.png",
|
||||||
height: 16,
|
height: 16,
|
||||||
@ -232,17 +243,16 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.settings_outlined),
|
icon: const Icon(Icons.settings_outlined),
|
||||||
onPressed: () => goToSettings(context)),
|
onPressed: () => Sidebar.goToSettings(context)),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => goToSettings(context),
|
onTap: () => Sidebar.goToSettings(context),
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
backgroundImage:
|
backgroundImage: UniversalImage.imageProvider(avatarImg),
|
||||||
UniversalImage.imageProvider(avatarImg),
|
|
||||||
onBackgroundImageError: (exception, stackTrace) =>
|
onBackgroundImageError: (exception, stackTrace) =>
|
||||||
Image.asset(
|
Image.asset(
|
||||||
"assets/user-placeholder.png",
|
"assets/user-placeholder.png",
|
||||||
@ -255,10 +265,6 @@ class Sidebar extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +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/UniversalImage.dart';
|
import 'package:spotube/components/Shared/UniversalImage.dart';
|
||||||
import 'package:spotube/provider/Downloader.dart';
|
import 'package:spotube/provider/Downloader.dart';
|
||||||
@ -47,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 ListTile(
|
return PlatformListTile(
|
||||||
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 +69,6 @@ class UserDownloads extends HookConsumerWidget {
|
|||||||
height: 30,
|
height: 30,
|
||||||
child: CircularProgressIndicator.adaptive(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
),
|
),
|
||||||
horizontalTitleGap: 5,
|
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
TypeConversionUtils.artists_X_String(
|
TypeConversionUtils.artists_X_String(
|
||||||
track.artists ?? <Artist>[],
|
track.artists ?? <Artist>[],
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import 'package:flutter/material.dart' hide Image;
|
import 'package:flutter/material.dart' hide Image;
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:spotube/components/Library/UserAlbums.dart';
|
import 'package:spotube/components/Library/UserAlbums.dart';
|
||||||
import 'package:spotube/components/Library/UserArtists.dart';
|
import 'package:spotube/components/Library/UserArtists.dart';
|
||||||
import 'package:spotube/components/Library/UserDownloads.dart';
|
import 'package:spotube/components/Library/UserDownloads.dart';
|
||||||
import 'package:spotube/components/Library/UserLocalTracks.dart';
|
import 'package:spotube/components/Library/UserLocalTracks.dart';
|
||||||
import 'package:spotube/components/Library/UserPlaylists.dart';
|
import 'package:spotube/components/Library/UserPlaylists.dart';
|
||||||
import 'package:spotube/components/Shared/AnonymousFallback.dart';
|
import 'package:spotube/components/Shared/AnonymousFallback.dart';
|
||||||
import 'package:spotube/components/Shared/ColoredTabBar.dart';
|
|
||||||
|
|
||||||
class UserLibrary extends ConsumerWidget {
|
class UserLibrary extends ConsumerWidget {
|
||||||
const UserLibrary({Key? key}) : super(key: key);
|
const UserLibrary({Key? key}) : super(key: key);
|
||||||
@ -15,27 +15,30 @@ class UserLibrary extends ConsumerWidget {
|
|||||||
return DefaultTabController(
|
return DefaultTabController(
|
||||||
length: 5,
|
length: 5,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Scaffold(
|
child: PlatformTabView(
|
||||||
appBar: ColoredTabBar(
|
placement: PlatformProperty.all(PlatformTabbarPlacement.top),
|
||||||
color: Theme.of(context).backgroundColor,
|
body: {
|
||||||
child: const TabBar(
|
PlatformTab(
|
||||||
isScrollable: true,
|
label: "Playlist",
|
||||||
tabs: [
|
icon: Container(),
|
||||||
Tab(text: "Playlist"),
|
): const AnonymousFallback(child: UserPlaylists()),
|
||||||
Tab(text: "Downloads"),
|
PlatformTab(
|
||||||
Tab(text: "Local"),
|
label: "Downloads",
|
||||||
Tab(text: "Artists"),
|
icon: Container(),
|
||||||
Tab(text: "Album"),
|
): const UserDownloads(),
|
||||||
],
|
PlatformTab(
|
||||||
),
|
label: "Local",
|
||||||
),
|
icon: Container(),
|
||||||
body: const TabBarView(children: [
|
): const UserLocalTracks(),
|
||||||
AnonymousFallback(child: UserPlaylists()),
|
PlatformTab(
|
||||||
UserDownloads(),
|
label: "Artists",
|
||||||
UserLocalTracks(),
|
icon: Container(),
|
||||||
AnonymousFallback(child: UserArtists()),
|
): const AnonymousFallback(child: UserArtists()),
|
||||||
AnonymousFallback(child: UserAlbums()),
|
PlatformTab(
|
||||||
]),
|
label: "Album",
|
||||||
|
icon: Container(),
|
||||||
|
): const AnonymousFallback(child: UserAlbums()),
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,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/components/LoaderShimmers/ShimmerTrackTile.dart';
|
import 'package:spotube/components/LoaderShimmers/ShimmerTrackTile.dart';
|
||||||
import 'package:spotube/components/Shared/SortTracksDropdown.dart';
|
import 'package:spotube/components/Shared/SortTracksDropdown.dart';
|
||||||
@ -169,13 +170,7 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
ElevatedButton.icon(
|
PlatformFilledButton(
|
||||||
label: const Text("Play"),
|
|
||||||
icon: Icon(
|
|
||||||
isPlaylistPlaying
|
|
||||||
? Icons.stop_rounded
|
|
||||||
: Icons.play_arrow_rounded,
|
|
||||||
),
|
|
||||||
onPressed: trackSnapshot.value != null
|
onPressed: trackSnapshot.value != null
|
||||||
? () {
|
? () {
|
||||||
if (trackSnapshot.value?.isNotEmpty == true) {
|
if (trackSnapshot.value?.isNotEmpty == true) {
|
||||||
@ -187,6 +182,16 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Text("Play"),
|
||||||
|
Icon(
|
||||||
|
isPlaylistPlaying
|
||||||
|
? Icons.stop_rounded
|
||||||
|
: Icons.play_arrow_rounded,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
SortTracksDropdown(
|
SortTracksDropdown(
|
||||||
@ -196,7 +201,7 @@ class UserLocalTracks extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
ElevatedButton(
|
PlatformFilledButton(
|
||||||
child: const Icon(Icons.refresh_rounded),
|
child: const Icon(Icons.refresh_rounded),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref.refresh(localTracksProvider);
|
ref.refresh(localTracksProvider);
|
||||||
|
@ -11,10 +11,11 @@ class ShimmerArtistProfile extends HookWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final shimmerColor =
|
final shimmerColor =
|
||||||
Theme.of(context).extension<ShimmerColorTheme>()!.shimmerColor!;
|
Theme.of(context).extension<ShimmerColorTheme>()?.shimmerColor ??
|
||||||
|
Colors.white;
|
||||||
final shimmerBackgroundColor = Theme.of(context)
|
final shimmerBackgroundColor = Theme.of(context)
|
||||||
.extension<ShimmerColorTheme>()!
|
.extension<ShimmerColorTheme>()
|
||||||
.shimmerBackgroundColor!;
|
?.shimmerBackgroundColor;
|
||||||
|
|
||||||
final avatarWidth = useBreakpointValue(
|
final avatarWidth = useBreakpointValue(
|
||||||
sm: MediaQuery.of(context).size.width * 0.80,
|
sm: MediaQuery.of(context).size.width * 0.80,
|
||||||
|
@ -9,10 +9,12 @@ class ShimmerCategories extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final shimmerColor =
|
final shimmerColor =
|
||||||
Theme.of(context).extension<ShimmerColorTheme>()!.shimmerColor!;
|
Theme.of(context).extension<ShimmerColorTheme>()?.shimmerColor ??
|
||||||
|
Colors.white;
|
||||||
final shimmerBackgroundColor = Theme.of(context)
|
final shimmerBackgroundColor = Theme.of(context)
|
||||||
.extension<ShimmerColorTheme>()!
|
.extension<ShimmerColorTheme>()
|
||||||
.shimmerBackgroundColor!;
|
?.shimmerBackgroundColor ??
|
||||||
|
Colors.grey;
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
@ -12,10 +12,12 @@ class ShimmerLyrics extends HookWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final shimmerColor =
|
final shimmerColor =
|
||||||
Theme.of(context).extension<ShimmerColorTheme>()!.shimmerColor!;
|
Theme.of(context).extension<ShimmerColorTheme>()?.shimmerColor ??
|
||||||
|
Colors.white;
|
||||||
final shimmerBackgroundColor = Theme.of(context)
|
final shimmerBackgroundColor = Theme.of(context)
|
||||||
.extension<ShimmerColorTheme>()!
|
.extension<ShimmerColorTheme>()
|
||||||
.shimmerBackgroundColor!;
|
?.shimmerBackgroundColor ??
|
||||||
|
Colors.grey;
|
||||||
|
|
||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
|
|
||||||
|
@ -9,10 +9,12 @@ class ShimmerPlaybuttonCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final shimmerColor =
|
final shimmerColor =
|
||||||
Theme.of(context).extension<ShimmerColorTheme>()!.shimmerColor!;
|
Theme.of(context).extension<ShimmerColorTheme>()?.shimmerColor ??
|
||||||
|
Colors.white;
|
||||||
final shimmerBackgroundColor = Theme.of(context)
|
final shimmerBackgroundColor = Theme.of(context)
|
||||||
.extension<ShimmerColorTheme>()!
|
.extension<ShimmerColorTheme>()
|
||||||
.shimmerBackgroundColor!;
|
?.shimmerBackgroundColor ??
|
||||||
|
Colors.grey;
|
||||||
|
|
||||||
final card = Stack(
|
final card = Stack(
|
||||||
children: [
|
children: [
|
||||||
|
@ -13,10 +13,12 @@ class ShimmerTrackTile extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final shimmerColor =
|
final shimmerColor =
|
||||||
Theme.of(context).extension<ShimmerColorTheme>()!.shimmerColor!;
|
Theme.of(context).extension<ShimmerColorTheme>()?.shimmerColor ??
|
||||||
|
Colors.white;
|
||||||
final shimmerBackgroundColor = Theme.of(context)
|
final shimmerBackgroundColor = Theme.of(context)
|
||||||
.extension<ShimmerColorTheme>()!
|
.extension<ShimmerColorTheme>()
|
||||||
.shimmerBackgroundColor!;
|
?.shimmerBackgroundColor ??
|
||||||
|
Colors.grey;
|
||||||
|
|
||||||
final single = Container(
|
final single = Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
@ -3,6 +3,8 @@ 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:palette_generator/palette_generator.dart';
|
||||||
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:spotube/components/Lyrics/GeniusLyrics.dart';
|
import 'package:spotube/components/Lyrics/GeniusLyrics.dart';
|
||||||
import 'package:spotube/components/Lyrics/SyncedLyrics.dart';
|
import 'package:spotube/components/Lyrics/SyncedLyrics.dart';
|
||||||
import 'package:spotube/components/Shared/UniversalImage.dart';
|
import 'package:spotube/components/Shared/UniversalImage.dart';
|
||||||
@ -14,6 +16,25 @@ import 'package:spotube/utils/type_conversion_utils.dart';
|
|||||||
class Lyrics extends HookConsumerWidget {
|
class Lyrics extends HookConsumerWidget {
|
||||||
const Lyrics({Key? key}) : super(key: key);
|
const Lyrics({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
Widget buildContainer(Widget child, String albumArt, PaletteColor palette) {
|
||||||
|
return Container(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: UniversalImage.imageProvider(albumArt),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||||
|
child: Container(
|
||||||
|
color: palette.color.withOpacity(.7),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
Playback playback = ref.watch(playbackProvider);
|
Playback playback = ref.watch(playbackProvider);
|
||||||
@ -33,8 +54,22 @@ class Lyrics extends HookConsumerWidget {
|
|||||||
noSetBGColor: true,
|
noSetBGColor: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
return DefaultTabController(
|
return SafeArea(
|
||||||
length: 2,
|
child: PlatformTabView(
|
||||||
|
body: {
|
||||||
|
PlatformTab(
|
||||||
|
label: "Synced Lyrics",
|
||||||
|
icon: Container(),
|
||||||
|
): buildContainer(SyncedLyrics(palette: palette), albumArt, palette),
|
||||||
|
PlatformTab(
|
||||||
|
label: "Lyrics (genius.com)",
|
||||||
|
icon: Container(),
|
||||||
|
): buildContainer(GeniusLyrics(palette: palette), albumArt, palette),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
appBar: const TabBar(
|
appBar: const TabBar(
|
||||||
|
@ -2,6 +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:spotube/provider/SpotifyDI.dart';
|
import 'package:spotube/provider/SpotifyDI.dart';
|
||||||
import 'package:spotube/provider/SpotifyRequests.dart';
|
import 'package:spotube/provider/SpotifyRequests.dart';
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final spotify = ref.watch(spotifyProvider);
|
final spotify = ref.watch(spotifyProvider);
|
||||||
|
|
||||||
return TextButton(
|
return PlatformTextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -26,11 +27,11 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text("Create a Playlist"),
|
title: const Text("Create a Playlist"),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
PlatformTextButton(
|
||||||
child: const Text("Cancel"),
|
child: const Text("Cancel"),
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
PlatformFilledButton(
|
||||||
child: const Text("Create"),
|
child: const Text("Create"),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (playlistName.text.isEmpty) return;
|
if (playlistName.text.isEmpty) return;
|
||||||
@ -58,19 +59,15 @@ class PlaylistCreateDialog extends HookConsumerWidget {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
children: [
|
children: [
|
||||||
TextField(
|
PlatformTextField(
|
||||||
controller: playlistName,
|
controller: playlistName,
|
||||||
decoration: const InputDecoration(
|
placeholder: "Name of the playlist",
|
||||||
hintText: "Name of the playlist",
|
label: "Playlist Name",
|
||||||
label: Text("Playlist Name"),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TextField(
|
PlatformTextField(
|
||||||
controller: description,
|
controller: description,
|
||||||
decoration: const InputDecoration(
|
placeholder: "Description...",
|
||||||
hintText: "Description...",
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
),
|
),
|
||||||
|
@ -3,6 +3,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/AlbumCard.dart';
|
import 'package:spotube/components/Album/AlbumCard.dart';
|
||||||
import 'package:spotube/components/Artist/ArtistCard.dart';
|
import 'package:spotube/components/Artist/ArtistCard.dart';
|
||||||
@ -85,23 +86,15 @@ class Search extends HookConsumerWidget {
|
|||||||
vertical: 10,
|
vertical: 10,
|
||||||
),
|
),
|
||||||
color: Theme.of(context).backgroundColor,
|
color: Theme.of(context).backgroundColor,
|
||||||
child: TextField(
|
child: PlatformTextField(
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
ref.read(searchTermStateProvider.notifier).state = value;
|
ref.read(searchTermStateProvider.notifier).state = value;
|
||||||
},
|
},
|
||||||
decoration: InputDecoration(
|
suffix: PlatformFilledButton(
|
||||||
isDense: true,
|
|
||||||
suffix: ElevatedButton(
|
|
||||||
onPressed: onSearch,
|
onPressed: onSearch,
|
||||||
child: const Icon(Icons.search_rounded),
|
child: const Icon(Icons.search_rounded),
|
||||||
),
|
),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
placeholder: "Search...",
|
||||||
horizontal: 10,
|
|
||||||
vertical: 7,
|
|
||||||
),
|
|
||||||
hintStyle: const TextStyle(height: 2),
|
|
||||||
hintText: "Search...",
|
|
||||||
),
|
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
onSearch();
|
onSearch();
|
||||||
},
|
},
|
||||||
|
@ -1,5 +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/Shared/Hyperlink.dart';
|
import 'package:spotube/components/Shared/Hyperlink.dart';
|
||||||
import 'package:spotube/hooks/usePackageInfo.dart';
|
import 'package:spotube/hooks/usePackageInfo.dart';
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ class About extends HookWidget {
|
|||||||
version: "2.5.0",
|
version: "2.5.0",
|
||||||
);
|
);
|
||||||
|
|
||||||
return ListTile(
|
return PlatformListTile(
|
||||||
leading: const Icon(Icons.info_outline_rounded),
|
leading: const Icon(Icons.info_outline_rounded),
|
||||||
title: const Text("About Spotube"),
|
title: const Text("About Spotube"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -4,6 +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/components/Settings/About.dart';
|
import 'package:spotube/components/Settings/About.dart';
|
||||||
import 'package:spotube/components/Settings/ColorSchemePickerDialog.dart';
|
import 'package:spotube/components/Settings/ColorSchemePickerDialog.dart';
|
||||||
import 'package:spotube/components/Shared/AdaptiveListTile.dart';
|
import 'package:spotube/components/Shared/AdaptiveListTile.dart';
|
||||||
@ -88,7 +89,7 @@ class Settings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: (context, update) => ElevatedButton(
|
trailing: (context, update) => PlatformFilledButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
GoRouter.of(context).push("/login");
|
GoRouter.of(context).push("/login");
|
||||||
},
|
},
|
||||||
@ -105,7 +106,7 @@ class Settings extends HookConsumerWidget {
|
|||||||
if (auth.isLoggedIn)
|
if (auth.isLoggedIn)
|
||||||
Builder(builder: (context) {
|
Builder(builder: (context) {
|
||||||
Auth auth = ref.watch(authProvider);
|
Auth auth = ref.watch(authProvider);
|
||||||
return ListTile(
|
return PlatformListTile(
|
||||||
leading: const Icon(Icons.logout_rounded),
|
leading: const Icon(Icons.logout_rounded),
|
||||||
title: const SizedBox(
|
title: const SizedBox(
|
||||||
height: 50,
|
height: 50,
|
||||||
@ -118,7 +119,7 @@ class Settings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: ElevatedButton(
|
trailing: PlatformFilledButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
MaterialStateProperty.all(Colors.red),
|
MaterialStateProperty.all(Colors.red),
|
||||||
@ -144,24 +145,25 @@ class Settings extends HookConsumerWidget {
|
|||||||
subtitle: const Text(
|
subtitle: const Text(
|
||||||
"Override responsive layout mode settings",
|
"Override responsive layout mode settings",
|
||||||
),
|
),
|
||||||
trailing: (context, update) => DropdownButton<LayoutMode>(
|
trailing: (context, update) =>
|
||||||
|
PlatformDropDownMenu<LayoutMode>(
|
||||||
value: preferences.layoutMode,
|
value: preferences.layoutMode,
|
||||||
items: const [
|
items: [
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: LayoutMode.adaptive,
|
value: LayoutMode.adaptive,
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Adaptive",
|
"Adaptive",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: LayoutMode.compact,
|
value: LayoutMode.compact,
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Compact",
|
"Compact",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: LayoutMode.extended,
|
value: LayoutMode.extended,
|
||||||
child: Text("Extended"),
|
child: const Text("Extended"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -175,24 +177,25 @@ class Settings extends HookConsumerWidget {
|
|||||||
AdaptiveListTile(
|
AdaptiveListTile(
|
||||||
leading: const Icon(Icons.dark_mode_outlined),
|
leading: const Icon(Icons.dark_mode_outlined),
|
||||||
title: const Text("Theme"),
|
title: const Text("Theme"),
|
||||||
trailing: (context, update) => DropdownButton<ThemeMode>(
|
trailing: (context, update) =>
|
||||||
|
PlatformDropDownMenu<ThemeMode>(
|
||||||
value: preferences.themeMode,
|
value: preferences.themeMode,
|
||||||
items: const [
|
items: [
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: ThemeMode.dark,
|
value: ThemeMode.dark,
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Dark",
|
"Dark",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: ThemeMode.light,
|
value: ThemeMode.light,
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Light",
|
"Light",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: ThemeMode.system,
|
value: ThemeMode.system,
|
||||||
child: Text("System"),
|
child: const Text("System"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -203,7 +206,7 @@ class Settings extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
PlatformListTile(
|
||||||
leading: const Icon(Icons.palette_outlined),
|
leading: const Icon(Icons.palette_outlined),
|
||||||
title: const Text("Accent Color Scheme"),
|
title: const Text("Accent Color Scheme"),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
@ -217,7 +220,7 @@ class Settings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
onTap: pickColorScheme(ColorSchemeType.accent),
|
onTap: pickColorScheme(ColorSchemeType.accent),
|
||||||
),
|
),
|
||||||
ListTile(
|
PlatformListTile(
|
||||||
leading: const Icon(Icons.format_color_fill_rounded),
|
leading: const Icon(Icons.format_color_fill_rounded),
|
||||||
title: const Text("Background Color Scheme"),
|
title: const Text("Background Color Scheme"),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
@ -231,11 +234,10 @@ class Settings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
onTap: pickColorScheme(ColorSchemeType.background),
|
onTap: pickColorScheme(ColorSchemeType.background),
|
||||||
),
|
),
|
||||||
ListTile(
|
PlatformListTile(
|
||||||
leading: const Icon(Icons.album_rounded),
|
leading: const Icon(Icons.album_rounded),
|
||||||
title: const Text("Rotating Album Art"),
|
title: const Text("Rotating Album Art"),
|
||||||
trailing: Switch.adaptive(
|
trailing: PlatformSwitch(
|
||||||
activeColor: Theme.of(context).primaryColor,
|
|
||||||
value: preferences.rotatingAlbumArt,
|
value: preferences.rotatingAlbumArt,
|
||||||
onChanged: (state) {
|
onChanged: (state) {
|
||||||
preferences.setRotatingAlbumArt(state);
|
preferences.setRotatingAlbumArt(state);
|
||||||
@ -251,18 +253,18 @@ class Settings extends HookConsumerWidget {
|
|||||||
leading: const Icon(Icons.multitrack_audio_rounded),
|
leading: const Icon(Icons.multitrack_audio_rounded),
|
||||||
title: const Text("Audio Quality"),
|
title: const Text("Audio Quality"),
|
||||||
trailing: (context, update) =>
|
trailing: (context, update) =>
|
||||||
DropdownButton<AudioQuality>(
|
PlatformDropDownMenu<AudioQuality>(
|
||||||
value: preferences.audioQuality,
|
value: preferences.audioQuality,
|
||||||
items: const [
|
items: [
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: AudioQuality.high,
|
value: AudioQuality.high,
|
||||||
child: Text(
|
child: const Text(
|
||||||
"High",
|
"High",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: AudioQuality.low,
|
value: AudioQuality.low,
|
||||||
child: Text("Low"),
|
child: const Text("Low"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -274,7 +276,7 @@ class Settings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (kIsMobile)
|
if (kIsMobile)
|
||||||
ListTile(
|
PlatformListTile(
|
||||||
leading: const Icon(Icons.download_for_offline_rounded),
|
leading: const Icon(Icons.download_for_offline_rounded),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
"Pre download and play",
|
"Pre download and play",
|
||||||
@ -282,21 +284,19 @@ class Settings extends HookConsumerWidget {
|
|||||||
subtitle: const Text(
|
subtitle: const Text(
|
||||||
"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: Switch.adaptive(
|
trailing: PlatformSwitch(
|
||||||
activeColor: Theme.of(context).primaryColor,
|
|
||||||
value: preferences.androidBytesPlay,
|
value: preferences.androidBytesPlay,
|
||||||
onChanged: (state) {
|
onChanged: (state) {
|
||||||
preferences.setAndroidBytesPlay(state);
|
preferences.setAndroidBytesPlay(state);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
PlatformListTile(
|
||||||
leading: const Icon(Icons.fast_forward_rounded),
|
leading: const Icon(Icons.fast_forward_rounded),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
"Skip non-music segments (SponsorBlock)",
|
"Skip non-music segments (SponsorBlock)",
|
||||||
),
|
),
|
||||||
trailing: Switch.adaptive(
|
trailing: PlatformSwitch(
|
||||||
activeColor: Theme.of(context).primaryColor,
|
|
||||||
value: preferences.skipSponsorSegments,
|
value: preferences.skipSponsorSegments,
|
||||||
onChanged: (state) {
|
onChanged: (state) {
|
||||||
preferences.setSkipSponsorSegments(state);
|
preferences.setSkipSponsorSegments(state);
|
||||||
@ -320,12 +320,11 @@ class Settings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
trailing: (context, update) => ConstrainedBox(
|
trailing: (context, update) => ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 250),
|
constraints: const BoxConstraints(maxWidth: 250),
|
||||||
child: DropdownButton(
|
child: PlatformDropDownMenu(
|
||||||
isExpanded: true,
|
|
||||||
value: preferences.recommendationMarket,
|
value: preferences.recommendationMarket,
|
||||||
items: spotifyMarkets
|
items: spotifyMarkets
|
||||||
.map(
|
.map(
|
||||||
(country) => (DropdownMenuItem(
|
(country) => (PlatformDropDownMenuItem(
|
||||||
value: country.first,
|
value: country.first,
|
||||||
child: Text(country.last),
|
child: Text(country.last),
|
||||||
)),
|
)),
|
||||||
@ -358,11 +357,9 @@ class Settings extends HookConsumerWidget {
|
|||||||
breakOn: Breakpoints.lg,
|
breakOn: Breakpoints.lg,
|
||||||
trailing: (context, update) => ConstrainedBox(
|
trailing: (context, update) => ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 450),
|
constraints: const BoxConstraints(maxWidth: 450),
|
||||||
child: TextField(
|
child: PlatformTextField(
|
||||||
controller: ytSearchFormatController,
|
controller: ytSearchFormatController,
|
||||||
decoration: InputDecoration(
|
suffix: PlatformFilledButton(
|
||||||
isDense: true,
|
|
||||||
suffix: ElevatedButton(
|
|
||||||
child: const Icon(Icons.save_rounded),
|
child: const Icon(Icons.save_rounded),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
preferences.setYtSearchFormat(
|
preferences.setYtSearchFormat(
|
||||||
@ -370,7 +367,6 @@ class Settings extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
preferences.setYtSearchFormat(value);
|
preferences.setYtSearchFormat(value);
|
||||||
update?.call(() {});
|
update?.call(() {});
|
||||||
@ -392,24 +388,24 @@ class Settings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: (context, update) =>
|
trailing: (context, update) =>
|
||||||
DropdownButton<SpotubeTrackMatchAlgorithm>(
|
PlatformDropDownMenu<SpotubeTrackMatchAlgorithm>(
|
||||||
value: preferences.trackMatchAlgorithm,
|
value: preferences.trackMatchAlgorithm,
|
||||||
items: const [
|
items: [
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: SpotubeTrackMatchAlgorithm.authenticPopular,
|
value: SpotubeTrackMatchAlgorithm.authenticPopular,
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Popular from Author",
|
"Popular from Author",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: SpotubeTrackMatchAlgorithm.popular,
|
value: SpotubeTrackMatchAlgorithm.popular,
|
||||||
child: Text(
|
child: const Text(
|
||||||
"Accurately Popular",
|
"Accurately Popular",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
PlatformDropDownMenuItem(
|
||||||
value: SpotubeTrackMatchAlgorithm.youtube,
|
value: SpotubeTrackMatchAlgorithm.youtube,
|
||||||
child: Text("YouTube's Top choice"),
|
child: const Text("YouTube's Top choice"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -425,21 +421,20 @@ class Settings extends HookConsumerWidget {
|
|||||||
style:
|
style:
|
||||||
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||||
),
|
),
|
||||||
ListTile(
|
PlatformListTile(
|
||||||
leading: const Icon(Icons.file_download_outlined),
|
leading: const Icon(Icons.file_download_outlined),
|
||||||
title: const Text("Download Location"),
|
title: const Text("Download Location"),
|
||||||
subtitle: Text(preferences.downloadLocation),
|
subtitle: Text(preferences.downloadLocation),
|
||||||
trailing: ElevatedButton(
|
trailing: PlatformFilledButton(
|
||||||
onPressed: pickDownloadLocation,
|
onPressed: pickDownloadLocation,
|
||||||
child: const Icon(Icons.folder_rounded),
|
child: const Icon(Icons.folder_rounded),
|
||||||
),
|
),
|
||||||
onTap: pickDownloadLocation,
|
onTap: pickDownloadLocation,
|
||||||
),
|
),
|
||||||
ListTile(
|
PlatformListTile(
|
||||||
leading: const Icon(Icons.lyrics_rounded),
|
leading: const Icon(Icons.lyrics_rounded),
|
||||||
title: const Text("Download lyrics along with the Track"),
|
title: const Text("Download lyrics along with the Track"),
|
||||||
trailing: Switch.adaptive(
|
trailing: PlatformSwitch(
|
||||||
activeColor: Theme.of(context).primaryColor,
|
|
||||||
value: preferences.saveTrackLyrics,
|
value: preferences.saveTrackLyrics,
|
||||||
onChanged: (state) {
|
onChanged: (state) {
|
||||||
preferences.setSaveTrackLyrics(state);
|
preferences.setSaveTrackLyrics(state);
|
||||||
@ -487,11 +482,10 @@ class Settings extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
PlatformListTile(
|
||||||
leading: const Icon(Icons.update_rounded),
|
leading: const Icon(Icons.update_rounded),
|
||||||
title: const Text("Check for Update"),
|
title: const Text("Check for Update"),
|
||||||
trailing: Switch.adaptive(
|
trailing: PlatformSwitch(
|
||||||
activeColor: Theme.of(context).primaryColor,
|
|
||||||
value: preferences.checkUpdate,
|
value: preferences.checkUpdate,
|
||||||
onChanged: (checked) =>
|
onChanged: (checked) =>
|
||||||
preferences.setCheckUpdate(checked),
|
preferences.setCheckUpdate(checked),
|
||||||
|
@ -1,5 +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/hooks/useBreakpoints.dart';
|
import 'package:spotube/hooks/useBreakpoints.dart';
|
||||||
|
|
||||||
class AdaptiveListTile extends HookWidget {
|
class AdaptiveListTile extends HookWidget {
|
||||||
@ -24,7 +25,7 @@ class AdaptiveListTile extends HookWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
|
|
||||||
return ListTile(
|
return PlatformListTile(
|
||||||
title: title,
|
title: title,
|
||||||
subtitle: subtitle,
|
subtitle: subtitle,
|
||||||
trailing:
|
trailing:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:spotube/components/Shared/HoverBuilder.dart';
|
import 'package:spotube/components/Shared/HoverBuilder.dart';
|
||||||
import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
|
import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
|
||||||
import 'package:spotube/components/Shared/UniversalImage.dart';
|
import 'package:spotube/components/Shared/UniversalImage.dart';
|
||||||
@ -67,7 +68,7 @@ class PlaybuttonCard extends StatelessWidget {
|
|||||||
bottom: 10,
|
bottom: 10,
|
||||||
end: 5,
|
end: 5,
|
||||||
child: Builder(builder: (context) {
|
child: Builder(builder: (context) {
|
||||||
return ElevatedButton(
|
return PlatformFilledButton(
|
||||||
onPressed: onPlaybuttonPressed,
|
onPressed: onPlaybuttonPressed,
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
shape: MaterialStateProperty.all(
|
shape: MaterialStateProperty.all(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:platform_ui/platform_ui.dart';
|
||||||
import 'package:spotube/components/Library/UserLocalTracks.dart';
|
import 'package:spotube/components/Library/UserLocalTracks.dart';
|
||||||
|
|
||||||
class SortTracksDropdown extends StatelessWidget {
|
class SortTracksDropdown extends StatelessWidget {
|
||||||
@ -12,43 +13,41 @@ class SortTracksDropdown extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopupMenuButton<SortBy>(
|
return PlatformPopupMenuButton<SortBy>(
|
||||||
itemBuilder: (context) {
|
items: [
|
||||||
return [
|
PlatformPopupMenuItem(
|
||||||
PopupMenuItem(
|
|
||||||
value: SortBy.none,
|
value: SortBy.none,
|
||||||
enabled: value != SortBy.none,
|
enabled: value != SortBy.none,
|
||||||
child: const Text("None"),
|
child: const Text("None"),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PlatformPopupMenuItem(
|
||||||
value: SortBy.ascending,
|
value: SortBy.ascending,
|
||||||
enabled: value != SortBy.ascending,
|
enabled: value != SortBy.ascending,
|
||||||
child: const Text("Sort by A-Z"),
|
child: const Text("Sort by A-Z"),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PlatformPopupMenuItem(
|
||||||
value: SortBy.descending,
|
value: SortBy.descending,
|
||||||
enabled: value != SortBy.descending,
|
enabled: value != SortBy.descending,
|
||||||
child: const Text("Sort by Z-A"),
|
child: const Text("Sort by Z-A"),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PlatformPopupMenuItem(
|
||||||
value: SortBy.dateAdded,
|
value: SortBy.dateAdded,
|
||||||
enabled: value != SortBy.dateAdded,
|
enabled: value != SortBy.dateAdded,
|
||||||
child: const Text("Sort by Date"),
|
child: const Text("Sort by Date"),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PlatformPopupMenuItem(
|
||||||
value: SortBy.artist,
|
value: SortBy.artist,
|
||||||
enabled: value != SortBy.artist,
|
enabled: value != SortBy.artist,
|
||||||
child: const Text("Sort by Artist"),
|
child: const Text("Sort by Artist"),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PlatformPopupMenuItem(
|
||||||
value: SortBy.album,
|
value: SortBy.album,
|
||||||
enabled: value != SortBy.album,
|
enabled: value != SortBy.album,
|
||||||
child: const Text("Sort by Album"),
|
child: const Text("Sort by Album"),
|
||||||
),
|
),
|
||||||
];
|
],
|
||||||
},
|
|
||||||
onSelected: onChanged,
|
onSelected: onChanged,
|
||||||
icon: const Icon(Icons.sort_rounded),
|
child: const Icon(Icons.sort_rounded),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
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:platform_ui/platform_ui.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:spotube/components/Shared/ReplaceDownloadedFileDialog.dart';
|
import 'package:spotube/components/Shared/ReplaceDownloadedFileDialog.dart';
|
||||||
import 'package:spotube/entities/CacheTrack.dart';
|
import 'package:spotube/entities/CacheTrack.dart';
|
||||||
@ -198,57 +199,66 @@ class SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return MaterialApp.router(
|
platform = TargetPlatform.macOS;
|
||||||
routerConfig: router,
|
|
||||||
|
return PlatformApp.router(
|
||||||
|
routeInformationParser: router.routeInformationParser,
|
||||||
|
routerDelegate: router.routerDelegate,
|
||||||
|
routeInformationProvider: router.routeInformationProvider,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
title: 'Spotube',
|
title: 'Spotube',
|
||||||
theme: lightTheme(
|
androidTheme: lightTheme(
|
||||||
accentMaterialColor: accentMaterialColor,
|
accentMaterialColor: accentMaterialColor,
|
||||||
backgroundMaterialColor: backgroundMaterialColor,
|
backgroundMaterialColor: backgroundMaterialColor,
|
||||||
),
|
),
|
||||||
darkTheme: darkTheme(
|
androidDarkTheme: darkTheme(
|
||||||
accentMaterialColor: accentMaterialColor,
|
accentMaterialColor: accentMaterialColor,
|
||||||
backgroundMaterialColor: backgroundMaterialColor,
|
backgroundMaterialColor: backgroundMaterialColor,
|
||||||
),
|
),
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
shortcuts: {
|
shortcuts: PlatformProperty.all({
|
||||||
...WidgetsApp.defaultShortcuts,
|
...WidgetsApp.defaultShortcuts.map((key, value) {
|
||||||
const SingleActivator(LogicalKeyboardKey.space): PlayPauseIntent(ref),
|
return MapEntry(
|
||||||
const SingleActivator(LogicalKeyboardKey.comma, control: true):
|
LogicalKeySet.fromSet(key.triggers?.toSet() ?? {}),
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
LogicalKeySet(LogicalKeyboardKey.space): PlayPauseIntent(ref),
|
||||||
|
LogicalKeySet(LogicalKeyboardKey.comma, LogicalKeyboardKey.control):
|
||||||
NavigationIntent(router, "/settings"),
|
NavigationIntent(router, "/settings"),
|
||||||
const SingleActivator(
|
LogicalKeySet(
|
||||||
LogicalKeyboardKey.keyB,
|
LogicalKeyboardKey.keyB,
|
||||||
control: true,
|
LogicalKeyboardKey.control,
|
||||||
shift: true,
|
LogicalKeyboardKey.shift,
|
||||||
): HomeTabIntent(ref, tab: HomeTabs.browse),
|
): HomeTabIntent(ref, tab: HomeTabs.browse),
|
||||||
const SingleActivator(
|
LogicalKeySet(
|
||||||
LogicalKeyboardKey.keyS,
|
LogicalKeyboardKey.keyS,
|
||||||
control: true,
|
LogicalKeyboardKey.control,
|
||||||
shift: true,
|
LogicalKeyboardKey.shift,
|
||||||
): HomeTabIntent(ref, tab: HomeTabs.search),
|
): HomeTabIntent(ref, tab: HomeTabs.search),
|
||||||
const SingleActivator(
|
LogicalKeySet(
|
||||||
LogicalKeyboardKey.keyL,
|
LogicalKeyboardKey.keyL,
|
||||||
control: true,
|
LogicalKeyboardKey.control,
|
||||||
shift: true,
|
LogicalKeyboardKey.shift,
|
||||||
): HomeTabIntent(ref, tab: HomeTabs.library),
|
): HomeTabIntent(ref, tab: HomeTabs.library),
|
||||||
const SingleActivator(
|
LogicalKeySet(
|
||||||
LogicalKeyboardKey.keyY,
|
LogicalKeyboardKey.keyY,
|
||||||
control: true,
|
LogicalKeyboardKey.control,
|
||||||
shift: true,
|
LogicalKeyboardKey.shift,
|
||||||
): HomeTabIntent(ref, tab: HomeTabs.lyrics),
|
): HomeTabIntent(ref, tab: HomeTabs.lyrics),
|
||||||
const SingleActivator(
|
LogicalKeySet(
|
||||||
LogicalKeyboardKey.keyW,
|
LogicalKeyboardKey.keyW,
|
||||||
control: true,
|
LogicalKeyboardKey.control,
|
||||||
shift: true,
|
LogicalKeyboardKey.shift,
|
||||||
): CloseAppIntent(),
|
): CloseAppIntent(),
|
||||||
},
|
}),
|
||||||
actions: {
|
actions: PlatformProperty.all({
|
||||||
...WidgetsApp.defaultActions,
|
...WidgetsApp.defaultActions,
|
||||||
PlayPauseIntent: PlayPauseAction(),
|
PlayPauseIntent: PlayPauseAction(),
|
||||||
NavigationIntent: NavigationAction(),
|
NavigationIntent: NavigationAction(),
|
||||||
HomeTabIntent: HomeTabAction(),
|
HomeTabIntent: HomeTabAction(),
|
||||||
CloseAppIntent: CloseAppAction(),
|
CloseAppIntent: CloseAppAction(),
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import audio_session
|
|||||||
import audioplayers_darwin
|
import audioplayers_darwin
|
||||||
import bitsdojo_window_macos
|
import bitsdojo_window_macos
|
||||||
import connectivity_plus_macos
|
import connectivity_plus_macos
|
||||||
|
import macos_ui
|
||||||
import metadata_god
|
import metadata_god
|
||||||
import package_info_plus_macos
|
import package_info_plus_macos
|
||||||
import path_provider_macos
|
import path_provider_macos
|
||||||
@ -23,6 +24,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||||
BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin"))
|
BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin"))
|
||||||
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
|
||||||
|
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"))
|
||||||
|
47
pubspec.lock
47
pubspec.lock
@ -493,6 +493,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.1"
|
version: "0.3.1"
|
||||||
|
fluent_ui:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fluent_ui
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.3"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -561,6 +568,11 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
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_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -690,6 +702,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.2.0"
|
||||||
|
intl:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.0"
|
||||||
introduction_screen:
|
introduction_screen:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -739,6 +758,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
macos_ui:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: macos_ui
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.7.5"
|
||||||
marquee:
|
marquee:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -972,6 +998,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
|
platform_ui:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "../platform_ui"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.0.1"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1021,6 +1054,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0+1"
|
version: "3.1.0+1"
|
||||||
|
recase:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: recase
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.0"
|
||||||
riverpod:
|
riverpod:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1035,6 +1075,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.27.3"
|
version: "0.27.3"
|
||||||
|
scroll_pos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: scroll_pos
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.0"
|
||||||
scroll_to_index:
|
scroll_to_index:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -62,6 +62,8 @@ dependencies:
|
|||||||
flutter_inappwebview: ^5.4.3+7
|
flutter_inappwebview: ^5.4.3+7
|
||||||
tuple: ^2.0.1
|
tuple: ^2.0.1
|
||||||
uuid: ^3.0.6
|
uuid: ^3.0.6
|
||||||
|
platform_ui:
|
||||||
|
path: ../platform_ui
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user