import 'dart:io'; import 'package:path/path.dart' as p; /// 路径工具类 - 获取应用根目录和 bin 目录 /// /// 约定: /// - Windows 开发模式:`windows/bin` 目录下放置配置和数据文件 /// - Windows 发布模式:可执行文件同级目录下放置 `bin` 目录 /// - Linux 开发模式:项目根目录下放置 `bin` 目录 /// - Linux 发布模式:可执行文件同级目录下放置 `bin` 目录 class PathUtils { static String? _cachedAppRoot; /// 获取应用根目录 static String get appRoot { if (_cachedAppRoot != null) { return _cachedAppRoot!; } try { final exePath = Platform.resolvedExecutable; final exeDir = p.dirname(exePath); if (Platform.isWindows) { _cachedAppRoot = _resolveWindowsAppRoot(exeDir); } else if (Platform.isLinux) { _cachedAppRoot = _resolveLinuxAppRoot(exeDir); } else { // 其他平台暂时按当前工作目录 + bin 处理 _cachedAppRoot = _resolveGenericAppRoot(exeDir); } return _cachedAppRoot!; } catch (_) { // 兜底:使用当前工作目录 _cachedAppRoot = Directory.current.path; return _cachedAppRoot!; } } /// Windows 平台下解析根目录 static String _resolveWindowsAppRoot(String exeDir) { // 开发模式:可执行文件在 build 目录下,需要回溯到 windows 目录 if (exeDir.contains('build') || exeDir.contains('windows\\build') || exeDir.contains('windows/build')) { var currentPath = exeDir; while (currentPath.isNotEmpty) { final dirName = p.basename(currentPath); final parent = p.dirname(currentPath); if (dirName == 'windows' && !currentPath.contains('build\\windows') && !currentPath.contains('build/windows')) { final binPath = p.join(currentPath, 'bin'); if (Directory(binPath).existsSync()) { return currentPath; } } if (parent == currentPath) break; currentPath = parent; } // 回退到通过当前工作目录查找 final fromCwd = _findDirWithBinFromCwd('windows'); if (fromCwd != null) return fromCwd; } // 发布模式:可执行文件和 bin 文件夹在同一目录 return exeDir; } /// Linux 平台下解析根目录 static String _resolveLinuxAppRoot(String exeDir) { // 如果可执行文件目录下有 bin,优先使用(发布模式) final binInExeDir = p.join(exeDir, 'bin'); if (Directory(binInExeDir).existsSync()) { return exeDir; } // 开发模式:从当前工作目录向上查找包含 bin 的目录(项目根目录) final fromCwd = _findDirWithBinFromCwd(null); if (fromCwd != null) return fromCwd; // 再退回到 exeDir return exeDir; } /// 通用平台兜底逻辑:优先找当前工作目录上的 bin static String _resolveGenericAppRoot(String exeDir) { final fromCwd = _findDirWithBinFromCwd(null); if (fromCwd != null) return fromCwd; return exeDir; } /// 从当前工作目录向上查找,直到找到满足条件的目录 /// 如果 [expectDirName] 不为空,则必须匹配目录名,例如 'windows' static String? _findDirWithBinFromCwd(String? expectDirName) { var currentPath = Directory.current.path; while (currentPath.isNotEmpty) { final dirName = p.basename(currentPath); final binPath = p.join(currentPath, 'bin'); if ((expectDirName == null || dirName == expectDirName) && Directory(binPath).existsSync()) { return currentPath; } final parent = p.dirname(currentPath); if (parent == currentPath) break; currentPath = parent; } return null; } /// 获取 bin 目录路径 static String get binDir { final root = appRoot; return p.join(root, 'bin'); } /// 获取 bin 目录下的文件路径 static String binFile(String filename) { return p.join(binDir, filename); } }