Files
xianyan/lib/core/router/app_nav_extension.dart
Developer 83720002e6 feat: 新增工作台模式、系统托盘,修复多平台兼容性问题
1. 新增工作台三栏布局模式,适配宽屏设备
2. 添加跨平台系统托盘支持,新增托盘图标资源
3. 修复工作台模式下导航返回异常问题
4. 统一JSON类型安全解析,替换硬类型转换
5. 增加macOS深度链接支持,统一渠道分发信息
6. 优化部分页面生命周期和状态加载逻辑
7. 移除废弃的nearby_connections依赖
2026-06-19 06:43:55 +08:00

272 lines
8.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 闲言APP — 跨平台导航扩展
// 创建时间: 2026-05-18
// 更新时间: 2026-06-19
// 作用: 统一导航API鸿蒙端使用OhosNavBridge其他端使用GoRouter自动记录最近路由
// 上次更新: 新增 appPop/appCanPop 工作台感知方法,修复右栏页面 Navigator.pop 导致根栈被弹空的白屏问题
// ============================================================
import 'package:flutter/widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:xianyan/core/utils/platform/platform_utils.dart' as pu;
import 'package:xianyan/core/router/ohos_nav_bridge.dart';
import 'package:xianyan/features/settings/providers/general_settings_provider.dart';
import 'package:xianyan/core/services/recent_route_service.dart';
import 'package:xianyan/core/router/route_registry.dart';
import 'package:xianyan/core/layout/workbench/right_panel_navigator.dart';
import 'package:xianyan/core/layout/adaptive_split_view.dart' show kCompactBreakpoint;
import 'package:xianyan/core/providers/split_view_provider.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
/// 工作台模式导航辅助
///
/// 在宽屏工作台模式下,将非全屏路由重定向到右栏嵌套 Navigator
class WorkbenchNavHelper {
/// 判断当前是否处于工作台模式
///
/// 所有平台(桌面/移动/Web在宽度 >= 768px 且工作台开关开启时自动启用
/// 仅鸿蒙端因独立布局不走工作台模式
static bool isWorkbenchMode(BuildContext context, WidgetRef ref) {
// 鸿蒙端不走工作台模式(独立布局)
if (pu.isOhos) return false;
final screenWidth = MediaQuery.sizeOf(context).width;
final splitState = ref.read(splitViewProvider);
return screenWidth >= kCompactBreakpoint && splitState.workbenchEnabled;
}
/// 判断路由是否应该进入右栏(非全屏路由)
static bool shouldOpenInRightPanel(String route) {
return !isFullScreenRoute(route);
}
/// 在右栏 push 一个页面
static void pushToRightPanel(
WidgetRef ref,
int currentTab,
String route, {
Object? extra,
String? title,
}) {
final entry = RightPanelEntry(
route: route,
title: title ?? inferRouteTitle(route),
extra: extra,
);
ref.read(rightPanelStackProvider.notifier).push(currentTab, entry);
}
}
extension AppNavExtension on BuildContext {
Future<T?> appPush<T extends Object?>(
String route, {
Object? extra,
PageTransitionMode? transitionMode,
String? title,
}) {
RecentRouteService.addRecentRoute(route);
if (pu.isOhos) {
return OhosNavBridge.push<T>(
this,
route,
extra: extra,
transitionMode: transitionMode,
);
}
// 工作台模式判断:宽屏 + 非全屏路由 → push 到右栏嵌套 Navigator
if (!pu.isOhos) {
final screenWidth = MediaQuery.sizeOf(this).width;
if (screenWidth >= kCompactBreakpoint) {
// 通过 ProviderScope 获取 ProviderContainer
final container = ProviderScope.containerOf(this);
final splitState = container.read(splitViewProvider);
if (splitState.workbenchEnabled && !isFullScreenRoute(route)) {
// 工作台模式push 到右栏嵌套 Navigator
final entry = RightPanelEntry(
route: route,
title: title ?? inferRouteTitle(route),
extra: extra,
);
container
.read(rightPanelStackProvider.notifier)
.push(splitState.currentTab, entry);
return Future<T?>.value();
}
}
}
return push<T>(route, extra: extra);
}
Future<T?> appReplace<T extends Object?>(String route, {Object? extra}) {
RecentRouteService.addRecentRoute(route);
if (pu.isOhos) {
return OhosNavBridge.replace<T>(this, route, extra: extra);
}
pushReplacement(route, extra: extra);
return Future<T?>.value();
}
void appGo(String route) {
RecentRouteService.addRecentRoute(route);
if (pu.isOhos) {
OhosNavBridge.go(this, route);
return;
}
go(route);
}
// ============================================================
// 工作台模式感知的 pop / canPop
// ============================================================
/// 工作台模式感知的 pop
///
/// - 宽屏工作台模式 → pop 右栏嵌套栈
/// - 窄屏或非工作台 → 走 Navigator.pop兼容 go_router
///
/// 修复问题:右栏页面直接调用 `Navigator.pop(context)` 会操作根 GoRouter
/// Navigator导致栈被弹空出现白屏断言错误。
void appPop<T extends Object?>([T? result]) {
if (pu.isOhos) {
Navigator.pop(this, result);
return;
}
// 工作台模式判断:宽屏 + 工作台开关开启 → pop 右栏栈
final screenWidth = MediaQuery.sizeOf(this).width;
if (screenWidth >= kCompactBreakpoint) {
final container = ProviderScope.containerOf(this);
final splitState = container.read(splitViewProvider);
if (splitState.workbenchEnabled) {
// 工作台模式pop 右栏栈顶页面
container
.read(rightPanelStackProvider.notifier)
.pop(splitState.currentTab);
return;
}
}
// 正常模式:使用 Navigator.pop
Navigator.pop(this, result);
}
/// 工作台模式感知的 canPop
///
/// - 宽屏工作台模式 → 检查右栏嵌套栈是否非空
/// - 窄屏或非工作台 → 走 Navigator.canPop
bool appCanPop() {
if (pu.isOhos) {
return Navigator.of(this).canPop();
}
// 工作台模式判断
final screenWidth = MediaQuery.sizeOf(this).width;
if (screenWidth >= kCompactBreakpoint) {
final container = ProviderScope.containerOf(this);
final splitState = container.read(splitViewProvider);
if (splitState.workbenchEnabled) {
// 工作台模式:检查右栏栈是否有页面
final stackMap = container.read(rightPanelStackProvider);
final stack = stackMap[splitState.currentTab];
return stack != null && stack.entries.isNotEmpty;
}
}
// 正常模式
return Navigator.of(this).canPop();
}
}
// ============================================================
// ConsumerContext 扩展 — 工作台模式感知的导航
// ============================================================
/// 工作台模式感知的导航扩展
///
/// 使用方式:
/// ```dart
/// ref.appPush(context, '/sentence/123', extra: {...});
/// ```
extension WorkbenchNavExtension on WidgetRef {
/// 工作台模式感知的 push
///
/// - 宽屏工作台模式 + 非全屏路由 → push 到右栏嵌套 Navigator
/// - 窄屏 或 全屏路由 → 走 GoRouter rootNavigator push
Future<T?> appPush<T extends Object?>(
BuildContext context,
String route, {
Object? extra,
PageTransitionMode? transitionMode,
String? title,
}) {
RecentRouteService.addRecentRoute(route);
// 鸿蒙端走 OhosNavBridge
if (pu.isOhos) {
return OhosNavBridge.push<T>(
context,
route,
extra: extra,
transitionMode: transitionMode,
);
}
// 工作台模式判断
final isWorkbench = WorkbenchNavHelper.isWorkbenchMode(context, this);
final shouldRightPanel = WorkbenchNavHelper.shouldOpenInRightPanel(route);
if (isWorkbench && shouldRightPanel) {
// 宽屏工作台模式push 到右栏嵌套 Navigator
final currentTab = read(splitViewProvider).currentTab;
WorkbenchNavHelper.pushToRightPanel(
this,
currentTab,
route,
extra: extra,
title: title,
);
return Future<T?>.value();
}
// 窄屏或全屏路由:走 GoRouter push
return context.push<T>(route, extra: extra);
}
/// 工作台模式感知的 popWidgetRef 版本)
///
/// - 宽屏工作台模式 → pop 右栏嵌套栈
/// - 窄屏或非工作台 → 走 Navigator.pop
void appPop<T extends Object?>(BuildContext context, [T? result]) {
if (pu.isOhos) {
Navigator.pop(context, result);
return;
}
final isWorkbench = WorkbenchNavHelper.isWorkbenchMode(context, this);
if (isWorkbench) {
final currentTab = read(splitViewProvider).currentTab;
read(rightPanelStackProvider.notifier).pop(currentTab);
return;
}
Navigator.pop(context, result);
}
/// 工作台模式感知的 canPopWidgetRef 版本)
bool appCanPop(BuildContext context) {
if (pu.isOhos) {
return Navigator.of(context).canPop();
}
if (WorkbenchNavHelper.isWorkbenchMode(context, this)) {
final currentTab = read(splitViewProvider).currentTab;
final stackMap = read(rightPanelStackProvider);
final stack = stackMap[currentTab];
return stack != null && stack.entries.isNotEmpty;
}
return Navigator.of(context).canPop();
}
}