Files
pangolin/client/lib/state/settings_provider.dart
T
wangjia 6ee7114ff8
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
feat(client): P6 设置项落地真实行为(#6 6F,本地部分)
- settings_provider:三开关 shared_preferences 持久化 + 副作用落地:
  killSwitch→VpnBridge.setKillSwitch(on:);autostart→桌面原生登录项
  (launch_at_startup);smartRoute 持久化为偏好(连接时作 split_cn,见 #5)。
- settings_page → ConsumerWidget,开关接 settingsProvider;协议标签
  WireGuard→REALITY/Hysteria2;版本号取 pubspec(package_info_plus)。
- 新依赖:shared_preferences / package_info_plus / launch_at_startup。

flutter analyze 0 error;114 tests passed;settings golden 重生成。
待办:#5 智能分流的 server 端 geoip-cn 下发 + autostart 真机验证(需 release .app)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:13:41 +08:00

109 lines
3.5 KiB
Dart

// settings_provider.dart — 本地应用设置(持久化 + 副作用落地)。
//
// 三个开关均本地持久化(shared_preferences),并落地真实行为:
// - killSwitch → VpnBridge.setKillSwitch(改 TUN strict_route 并重载内核)
// - autostart → 桌面端原生登录项(launch_at_startup)
// - smartRoute → 持久化为偏好,连接时作为 split_cn 下发(geoip-cn 直连,见 #5)
import 'dart:io';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:launch_at_startup/launch_at_startup.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../bridge/vpn_bridge_provider.dart';
class AppSettings {
const AppSettings({this.autostart = true, this.smartRoute = true, this.killSwitch = true});
final bool autostart;
final bool smartRoute;
final bool killSwitch;
AppSettings copyWith({bool? autostart, bool? smartRoute, bool? killSwitch}) => AppSettings(
autostart: autostart ?? this.autostart,
smartRoute: smartRoute ?? this.smartRoute,
killSwitch: killSwitch ?? this.killSwitch,
);
}
const _kAutostart = 'set_autostart';
const _kSmartRoute = 'set_smart_route';
const _kKillSwitch = 'set_kill_switch';
class SettingsController extends StateNotifier<AppSettings> {
SettingsController(this._ref) : super(const AppSettings()) {
_load();
}
final Ref _ref;
final bool _desktop = Platform.isMacOS || Platform.isWindows || Platform.isLinux;
Future<void> _load() async {
try {
final p = await SharedPreferences.getInstance();
state = AppSettings(
autostart: p.getBool(_kAutostart) ?? true,
smartRoute: p.getBool(_kSmartRoute) ?? true,
killSwitch: p.getBool(_kKillSwitch) ?? true,
);
} catch (_) {
// 测试/无平台:保留默认值。
}
_applyKillSwitch(state.killSwitch);
if (_desktop) await _applyAutostart(state.autostart);
}
Future<void> setAutostart(bool v) async {
state = state.copyWith(autostart: v);
await _persist(_kAutostart, v);
if (_desktop) await _applyAutostart(v);
}
Future<void> setSmartRoute(bool v) async {
state = state.copyWith(smartRoute: v);
await _persist(_kSmartRoute, v);
}
Future<void> setKillSwitch(bool v) async {
state = state.copyWith(killSwitch: v);
await _persist(_kKillSwitch, v);
_applyKillSwitch(v);
}
void _applyKillSwitch(bool v) {
try {
_ref.read(vpnBridgeProvider).setKillSwitch(on: v);
} catch (_) {
// 桥未就绪/测试环境:忽略,持久化值在下次连接时生效。
}
}
Future<void> _applyAutostart(bool v) async {
try {
final info = await PackageInfo.fromPlatform();
launchAtStartup.setup(appName: info.appName, appPath: Platform.resolvedExecutable);
if (v) {
await launchAtStartup.enable();
} else {
await launchAtStartup.disable();
}
} catch (_) {
// 登录项注册失败(权限/调试运行)不应阻塞 UI。
}
}
Future<void> _persist(String key, bool v) async {
final p = await SharedPreferences.getInstance();
await p.setBool(key, v);
}
}
final settingsProvider =
StateNotifierProvider<SettingsController, AppSettings>((ref) => SettingsController(ref));
/// 应用版本号(来自 pubspec,经 package_info_plus)。
final appVersionProvider = FutureProvider<String>((ref) async {
final info = await PackageInfo.fromPlatform();
return 'v${info.version}';
});