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

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) {