fix+build(client/windows): 复查修缮——一键构建脚本 + 修 README dart-define 漏项 + 清 lint

- 新增 client/windows/build.ps1:拉内核→带 --dart-define 构建→出安装包一键串联,
  规避 Release 漏 --dart-define 导致默认连 localhost、登录「网络请求失败」的坑。
- README:Release 命令补回 --dart-define 并加显式警告,指向一键脚本。
- 清 4 个 lint warning:desktop_vpn_bridge 多余 cast→pattern match、去多余非空断言;
  nav_sidebar 删未用 import;vpn_bridge_mock 加 killSwitchEnabled getter。
- 同步 window_manager/tray_manager/screen_retriever 插件注册生成文件(win+macos)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-22 14:17:13 +08:00
parent d7c2e22e6f
commit 322d806876
8 changed files with 135 additions and 5 deletions
+3 -3
View File
@@ -213,8 +213,8 @@ class DesktopVpnBridge implements VpnBridge {
_reconnectTimer = null;
_kernelStatusSub?.cancel();
_kernelStatusSub = null;
if (_kernel is DesktopKernelProcess) {
(_kernel as DesktopKernelProcess).dispose();
if (_kernel case final DesktopKernelProcess k) {
k.dispose();
}
}
@@ -350,7 +350,7 @@ class DesktopVpnBridge implements VpnBridge {
Future<Directory> _resolveConfigDir() async {
if (_configDirOverride != null) {
return Directory(_configDirOverride!);
return Directory(_configDirOverride);
}
return _defaultConfigDir();
}
+3
View File
@@ -121,6 +121,9 @@ class VpnBridgeMock implements VpnBridge {
_killSwitch = on;
}
/// 测试可读:mock 记录的 kill-switch 状态。
bool get killSwitchEnabled => _killSwitch;
@override
Stream<VpnStatus> get statusStream => _statusCtrl.stream;
-1
View File
@@ -8,7 +8,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../pangolin_theme.dart';
import '../state/app_providers.dart';
import '../state/navigation_provider.dart';
import 'pangolin_icons.dart';
import 'pangolin_logo.dart';
import 'plan_badge_card.dart';
@@ -7,10 +7,16 @@ import Foundation
import flutter_secure_storage_macos
import package_info_plus
import screen_retriever_macos
import shared_preferences_foundation
import tray_manager
import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
}
+14 -1
View File
@@ -49,10 +49,23 @@ flutter run -d windows --dart-define=PANGOLIN_API_URL=http://<后端IP>:8080
## 打包(release
**推荐:一键脚本**(拉内核 → 带 dart-define 构建 → 出安装包,自动避开漏 `--dart-define` 的坑):
```powershell
powershell -ExecutionPolicy Bypass -File client\windows\build.ps1 -ApiUrl http://<后端IP>:8080
# 只构建不出安装包:加 -SkipInstaller
```
或手动分步:
```powershell
cd client
flutter build windows
# 必须带 --dart-define 指定后端,否则默认连 localhost:8080,登录会「网络请求失败」
flutter build windows --dart-define=PANGOLIN_API_URL=http://<后端IP>:8080
```
> ⚠️ `kApiBaseUrl` 默认值是 `http://localhost:8080`(见 `lib/services/api_config.dart`)。
> Release 包必须显式 `--dart-define=PANGOLIN_API_URL=...`,构建期烘进二进制;漏了会复现登录失败。
CMake 的 install 步骤会自动把 `app/kernel/dist/desktop/windows-*/{sing-box.exe, wintun.dll}`
拷到 app `.exe` 同级目录(见 `windows/CMakeLists.txt` 的「Pangolin sing-box kernel」段)。
**wintun.dll 必须与 sing-box.exe 同目录**,否则 sing-box 无法创建 TUN。
+97
View File
@@ -0,0 +1,97 @@
<#
.SYNOPSIS
Pangolin Windows 客户端一键构建脚本(拉内核 → 带 dart-define 构建 → 出安装包)。
.DESCRIPTION
串起 client/windows/README.md 里的三步手动流程,避免漏掉 --dart-define
(漏了会默认连 localhost:8080,登录「网络请求失败」)。
步骤:
1. bash app/kernel/fetch-desktop-bin.sh windows <arch> (拉 sing-box.exe + wintun.dllSHA 校验)
2. flutter build windows --release --dart-define=PANGOLIN_API_URL=<ApiUrl>
3. ISCC.exe installer\pangolin.iss (除非 -SkipInstaller
.PARAMETER ApiUrl
必填。后端地址,会在构建期烘进二进制,例:http://103.119.13.48:8080
.PARAMETER Arch
CPU 架构,默认 amd64。
.PARAMETER SkipInstaller
只构建,不出 Inno Setup 安装包。
.EXAMPLE
powershell -ExecutionPolicy Bypass -File client\windows\build.ps1 -ApiUrl http://103.119.13.48:8080
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ApiUrl,
[string]$Arch = "amd64",
[switch]$SkipInstaller
)
$ErrorActionPreference = "Stop"
# 路径解析:本脚本在 client/windows/ 下
$WindowsDir = $PSScriptRoot
$ClientDir = Split-Path -Parent $WindowsDir
$RepoRoot = Split-Path -Parent $ClientDir
function Step([string]$msg) { Write-Host "`n==> $msg" -ForegroundColor Cyan }
# ── 1. 拉内核(sing-box.exe + wintun.dll)─────────────────────────────────────
Step "拉取 sing-box.exe + wintun.dll (windows $Arch)"
$bash = Get-Command bash -ErrorAction SilentlyContinue
if (-not $bash) {
throw "未找到 bashfetch 脚本需 Git Bash)。请安装 Git for Windows 后重试。"
}
$fetchScript = Join-Path $RepoRoot "app/kernel/fetch-desktop-bin.sh"
& $bash.Source $fetchScript windows $Arch
if ($LASTEXITCODE -ne 0) { throw "内核拉取失败 (exit $LASTEXITCODE)" }
# ── 2. Flutter 构建(必须带 dart-define)──────────────────────────────────────
Step "flutter build windows --release (PANGOLIN_API_URL=$ApiUrl)"
Push-Location $ClientDir
try {
flutter build windows --release --dart-define=PANGOLIN_API_URL=$ApiUrl
if ($LASTEXITCODE -ne 0) { throw "flutter build 失败 (exit $LASTEXITCODE)" }
}
finally {
Pop-Location
}
$releaseDir = Join-Path $ClientDir "build\windows\x64\runner\Release"
Write-Host "构建产物:$releaseDir" -ForegroundColor Green
# ── 3. 出安装包(Inno Setup)──────────────────────────────────────────────────
if ($SkipInstaller) {
Step "跳过安装包(-SkipInstaller"
return
}
Step "编译 Inno Setup 安装包"
$iscc = $null
foreach ($p in @(
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe",
"C:\Program Files\Inno Setup 6\ISCC.exe")) {
if (Test-Path $p) { $iscc = $p; break }
}
if (-not $iscc) {
$cmd = Get-Command ISCC.exe -ErrorAction SilentlyContinue
if ($cmd) { $iscc = $cmd.Source }
}
if (-not $iscc) {
Write-Warning "未找到 ISCC.exeInno Setup 6 未安装)。已完成构建,跳过安装包。"
Write-Warning "装好 Inno Setup 6 后可手动:ISCC.exe `"$WindowsDir\installer\pangolin.iss`""
return
}
$iss = Join-Path $WindowsDir "installer\pangolin.iss"
& $iscc $iss
if ($LASTEXITCODE -ne 0) { throw "ISCC 编译失败 (exit $LASTEXITCODE)" }
$output = Join-Path $WindowsDir "installer\Output"
Write-Host "`n✓ 全部完成。安装包在:$output" -ForegroundColor Green
@@ -7,8 +7,17 @@
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
#include <tray_manager/tray_manager_plugin.h>
#include <window_manager/window_manager_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
TrayManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
}
@@ -4,6 +4,9 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_windows
screen_retriever_windows
tray_manager
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST