1.增加托盘图标

2.关闭修改为最小化到托盘
3.优化播放页播放速率选项
This commit is contained in:
rang 2025-11-06 17:09:09 +08:00
parent 779c132a11
commit 13767656b5
14 changed files with 354 additions and 15 deletions

BIN
assets/icon/logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -28,7 +28,9 @@ class SpeedSelector extends HookConsumerWidget {
// hook the onSpeedSelected function to the state
useEffect(
() {
onSpeedSelected(speedState.value);
Future.microtask(() {
onSpeedSelected(speedState.value);
});
return null;
},
[speedState.value],

View file

@ -14,19 +14,34 @@ import 'package:vaani/features/player/providers/audiobook_player.dart'
import 'package:vaani/features/shake_detection/providers/shake_detector.dart';
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/models/tray.dart';
import 'package:vaani/router/router.dart';
import 'package:vaani/settings/api_settings_provider.dart';
import 'package:vaani/settings/app_settings_provider.dart';
import 'package:vaani/settings/settings.dart';
import 'package:vaani/shared/utils/utils.dart';
import 'package:vaani/theme/providers/system_theme_provider.dart';
import 'package:vaani/theme/providers/theme_from_cover_provider.dart';
import 'package:vaani/theme/theme.dart';
import 'package:window_manager/window_manager.dart';
final appLogger = Logger(AppMetadata.appName);
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//
if (Utils.isDesktop()) {
await windowManager.ensureInitialized();
final windowOptions = WindowOptions(
minimumSize: Size(1050, 700),
center: true,
skipTaskbar: false,
);
await windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.setPreventClose(true);
});
}
// Configure the root Logger
await initLogging();
@ -39,7 +54,7 @@ void main() async {
// run the app
runApp(
const ProviderScope(
child: _EagerInitialization(child: MyApp()),
child: _EagerInitialization(child: TrayFramework(MyApp())),
),
);
}

130
lib/models/tray.dart Normal file
View file

@ -0,0 +1,130 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:vaani/features/player/providers/audiobook_player.dart';
import 'package:vaani/settings/constants.dart';
import 'package:vaani/shared/utils/utils.dart';
import 'package:window_manager/window_manager.dart';
class TrayFramework extends ConsumerStatefulWidget {
final Widget child;
const TrayFramework(this.child, {super.key});
@override
ConsumerState<TrayFramework> createState() => _TrayFrameworkState();
}
class _TrayFrameworkState extends ConsumerState<TrayFramework>
with TrayListener, WindowListener {
@override
void initState() {
if (Utils.isDesktop()) {
windowManager.addListener(this);
_init();
}
super.initState();
}
@override
void dispose() {
trayManager.removeListener(this);
super.dispose();
}
void _init() async {
await trayManager.setIcon(
Utils.isWindows() ? 'assets/icon/logo.ico' : 'assets/icon/logo.png',
);
await trayManager.setToolTip(AppMetadata.appName);
Menu menu = Menu(
items: [
MenuItem(
key: 'show_window',
// label: 'Show Window',
label: '显示主窗口',
onClick: (menuItem) => windowManager.show(),
),
MenuItem.separator(),
MenuItem(
key: 'play_pause',
label: '播放/暂停',
onClick: (menuItem) =>
ref.read(audiobookPlayerProvider).togglePlayPause(),
),
MenuItem(
key: 'previous',
label: '上一个',
onClick: (menuItem) =>
ref.read(audiobookPlayerProvider).seekToPrevious(),
),
MenuItem(
key: 'next',
label: '下一个',
onClick: (menuItem) => ref.read(audiobookPlayerProvider).seekToNext(),
),
MenuItem.separator(),
MenuItem(
key: 'exit_app',
// label: 'Exit App',
label: '退出',
onClick: (menuItem) => windowManager.destroy(),
),
],
);
await trayManager.setContextMenu(menu);
trayManager.addListener(this);
}
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void onTrayIconMouseDown() {
// do something, for example pop up the menu
// print('onTrayIconMouseDown');
windowManager.show();
}
@override
void onTrayIconMouseUp() {
// do something, for example pop up the menu
// print('onTrayIconMouseUp');
}
@override
void onTrayIconRightMouseDown() {
// do something
// print('onTrayIconRightMouseDown');
trayManager.popUpContextMenu(bringAppToFront: true);
}
@override
void onTrayIconRightMouseUp() {
// do something
// print('onTrayIconRightMouseUp');
}
// @override
// void onTrayMenuItemClick(MenuItem menuItem) {
// print(menuItem.key);
// if (menuItem.key == 'show_window') {
// // do something
// } else if (menuItem.key == 'exit_app') {
// // do something
// } else if (menuItem.key == 'play_pause'){
// }
// }
@override
void onWindowClose() async {
final isPreventClose = await windowManager.isPreventClose();
if (isPreventClose) {
windowManager.hide();
}
}
}

View file

@ -0,0 +1,40 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
class Utils {
static isAndroid() {
return !kIsWeb && Platform.isAndroid;
}
static isIOS() {
return !kIsWeb && Platform.isIOS;
}
static isMobile() {
return !kIsWeb && (Platform.isAndroid || Platform.isIOS);
}
static isDesktop() {
if (kIsWeb) {
return false;
}
return Platform.isWindows || Platform.isLinux || Platform.isMacOS;
}
static isWindows() {
return !kIsWeb && Platform.isWindows;
}
static isMacos() {
return !kIsWeb && Platform.isMacOS;
}
static isLinux() {
return !kIsWeb && Platform.isLinux;
}
static isWeb() {
return kIsWeb;
}
}

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_html/flutter_html.dart';
class ExpandableDescription extends HookWidget {
const ExpandableDescription({
@ -78,18 +79,28 @@ class ExpandableDescription extends HookWidget {
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: AnimatedSwitcher(
duration: duration * 3,
child: isDescExpanded.value
? Text(
style: textTheme.bodyMedium,
content,
maxLines: null,
)
: Text(
style: textTheme.bodyMedium,
content,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
// child: isDescExpanded.value
// ? Text(
// style: textTheme.bodyMedium,
// content,
// maxLines: null,
// )
// : Text(
// style: textTheme.bodyMedium,
// content,
// maxLines: 3,
// overflow: TextOverflow.ellipsis,
// ),
child: Html(
data: '<div class="vaani-ellipsis">$content</div>',
style: {
"div": Style(
maxLines: isDescExpanded.value ? null : 3,
textOverflow: TextOverflow.ellipsis,
fontStyle: textTheme.bodyMedium?.fontStyle,
),
},
),
),
),

View file

@ -70,7 +70,7 @@ class SimpleHomeShelf extends HookConsumerWidget {
),
// fix the height of the shelf as a percentage of the screen height
SizedBox(
height: height ?? getDefaultShelfHeight(context, perCent: 0.5),
height: height ?? getDefaultShelfHeight(context, perCent: 0.45),
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {

View file

@ -9,7 +9,10 @@
#include <dynamic_color/dynamic_color_plugin.h>
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
#include <tray_manager/tray_manager_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
@ -21,7 +24,16 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin");
media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar);
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
g_autoptr(FlPluginRegistrar) tray_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin");
tray_manager_plugin_register_with_registrar(tray_manager_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);
g_autoptr(FlPluginRegistrar) window_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
window_manager_plugin_register_with_registrar(window_manager_registrar);
}

View file

@ -6,7 +6,10 @@ list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
isar_flutter_libs
media_kit_libs_linux
screen_retriever_linux
tray_manager
url_launcher_linux
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View file

@ -14,9 +14,12 @@ import isar_flutter_libs
import just_audio
import package_info_plus
import path_provider_foundation
import screen_retriever_macos
import share_plus
import sqflite_darwin
import tray_manager
import url_launcher_macos
import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
@ -28,7 +31,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
}

View file

@ -318,6 +318,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.6"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
cupertino_icons:
dependency: "direct main"
description:
@ -499,6 +507,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.21.3+1"
flutter_html:
dependency: "direct main"
description:
name: flutter_html
sha256: "38a2fd702ffdf3243fb7441ab58aa1bc7e6922d95a50db76534de8260638558d"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
@ -642,6 +658,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.3.0"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.dev"
source: hosted
version: "0.15.6"
http:
dependency: transitive
description:
@ -811,6 +835,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.1.1"
list_counter:
dependency: transitive
description:
name: list_counter
sha256: c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237
url: "https://pub.dev"
source: hosted
version: "1.0.2"
list_wheel_scroll_view_nls:
dependency: "direct main"
description:
@ -899,6 +931,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.9"
menu_base:
dependency: transitive
description:
name: menu_base
sha256: "820368014a171bd1241030278e6c2617354f492f5c703d7b7d4570a6b8b84405"
url: "https://pub.dev"
source: hosted
version: "0.1.1"
meta:
dependency: transitive
description:
@ -1180,6 +1220,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
screen_retriever:
dependency: transitive
description:
name: screen_retriever
sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
screen_retriever_linux:
dependency: transitive
description:
name: screen_retriever_linux
sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18
url: "https://pub.dev"
source: hosted
version: "0.2.0"
screen_retriever_macos:
dependency: transitive
description:
name: screen_retriever_macos
sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
screen_retriever_platform_interface:
dependency: transitive
description:
name: screen_retriever_platform_interface
sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0
url: "https://pub.dev"
source: hosted
version: "0.2.0"
screen_retriever_windows:
dependency: transitive
description:
name: screen_retriever_windows
sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
scroll_loop_auto_scroll:
dependency: "direct main"
description:
@ -1251,6 +1331,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0"
shortid:
dependency: transitive
description:
name: shortid
sha256: d0b40e3dbb50497dad107e19c54ca7de0d1a274eb9b4404991e443dadb9ebedb
url: "https://pub.dev"
source: hosted
version: "0.1.2"
sky_engine:
dependency: transitive
description: flutter
@ -1416,6 +1504,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
tray_manager:
dependency: "direct main"
description:
name: tray_manager
sha256: c5fd83b0ae4d80be6eaedfad87aaefab8787b333b8ebd064b0e442a81006035b
url: "https://pub.dev"
source: hosted
version: "0.5.2"
typed_data:
dependency: transitive
description:
@ -1592,6 +1688,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
window_manager:
dependency: "direct main"
description:
name: window_manager
sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
xdg_directories:
dependency: transitive
description:

View file

@ -96,8 +96,11 @@ dependencies:
shimmer: ^3.0.0
url_launcher: ^6.2.6
vibration: ^3.1.3
flutter_html: ^3.0.0
flutter_localizations:
sdk: flutter
window_manager: ^0.5.1
tray_manager: ^0.5.2
dev_dependencies:
build_runner: ^2.4.9
custom_lint: ^0.7.0
@ -126,6 +129,7 @@ flutter:
- assets/sounds/
- assets/images/
- assets/fonts/
- assets/icon/
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see

View file

@ -10,8 +10,11 @@
#include <isar_flutter_libs/isar_flutter_libs_plugin.h>
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
#include <tray_manager/tray_manager_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_manager/window_manager_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
DynamicColorPluginCApiRegisterWithRegistrar(
@ -22,8 +25,14 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("MediaKitLibsWindowsAudioPluginCApi"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
TrayManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
}

View file

@ -7,8 +7,11 @@ list(APPEND FLUTTER_PLUGIN_LIST
isar_flutter_libs
media_kit_libs_windows_audio
permission_handler_windows
screen_retriever_windows
share_plus
tray_manager
url_launcher_windows
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST