feat: initial platform_ui integration

What's changed:
- Sidebar
- Settings
- UserLibrary (root)
- Search (search field)
This commit is contained in:
Kingkor Roy Tirtho 2022-10-29 14:23:17 +06:00
parent 4b21cc8299
commit 9eee573ce9
22 changed files with 452 additions and 351 deletions

View File

@ -84,17 +84,13 @@ class Shell extends HookConsumerWidget {
) )
: null, : null,
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
body: Row( body: Sidebar(
children: [ selectedIndex: index.value,
Sidebar( onSelectedIndexChanged: (i) {
selectedIndex: index.value, index.value = i;
onSelectedIndexChanged: (selectedIndex) { GoRouter.of(context).go(_path[index.value]!);
index.value = selectedIndex; },
GoRouter.of(context).go(_path[selectedIndex]!); child: child,
},
),
Expanded(child: child),
],
), ),
extendBody: true, extendBody: true,
bottomNavigationBar: Column( bottomNavigationBar: Column(

View File

@ -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,138 +150,120 @@ 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,
),
),
animationType: BadgeAnimationType.fade,
child: icon,
)
: icon,
label: Text(
e.title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
);
},
).toList(),
selectedIndex: selectedIndex,
onDestinationSelected: onSelectedIndexChanged,
extended: extended.value,
),
),
SizedBox(
width: extended.value ? 256 : 80,
child: HookBuilder(
builder: (context) {
final me = useQuery(
job: currentUserQueryJob,
externalData: ref.watch(spotifyProvider),
);
final data = me.data;
final avatarImg = TypeConversionUtils.image_X_UrlString(
data?.images,
index: (data?.images?.length ?? 1) - 1,
placeholder: ImagePlaceholder.artist,
);
useEffect(() {
if (auth.isLoggedIn && !me.hasData) {
me.setExternalData(ref.read(spotifyProvider));
me.refetch();
}
return;
}, [auth.isLoggedIn, me.hasData]);
if (extended.value) {
return Padding(
padding: const EdgeInsets.all(16).copyWith(left: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (auth.isLoggedIn && data == null)
const Center(
child: CircularProgressIndicator(),
)
else if (data != null)
Flexible(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
CircleAvatar(
backgroundImage:
UniversalImage.imageProvider(
avatarImg),
onBackgroundImageError:
(exception, stackTrace) =>
Image.asset(
"assets/user-placeholder.png",
height: 16,
width: 16,
),
),
const SizedBox(
width: 10,
),
Flexible(
child: Text(
data.displayName ?? "Guest",
maxLines: 1,
softWrap: false,
overflow: TextOverflow.fade,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
],
),
),
IconButton(
icon: const Icon(Icons.settings_outlined),
onPressed: () => goToSettings(context)),
],
));
} else {
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () => goToSettings(context),
child: CircleAvatar(
backgroundImage:
UniversalImage.imageProvider(avatarImg),
onBackgroundImageError: (exception, stackTrace) =>
Image.asset(
"assets/user-placeholder.png",
height: 16,
width: 16,
),
),
),
);
}
},
),
)
], ],
), ),
windowsFooterItems: [
FluentUI.PaneItemAction(
icon: const FluentUI.Icon(FluentUI.FluentIcons.settings),
onTap: () => goToSettings(context),
),
],
footer: SidebarFooter(extended: extended.value),
),
);
}
}
class SidebarFooter extends HookConsumerWidget {
final bool extended;
const SidebarFooter({
Key? key,
required this.extended,
}) : super(key: key);
@override
Widget build(BuildContext context, ref) {
final auth = ref.watch(authProvider);
return SizedBox(
width: extended ? 256 : 80,
child: HookBuilder(
builder: (context) {
final me = useQuery(
job: currentUserQueryJob,
externalData: ref.watch(spotifyProvider),
);
final data = me.data;
final avatarImg = TypeConversionUtils.image_X_UrlString(
data?.images,
index: (data?.images?.length ?? 1) - 1,
placeholder: ImagePlaceholder.artist,
);
useEffect(() {
if (auth.isLoggedIn && !me.hasData) {
me.setExternalData(ref.read(spotifyProvider));
me.refetch();
}
return;
}, [auth.isLoggedIn, me.hasData]);
if (extended) {
return Padding(
padding: const EdgeInsets.all(16).copyWith(left: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (auth.isLoggedIn && data == null)
const Center(
child: CircularProgressIndicator(),
)
else if (data != null)
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CircleAvatar(
backgroundImage:
UniversalImage.imageProvider(avatarImg),
onBackgroundImageError: (exception, stackTrace) =>
Image.asset(
"assets/user-placeholder.png",
height: 16,
width: 16,
),
),
const SizedBox(
width: 10,
),
Flexible(
child: Text(
data.displayName ?? "Guest",
maxLines: 1,
softWrap: false,
overflow: TextOverflow.fade,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
],
),
),
IconButton(
icon: const Icon(Icons.settings_outlined),
onPressed: () => Sidebar.goToSettings(context)),
],
));
} else {
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () => Sidebar.goToSettings(context),
child: CircleAvatar(
backgroundImage: UniversalImage.imageProvider(avatarImg),
onBackgroundImageError: (exception, stackTrace) =>
Image.asset(
"assets/user-placeholder.png",
height: 16,
width: 16,
),
),
),
);
}
},
), ),
); );
} }

View File

@ -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>[],

View File

@ -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()),
},
), ),
), ),
); );

View File

@ -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);

View File

@ -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,

View File

@ -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),

View File

@ -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();

View File

@ -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: [

View File

@ -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),

View File

@ -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(

View File

@ -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,
), ),

View File

@ -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, onPressed: onSearch,
suffix: ElevatedButton( child: const Icon(Icons.search_rounded),
onPressed: onSearch,
child: const Icon(Icons.search_rounded),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 7,
),
hintStyle: const TextStyle(height: 2),
hintText: "Search...",
), ),
placeholder: "Search...",
onSubmitted: (value) { onSubmitted: (value) {
onSearch(); onSearch();
}, },

View File

@ -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: () {

View File

@ -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,18 +357,15 @@ 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, child: const Icon(Icons.save_rounded),
suffix: ElevatedButton( onPressed: () {
child: const Icon(Icons.save_rounded), preferences.setYtSearchFormat(
onPressed: () { ytSearchFormatController.value.text,
preferences.setYtSearchFormat( );
ytSearchFormatController.value.text, },
);
},
),
), ),
onSubmitted: (value) { onSubmitted: (value) {
preferences.setYtSearchFormat(value); preferences.setYtSearchFormat(value);
@ -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),

View File

@ -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:

View File

@ -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(

View File

@ -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"), ),
), PlatformPopupMenuItem(
PopupMenuItem( value: SortBy.ascending,
value: SortBy.ascending, enabled: value != SortBy.ascending,
enabled: value != SortBy.ascending, child: const Text("Sort by A-Z"),
child: const Text("Sort by A-Z"), ),
), PlatformPopupMenuItem(
PopupMenuItem( value: SortBy.descending,
value: SortBy.descending, enabled: value != SortBy.descending,
enabled: value != SortBy.descending, child: const Text("Sort by Z-A"),
child: const Text("Sort by Z-A"), ),
), PlatformPopupMenuItem(
PopupMenuItem( value: SortBy.dateAdded,
value: SortBy.dateAdded, enabled: value != SortBy.dateAdded,
enabled: value != SortBy.dateAdded, child: const Text("Sort by Date"),
child: const Text("Sort by Date"), ),
), PlatformPopupMenuItem(
PopupMenuItem( value: SortBy.artist,
value: SortBy.artist, enabled: value != SortBy.artist,
enabled: value != SortBy.artist, child: const Text("Sort by Artist"),
child: const Text("Sort by Artist"), ),
), PlatformPopupMenuItem(
PopupMenuItem( value: SortBy.album,
value: SortBy.album, enabled: value != SortBy.album,
enabled: value != SortBy.album, child: const Text("Sort by Album"),
child: const Text("Sort by Album"), ),
), ],
];
},
onSelected: onChanged, onSelected: onChanged,
icon: const Icon(Icons.sort_rounded), child: const Icon(Icons.sort_rounded),
); );
} }
} }

View File

@ -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(),
}, }),
); );
} }
} }

View File

@ -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"))

View File

@ -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:

View File

@ -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: