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:
@@ -213,8 +213,8 @@ class DesktopVpnBridge implements VpnBridge {
|
|||||||
_reconnectTimer = null;
|
_reconnectTimer = null;
|
||||||
_kernelStatusSub?.cancel();
|
_kernelStatusSub?.cancel();
|
||||||
_kernelStatusSub = null;
|
_kernelStatusSub = null;
|
||||||
if (_kernel is DesktopKernelProcess) {
|
if (_kernel case final DesktopKernelProcess k) {
|
||||||
(_kernel as DesktopKernelProcess).dispose();
|
k.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +350,7 @@ class DesktopVpnBridge implements VpnBridge {
|
|||||||
|
|
||||||
Future<Directory> _resolveConfigDir() async {
|
Future<Directory> _resolveConfigDir() async {
|
||||||
if (_configDirOverride != null) {
|
if (_configDirOverride != null) {
|
||||||
return Directory(_configDirOverride!);
|
return Directory(_configDirOverride);
|
||||||
}
|
}
|
||||||
return _defaultConfigDir();
|
return _defaultConfigDir();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ class VpnBridgeMock implements VpnBridge {
|
|||||||
_killSwitch = on;
|
_killSwitch = on;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 测试可读:mock 记录的 kill-switch 状态。
|
||||||
|
bool get killSwitchEnabled => _killSwitch;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<VpnStatus> get statusStream => _statusCtrl.stream;
|
Stream<VpnStatus> get statusStream => _statusCtrl.stream;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import '../pangolin_theme.dart';
|
import '../pangolin_theme.dart';
|
||||||
import '../state/app_providers.dart';
|
import '../state/app_providers.dart';
|
||||||
import '../state/navigation_provider.dart';
|
import '../state/navigation_provider.dart';
|
||||||
import 'pangolin_icons.dart';
|
|
||||||
import 'pangolin_logo.dart';
|
import 'pangolin_logo.dart';
|
||||||
import 'plan_badge_card.dart';
|
import 'plan_badge_card.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,16 @@ import Foundation
|
|||||||
|
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
|
import screen_retriever_macos
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
import tray_manager
|
||||||
|
import window_manager
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
|
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
|
||||||
|
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,10 +49,23 @@ flutter run -d windows --dart-define=PANGOLIN_API_URL=http://<后端IP>:8080
|
|||||||
|
|
||||||
## 打包(release)
|
## 打包(release)
|
||||||
|
|
||||||
|
**推荐:一键脚本**(拉内核 → 带 dart-define 构建 → 出安装包,自动避开漏 `--dart-define` 的坑):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ExecutionPolicy Bypass -File client\windows\build.ps1 -ApiUrl http://<后端IP>:8080
|
||||||
|
# 只构建不出安装包:加 -SkipInstaller
|
||||||
|
```
|
||||||
|
|
||||||
|
或手动分步:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
cd client
|
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}`
|
CMake 的 install 步骤会自动把 `app/kernel/dist/desktop/windows-*/{sing-box.exe, wintun.dll}`
|
||||||
拷到 app `.exe` 同级目录(见 `windows/CMakeLists.txt` 的「Pangolin sing-box kernel」段)。
|
拷到 app `.exe` 同级目录(见 `windows/CMakeLists.txt` 的「Pangolin sing-box kernel」段)。
|
||||||
**wintun.dll 必须与 sing-box.exe 同目录**,否则 sing-box 无法创建 TUN。
|
**wintun.dll 必须与 sing-box.exe 同目录**,否则 sing-box 无法创建 TUN。
|
||||||
|
|||||||
@@ -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.dll,SHA 校验)
|
||||||
|
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 "未找到 bash(fetch 脚本需 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.exe(Inno 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 "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.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) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
|
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
|
||||||
|
TrayManagerPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
|
||||||
|
WindowManagerPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
|
screen_retriever_windows
|
||||||
|
tray_manager
|
||||||
|
window_manager
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Reference in New Issue
Block a user