1. 新增工作台三栏布局模式,适配宽屏设备 2. 添加跨平台系统托盘支持,新增托盘图标资源 3. 修复工作台模式下导航返回异常问题 4. 统一JSON类型安全解析,替换硬类型转换 5. 增加macOS深度链接支持,统一渠道分发信息 6. 优化部分页面生命周期和状态加载逻辑 7. 移除废弃的nearby_connections依赖
446 lines
13 KiB
Dart
446 lines
13 KiB
Dart
/// ============================================================
|
||
/// 闲言APP — macOS 原生菜单栏包装器
|
||
/// 创建时间: 2026-06-18
|
||
/// 更新时间: 2026-06-18
|
||
/// 作用: 使用 PlatformMenuBar 注册 macOS 顶部原生菜单(闲言/文件/编辑/视图/窗口/帮助)
|
||
/// 上次更新: 初始创建,实现 6 个顶级菜单 + 业务功能入口
|
||
/// ============================================================
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
||
import '../../core/providers/split_view_provider.dart';
|
||
import '../../core/router/app_router.dart' show appRouter;
|
||
import '../../core/router/app_routes.dart';
|
||
import '../../core/utils/logger.dart';
|
||
import '../../core/utils/platform/platform_utils.dart' as pu;
|
||
import '../../features/settings/providers/theme_settings_provider.dart';
|
||
|
||
/// macOS 原生菜单栏包装器
|
||
///
|
||
/// 在 macOS 上使用 [PlatformMenuBar] 注册原生顶部菜单栏,包含:
|
||
/// 1. 闲言菜单:关于/偏好设置/新建笔记/新建灵感/隐藏/退出
|
||
/// 2. 文件菜单:新建笔记/打开文件/关闭窗口
|
||
/// 3. 编辑菜单:撤销/重做/剪切/复制/粘贴/全选(系统标准)
|
||
/// 4. 视图菜单:全屏/切换深色模式/工作台模式/切换侧边栏
|
||
/// 5. 窗口菜单:最小化/缩放/关闭窗口(系统标准)
|
||
/// 6. 帮助菜单:闲言帮助/检查更新/反馈问题
|
||
///
|
||
/// 非 macOS 平台直接返回 child,不包裹 PlatformMenuBar。
|
||
class MacosMenuBarWrapper extends ConsumerWidget {
|
||
const MacosMenuBarWrapper({super.key, required this.child});
|
||
|
||
final Widget child;
|
||
|
||
@override
|
||
Widget build(BuildContext context, WidgetRef ref) {
|
||
// 非 macOS 平台不注册原生菜单栏
|
||
if (!pu.isMacOS) return child;
|
||
|
||
// 监听主题和工作台模式状态(用于菜单勾选)
|
||
final isDark = ref.watch(themeSettingsProvider).isDark;
|
||
final isWorkbench = ref.watch(splitViewProvider).workbenchEnabled;
|
||
|
||
return PlatformMenuBar(
|
||
menus: _buildMenus(
|
||
context: context,
|
||
ref: ref,
|
||
isDark: isDark,
|
||
isWorkbench: isWorkbench,
|
||
),
|
||
child: child,
|
||
);
|
||
}
|
||
|
||
/// 构建所有菜单
|
||
List<PlatformMenuItem> _buildMenus({
|
||
required BuildContext context,
|
||
required WidgetRef ref,
|
||
required bool isDark,
|
||
required bool isWorkbench,
|
||
}) {
|
||
return [
|
||
_buildAppMenu(context, ref),
|
||
_buildFileMenu(context, ref),
|
||
_buildEditMenu(),
|
||
_buildViewMenu(context, ref, isDark, isWorkbench),
|
||
_buildWindowMenu(),
|
||
_buildHelpMenu(context, ref),
|
||
];
|
||
}
|
||
|
||
// ============================================================
|
||
// 闲言菜单(application)
|
||
// ============================================================
|
||
|
||
PlatformMenu _buildAppMenu(BuildContext context, WidgetRef ref) {
|
||
return PlatformMenu(
|
||
label: '闲言',
|
||
menus: [
|
||
const PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.about,
|
||
),
|
||
],
|
||
),
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '偏好设置…',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.comma,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _navigateTo(AppRoutes.generalSettings),
|
||
),
|
||
],
|
||
),
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '新建笔记',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyN,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _navigateTo(AppRoutes.noteEdit),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '新建灵感',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyI,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _navigateTo(AppRoutes.inspiration),
|
||
),
|
||
],
|
||
),
|
||
const PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.hide,
|
||
),
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.hideOtherApplications,
|
||
),
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.showAllApplications,
|
||
),
|
||
],
|
||
),
|
||
const PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.quit,
|
||
),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// 文件菜单
|
||
// ============================================================
|
||
|
||
PlatformMenu _buildFileMenu(BuildContext context, WidgetRef ref) {
|
||
return PlatformMenu(
|
||
label: '文件',
|
||
menus: [
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '新建笔记',
|
||
// 快捷键 Cmd+N 已在"闲言"菜单中注册,此处不重复注册
|
||
onSelected: () => _navigateTo(AppRoutes.noteEdit),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '打开稍后阅读…',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyR,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _navigateTo(AppRoutes.readLater),
|
||
),
|
||
],
|
||
),
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '关闭窗口',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyW,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _closeWindow(),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// 编辑菜单(系统标准)
|
||
// ============================================================
|
||
|
||
PlatformMenu _buildEditMenu() {
|
||
return PlatformMenu(
|
||
label: '编辑',
|
||
menus: [
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '撤销',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyZ,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _undo(),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '重做',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyZ,
|
||
meta: true,
|
||
shift: true,
|
||
),
|
||
onSelected: () => _redo(),
|
||
),
|
||
],
|
||
),
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '剪切',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyX,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _cut(),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '复制',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyC,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _copy(),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '粘贴',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyV,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _paste(),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '全选',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyA,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _selectAll(),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// 视图菜单
|
||
// ============================================================
|
||
|
||
PlatformMenu _buildViewMenu(
|
||
BuildContext context,
|
||
WidgetRef ref,
|
||
bool isDark,
|
||
bool isWorkbench,
|
||
) {
|
||
return PlatformMenu(
|
||
label: '视图',
|
||
menus: [
|
||
const PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.toggleFullScreen,
|
||
),
|
||
],
|
||
),
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '深色模式',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyD,
|
||
meta: true,
|
||
shift: true,
|
||
),
|
||
onSelected: () => _toggleDarkMode(ref),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '工作台模式',
|
||
shortcut: const SingleActivator(
|
||
LogicalKeyboardKey.keyB,
|
||
meta: true,
|
||
),
|
||
onSelected: () => _toggleWorkbench(ref),
|
||
),
|
||
],
|
||
),
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '每日拾句',
|
||
onSelected: () => _navigateTo(AppRoutes.home),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '句子广场',
|
||
onSelected: () => _navigateTo(AppRoutes.home),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// 窗口菜单(系统标准)
|
||
// ============================================================
|
||
|
||
PlatformMenu _buildWindowMenu() {
|
||
return const PlatformMenu(
|
||
label: '窗口',
|
||
menus: [
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.minimizeWindow,
|
||
),
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.zoomWindow,
|
||
),
|
||
],
|
||
),
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformProvidedMenuItem(
|
||
type: PlatformProvidedMenuItemType.arrangeWindowsInFront,
|
||
),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// 帮助菜单
|
||
// ============================================================
|
||
|
||
PlatformMenu _buildHelpMenu(BuildContext context, WidgetRef ref) {
|
||
return PlatformMenu(
|
||
label: '帮助',
|
||
menus: [
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '闲言帮助',
|
||
onSelected: () => _navigateTo(AppRoutes.about),
|
||
),
|
||
],
|
||
),
|
||
PlatformMenuItemGroup(
|
||
members: [
|
||
PlatformMenuItem(
|
||
label: '检查更新',
|
||
onSelected: () => _navigateTo(AppRoutes.appInfo),
|
||
),
|
||
PlatformMenuItem(
|
||
label: '反馈问题',
|
||
onSelected: () => _navigateTo(AppRoutes.correction),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
// ============================================================
|
||
// 辅助方法
|
||
// ============================================================
|
||
|
||
/// 导航到指定路由
|
||
void _navigateTo(String route) {
|
||
try {
|
||
appRouter.push(route);
|
||
} catch (e) {
|
||
Log.e('MacosMenuBarWrapper._navigateTo 失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 切换深色/浅色模式
|
||
void _toggleDarkMode(WidgetRef ref) {
|
||
try {
|
||
final settings = ref.read(themeSettingsProvider);
|
||
final newMode = settings.isDark ? AppThemeMode.light : AppThemeMode.dark;
|
||
ref.read(themeSettingsProvider.notifier).setThemeMode(newMode);
|
||
} catch (e) {
|
||
Log.e('MacosMenuBarWrapper._toggleDarkMode 失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 切换工作台模式
|
||
void _toggleWorkbench(WidgetRef ref) {
|
||
try {
|
||
final current = ref.read(splitViewProvider).workbenchEnabled;
|
||
ref.read(splitViewProvider.notifier).setWorkbenchEnabled(!current);
|
||
} catch (e) {
|
||
Log.e('MacosMenuBarWrapper._toggleWorkbench 失败: $e');
|
||
}
|
||
}
|
||
|
||
/// 关闭窗口
|
||
///
|
||
/// macOS 上快捷键 Cmd+W 由系统自动处理,此方法作为菜单点击的 fallback。
|
||
void _closeWindow() {
|
||
// 快捷键 Cmd+W 由系统自动处理,此方法仅作为菜单点击的 fallback
|
||
Log.d('MacosMenuBarWrapper._closeWindow: 由系统快捷键处理');
|
||
}
|
||
|
||
/// 撤销
|
||
///
|
||
/// macOS 上快捷键 Cmd+Z 由系统自动处理,此方法作为菜单点击的 fallback。
|
||
void _undo() {
|
||
Log.d('MacosMenuBarWrapper._undo: 由系统快捷键处理');
|
||
}
|
||
|
||
/// 重做
|
||
void _redo() {
|
||
Log.d('MacosMenuBarWrapper._redo: 由系统快捷键处理');
|
||
}
|
||
|
||
/// 剪切
|
||
void _cut() {
|
||
Log.d('MacosMenuBarWrapper._cut: 由系统快捷键处理');
|
||
}
|
||
|
||
/// 复制
|
||
void _copy() {
|
||
Log.d('MacosMenuBarWrapper._copy: 由系统快捷键处理');
|
||
}
|
||
|
||
/// 粘贴
|
||
void _paste() {
|
||
Log.d('MacosMenuBarWrapper._paste: 由系统快捷键处理');
|
||
}
|
||
|
||
/// 全选
|
||
void _selectAll() {
|
||
Log.d('MacosMenuBarWrapper._selectAll: 由系统快捷键处理');
|
||
}
|
||
}
|