diff --git a/assets/spotube-logo.ico b/assets/spotube-logo.ico new file mode 100644 index 000000000..849063088 Binary files /dev/null and b/assets/spotube-logo.ico differ diff --git a/lib/collections/spotube_icons.dart b/lib/collections/spotube_icons.dart index 3d2594918..697ae2072 100644 --- a/lib/collections/spotube_icons.dart +++ b/lib/collections/spotube_icons.dart @@ -67,4 +67,5 @@ abstract class SpotubeIcons { static const genres = FeatherIcons.music; static const zoomIn = FeatherIcons.zoomIn; static const zoomOut = FeatherIcons.zoomOut; + static const tray = FeatherIcons.chevronDown; } diff --git a/lib/components/shared/page_window_title_bar.dart b/lib/components/shared/page_window_title_bar.dart index 652497849..fb2ff1ead 100644 --- a/lib/components/shared/page_window_title_bar.dart +++ b/lib/components/shared/page_window_title_bar.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:spotube/provider/user_preferences_provider.dart'; import 'package:spotube/utils/platform.dart'; import 'package:titlebar_buttons/titlebar_buttons.dart'; import 'package:window_manager/window_manager.dart'; @@ -85,7 +87,7 @@ class _PageWindowTitleBarState extends State { } } -class WindowTitleBarButtons extends HookWidget { +class WindowTitleBarButtons extends HookConsumerWidget { final Color? foregroundColor; const WindowTitleBarButtons({ Key? key, @@ -93,10 +95,20 @@ class WindowTitleBarButtons extends HookWidget { }) : super(key: key); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, ref) { + final closeBehavior = + ref.watch(userPreferencesProvider.select((s) => s.closeBehavior)); final isMaximized = useState(null); const type = ThemeType.auto; + void onClose() { + if (closeBehavior == CloseBehavior.close) { + windowManager.close(); + } else { + windowManager.hide(); + } + } + useEffect(() { if (kIsDesktop) { windowManager.isMaximized().then((value) { @@ -157,7 +169,7 @@ class WindowTitleBarButtons extends HookWidget { ), CloseWindowButton( colors: closeColors, - onPressed: windowManager.close, + onPressed: onClose, ), ], ), @@ -187,7 +199,7 @@ class WindowTitleBarButtons extends HookWidget { ), DecoratedCloseButton( type: type, - onPressed: windowManager.close, + onPressed: onClose, ), ], ), diff --git a/lib/hooks/use_init_sys_tray.dart b/lib/hooks/use_init_sys_tray.dart new file mode 100644 index 000000000..4bebdcbc8 --- /dev/null +++ b/lib/hooks/use_init_sys_tray.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:spotube/collections/intents.dart'; +import 'package:spotube/provider/playlist_queue_provider.dart'; +import 'package:spotube/provider/user_preferences_provider.dart'; + +void useInitSysTray(WidgetRef ref) { + final context = useContext(); + final systemTray = useRef(null); + + final initializeMenu = useCallback(() async { + systemTray.value?.destroy(); + final playlistQueue = ref.read(PlaylistQueueNotifier.notifier); + final preferences = ref.read(userPreferencesProvider); + if (!preferences.showSystemTrayIcon) { + await systemTray.value?.destroy(); + systemTray.value = null; + return; + } + final enabled = + playlistQueue.isLoaded && playlistQueue.state?.isLoading != true; + systemTray.value = await DesktopTools.createSystemTrayMenu( + title: "Spotube", + iconPath: "assets/spotube-logo.png", + windowsIconPath: "assets/spotube-logo.ico", + items: [ + MenuItemLabel( + label: "Show/Hide", + name: "show-hide", + onClicked: (item) async { + if (await DesktopTools.window.isVisible()) { + await DesktopTools.window.hide(); + } else { + await DesktopTools.window.show(); + } + }, + ), + MenuSeparator(), + MenuItemLabel( + label: "Play/Pause", + name: "play-pause", + enabled: enabled, + onClicked: (_) async { + Actions.maybeInvoke( + context, PlayPauseIntent(ref)) ?? + PlayPauseAction().invoke(PlayPauseIntent(ref)); + }, + ), + MenuItemLabel( + label: "Next", + name: "next", + enabled: enabled && (playlistQueue.state?.tracks.length ?? 0) > 1, + onClicked: (p0) async { + await playlistQueue.next(); + }, + ), + MenuItemLabel( + label: "Previous", + name: "previous", + enabled: enabled && (playlistQueue.state?.tracks.length ?? 0) > 1, + onClicked: (p0) async { + await playlistQueue.previous(); + }, + ), + MenuSeparator(), + MenuItemLabel( + label: "Quit", + name: "quit", + onClicked: (item) async { + await DesktopTools.window.close(); + }, + ), + ], + onEvent: (event, tray) async { + if (DesktopTools.platform.isWindows) { + switch (event) { + case SystemTrayEvent.click: + await DesktopTools.window.show(); + break; + case SystemTrayEvent.rightClick: + await tray.popUpContextMenu(); + break; + default: + } + } else { + switch (event) { + case SystemTrayEvent.rightClick: + await DesktopTools.window.show(); + break; + case SystemTrayEvent.click: + await tray.popUpContextMenu(); + break; + default: + } + } + }, + ); + }, [ref]); + + useReassemble(initializeMenu); + + ref.listen( + PlaylistQueueNotifier.provider, + (previous, next) { + initializeMenu(); + }, + ); + ref.listen( + userPreferencesProvider.select((s) => s.showSystemTrayIcon), + (previous, next) { + initializeMenu(); + }, + ); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((_) { + initializeMenu(); + }); + return () async { + await systemTray.value?.destroy(); + }; + }, [initializeMenu]); +} diff --git a/lib/main.dart b/lib/main.dart index 491209e21..11c80b6be 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:io'; import 'package:args/args.dart'; @@ -7,13 +6,13 @@ import 'package:fl_query/fl_query.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:metadata_god/metadata_god.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:spotube/collections/cache_keys.dart'; import 'package:spotube/collections/env.dart'; import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart'; import 'package:spotube/entities/cache_track.dart'; @@ -26,11 +25,10 @@ import 'package:spotube/services/audio_player.dart'; import 'package:spotube/services/pocketbase.dart'; import 'package:spotube/services/youtube.dart'; import 'package:spotube/themes/theme.dart'; -import 'package:spotube/utils/platform.dart'; -import 'package:window_manager/window_manager.dart'; -import 'package:window_size/window_size.dart'; import 'package:system_theme/system_theme.dart'; +import 'hooks/use_init_sys_tray.dart'; + Future main(List rawArgs) async { final parser = ArgParser(); @@ -69,6 +67,15 @@ Future main(List rawArgs) async { } WidgetsFlutterBinding.ensureInitialized(); + await DesktopTools.ensureInitialized( + DesktopWindowOptions( + hideTitleBar: true, + title: "Spotube", + backgroundColor: Colors.transparent, + minimumSize: const Size(300, 700), + ), + ); + await SystemTheme.accentColor.load(); MetadataGod.initialize(); await QueryClient.initialize(cachePrefix: "oss.krtirtho.spotube"); @@ -77,32 +84,6 @@ Future main(List rawArgs) async { Hive.registerAdapter(CacheTrackSkipSegmentAdapter()); await Env.configure(); - if (kIsDesktop) { - await windowManager.ensureInitialized(); - WindowOptions windowOptions = const WindowOptions( - center: true, - backgroundColor: Colors.transparent, - titleBarStyle: TitleBarStyle.hidden, - title: "Spotube", - ); - setWindowMinSize(const Size(kReleaseMode ? 1020 : 300, 700)); - await windowManager.waitUntilReadyToShow(windowOptions, () async { - final localStorage = await SharedPreferences.getInstance(); - final rawSize = localStorage.getString(LocalStorageKeys.windowSizeInfo); - final savedSize = rawSize != null ? json.decode(rawSize) : null; - final wasMaximized = savedSize?["maximized"] ?? false; - final double? height = savedSize?["height"]; - final double? width = savedSize?["width"]; - await windowManager.setResizable(true); - if (wasMaximized) { - await windowManager.maximize(); - } else if (height != null && width != null) { - await windowManager.setSize(Size(width, height)); - } - await windowManager.show(); - }); - } - Catcher( enableLogger: arguments["verbose"], debugConfig: CatcherOptions( @@ -188,44 +169,14 @@ class Spotube extends StatefulHookConsumerWidget { context.findAncestorStateOfType()!; } -class SpotubeState extends ConsumerState with WidgetsBindingObserver { +class SpotubeState extends ConsumerState { final logger = getLogger(Spotube); SharedPreferences? localStorage; - Size? prevSize; - @override void initState() { super.initState(); SharedPreferences.getInstance().then(((value) => localStorage = value)); - WidgetsBinding.instance.addObserver(this); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - @override - void didChangeMetrics() async { - super.didChangeMetrics(); - if (kIsMobile) return; - final size = await windowManager.getSize(); - final windowSameDimension = - prevSize?.width == size.width && prevSize?.height == size.height; - - if (localStorage == null || windowSameDimension) return; - final isMaximized = await windowManager.isMaximized(); - localStorage!.setString( - LocalStorageKeys.windowSizeInfo, - jsonEncode({ - 'maximized': isMaximized, - 'width': size.width, - 'height': size.height, - }), - ); - prevSize = size; } @override @@ -235,6 +186,8 @@ class SpotubeState extends ConsumerState with WidgetsBindingObserver { final accentMaterialColor = ref.watch(userPreferencesProvider.select((s) => s.accentColorScheme)); + useInitSysTray(ref); + /// For enabling hot reload for audio player useEffect(() { return () { diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index ea7a073f1..832305cb1 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -1,6 +1,7 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_desktop_tools/flutter_desktop_tools.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -198,14 +199,6 @@ class SettingsPage extends HookConsumerWidget { ), onTap: pickColorScheme(), ), - SwitchListTile( - secondary: const Icon(SpotubeIcons.album), - title: const Text("Rotating Album Art"), - value: preferences.rotatingAlbumArt, - onChanged: (state) { - preferences.setRotatingAlbumArt(state); - }, - ), Text( " Playback", style: theme.textTheme.headlineSmall @@ -321,6 +314,43 @@ class SettingsPage extends HookConsumerWidget { preferences.setSaveTrackLyrics(state); }, ), + if (DesktopTools.platform.isDesktop) ...[ + Text( + " Desktop", + style: theme.textTheme.headlineSmall + ?.copyWith(fontWeight: FontWeight.bold), + ), + AdaptiveListTile( + leading: const Icon(SpotubeIcons.close), + title: const Text("Close Behavior"), + trailing: (context, update) => + DropdownButton( + value: preferences.closeBehavior, + items: const [ + DropdownMenuItem( + value: CloseBehavior.close, + child: Text("Close"), + ), + DropdownMenuItem( + value: CloseBehavior.minimizeToTray, + child: Text("Minimize to Tray"), + ), + ], + onChanged: (value) { + if (value != null) { + preferences.setCloseBehavior(value); + update?.call(() {}); + } + }, + ), + ), + SwitchListTile( + secondary: const Icon(SpotubeIcons.tray), + title: const Text("Show System Tray Icon"), + value: preferences.showSystemTrayIcon, + onChanged: preferences.setShowSystemTrayIcon, + ), + ], Text( " About", style: theme.textTheme.headlineSmall diff --git a/lib/provider/user_preferences_provider.dart b/lib/provider/user_preferences_provider.dart index d5377826e..432506605 100644 --- a/lib/provider/user_preferences_provider.dart +++ b/lib/provider/user_preferences_provider.dart @@ -21,6 +21,11 @@ enum AudioQuality { low, } +enum CloseBehavior { + minimizeToTray, + close, +} + class UserPreferences extends PersistedChangeNotifier { ThemeMode themeMode; String recommendationMarket; @@ -35,10 +40,13 @@ class UserPreferences extends PersistedChangeNotifier { String downloadLocation; LayoutMode layoutMode; - bool rotatingAlbumArt; bool predownload; + CloseBehavior closeBehavior; + + bool showSystemTrayIcon; + UserPreferences({ required this.geniusAccessToken, required this.recommendationMarket, @@ -51,7 +59,8 @@ class UserPreferences extends PersistedChangeNotifier { this.audioQuality = AudioQuality.high, this.skipSponsorSegments = true, this.downloadLocation = "", - this.rotatingAlbumArt = true, + this.closeBehavior = CloseBehavior.minimizeToTray, + this.showSystemTrayIcon = true, }) : super() { if (downloadLocation.isEmpty) { _getDefaultDownloadDirectory().then( @@ -134,8 +143,14 @@ class UserPreferences extends PersistedChangeNotifier { updatePersistence(); } - void setRotatingAlbumArt(bool should) { - rotatingAlbumArt = should; + void setCloseBehavior(CloseBehavior behavior) { + closeBehavior = behavior; + notifyListeners(); + updatePersistence(); + } + + void setShowSystemTrayIcon(bool show) { + showSystemTrayIcon = show; notifyListeners(); updatePersistence(); } @@ -175,8 +190,14 @@ class UserPreferences extends PersistedChangeNotifier { (mode) => mode.name == map["layoutMode"], orElse: () => kIsDesktop ? LayoutMode.extended : LayoutMode.compact, ); - rotatingAlbumArt = map["rotatingAlbumArt"] ?? rotatingAlbumArt; + predownload = map["predownload"] ?? predownload; + + closeBehavior = map["closeBehavior"] != null + ? CloseBehavior.values[map["closeBehavior"]] + : closeBehavior; + + showSystemTrayIcon = map["showSystemTrayIcon"] ?? showSystemTrayIcon; } @override @@ -192,8 +213,9 @@ class UserPreferences extends PersistedChangeNotifier { "skipSponsorSegments": skipSponsorSegments, "downloadLocation": downloadLocation, "layoutMode": layoutMode.name, - "rotatingAlbumArt": rotatingAlbumArt, "predownload": predownload, + "closeBehavior": closeBehavior.index, + "showSystemTrayIcon": showSystemTrayIcon, }; } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 27cb2b9c9..ed93fee31 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -8,8 +8,11 @@ #include #include +#include +#include #include #include +#include #include #include #include @@ -21,12 +24,21 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) catcher_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "CatcherPlugin"); catcher_plugin_register_with_registrar(catcher_registrar); + g_autoptr(FlPluginRegistrar) desktop_multi_window_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopMultiWindowPlugin"); + desktop_multi_window_plugin_register_with_registrar(desktop_multi_window_registrar); + g_autoptr(FlPluginRegistrar) native_context_menu_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "NativeContextMenuPlugin"); + native_context_menu_plugin_register_with_registrar(native_context_menu_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); g_autoptr(FlPluginRegistrar) system_theme_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "SystemThemePlugin"); system_theme_plugin_register_with_registrar(system_theme_registrar); + g_autoptr(FlPluginRegistrar) system_tray_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SystemTrayPlugin"); + system_tray_plugin_register_with_registrar(system_tray_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 0ca5171f1..10ec19868 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -5,8 +5,11 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux catcher + desktop_multi_window + native_context_menu screen_retriever system_theme + system_tray url_launcher_linux window_manager window_size diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 3605b2f78..08679c1cb 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,13 +9,16 @@ import audio_service import audio_session import audioplayers_darwin import catcher +import desktop_multi_window import device_info_plus +import native_context_menu import package_info_plus import path_provider_foundation import screen_retriever import shared_preferences_foundation import sqflite import system_theme +import system_tray import url_launcher_macos import window_manager import window_size @@ -25,13 +28,16 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin")) + FlutterMultiWindowPlugin.register(with: registry.registrar(forPlugin: "FlutterMultiWindowPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + NativeContextMenuPlugin.register(with: registry.registrar(forPlugin: "NativeContextMenuPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin")) + SystemTrayPlugin.register(with: registry.registrar(forPlugin: "SystemTrayPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin")) diff --git a/pubspec.lock b/pubspec.lock index c87aea489..a53560a01 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -466,6 +466,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.8" + desktop_multi_window: + dependency: transitive + description: + name: desktop_multi_window + sha256: "29971186ae0790e32b156f127f9c22c5ee77bdb94b14f7cea23f2356d0c76cfc" + url: "https://pub.dev" + source: hosted + version: "0.2.0" device_info_plus: dependency: transitive description: @@ -599,6 +607,13 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.0" + flutter_desktop_tools: + dependency: "direct main" + description: + path: "../flutter_desktop_tools" + relative: true + source: path + version: "0.0.1" flutter_distributor: dependency: "direct dev" description: @@ -1000,6 +1015,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + native_context_menu: + dependency: transitive + description: + name: native_context_menu + sha256: "566f13d1f55e57bc8aca3b71b157b1ff03c2cc3821f6e9d980f76233ed30c9b7" + url: "https://pub.dev" + source: hosted + version: "0.2.2+5" oauth2: dependency: transitive description: @@ -1324,58 +1347,58 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" + sha256: "858aaa72d8f61637d64e776aca82e1c67e6d9ee07979123c5d17115031c1b13b" url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.1.0" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7" + sha256: "8304d8a1f7d21a429f91dee552792249362b68a331ac5c3c1caf370f658873f6" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.0" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0" + sha256: cf2a42fb20148502022861f71698db12d937c7459345a1bdaa88fc91a91b3603 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.2.0" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874 + sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.2.0" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.1.0" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9" + sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.2.0" shelf: dependency: transitive description: @@ -1550,6 +1573,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.0.2" + system_tray: + dependency: transitive + description: + name: system_tray + sha256: "087edb877f22f286d82d42f330fa640138c192e98aa9d20c2b83aa4e406bb432" + url: "https://pub.dev" + source: hosted + version: "2.0.2" term_glyph: dependency: transitive description: @@ -1808,5 +1839,5 @@ packages: source: hosted version: "1.12.3" sdks: - dart: ">=2.19.2 <3.0.0" + dart: ">=2.19.6 <3.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index f0f14b215..e6d1ae8f8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -82,6 +82,8 @@ dependencies: ref: a738913c8ce2c9f47515382d40827e794a334274 path: plugins/window_size youtube_explode_dart: ^1.12.1 + flutter_desktop_tools: + path: ../flutter_desktop_tools dev_dependencies: build_runner: ^2.3.2 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 68536c15e..a8be95c8d 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,9 +8,12 @@ #include #include +#include +#include #include #include #include +#include #include #include #include @@ -20,12 +23,18 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); CatcherPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("CatcherPlugin")); + DesktopMultiWindowPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopMultiWindowPlugin")); + NativeContextMenuPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("NativeContextMenuPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); SystemThemePluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("SystemThemePlugin")); + SystemTrayPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SystemTrayPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowManagerPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 1fc23cc85..bfe4236b0 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,9 +5,12 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_windows catcher + desktop_multi_window + native_context_menu permission_handler_windows screen_retriever system_theme + system_tray url_launcher_windows window_manager window_size