Optimize desktop view

Optimize logs, requests, connection pages

Optimize windows tray auto hide

Optimize some details

Update core
This commit is contained in:
chen08209
2025-06-07 01:48:34 +08:00
parent adb890d763
commit 1154e7b245
169 changed files with 6484 additions and 5230 deletions

View File

@@ -11,8 +11,8 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/l10n/l10n.dart';
import 'package:fl_clash/plugins/service.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_js/flutter_js.dart';
import 'package:material_color_utilities/palettes/core_palette.dart';
import 'package:package_info_plus/package_info_plus.dart';
@@ -26,8 +26,10 @@ typedef UpdateTasks = List<FutureOr Function()>;
class GlobalState {
static GlobalState? _instance;
Map<CacheTag, double> cacheScrollPosition = {};
Map<CacheTag, FixedMap<String, double>> cacheHeightMap = {};
Map<CacheTag, FixedMap<String, double>> computeHeightMapCache = {};
// Map<CacheTag, double> computeScrollPositionCache = {};
// final Map<String, double> scrollPositionCache = {};
bool isService = false;
Timer? timer;
Timer? groupsUpdateTimer;
@@ -45,7 +47,8 @@ class GlobalState {
UpdateTasks tasks = [];
final navigatorKey = GlobalKey<NavigatorState>();
AppController? _appController;
GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
// GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
bool isInit = false;
bool get isStart => startTime != null && startTime!.isBeforeNow;
@@ -64,22 +67,24 @@ class GlobalState {
return _instance!;
}
initApp(int version) async {
coreSHA256 = const String.fromEnvironment("CORE_SHA256");
isPre = const String.fromEnvironment("APP_ENV") != 'stable';
Future<void> initApp(int version) async {
coreSHA256 = const String.fromEnvironment('CORE_SHA256');
isPre = const String.fromEnvironment('APP_ENV') != 'stable';
appState = AppState(
brightness: WidgetsBinding.instance.platformDispatcher.platformBrightness,
version: version,
viewSize: Size.zero,
requests: FixedList(maxLength),
logs: FixedList(maxLength),
traffics: FixedList(30),
totalTraffic: Traffic(),
systemUiOverlayStyle: const SystemUiOverlayStyle(),
);
await _initDynamicColor();
await init();
}
_initDynamicColor() async {
Future<void> _initDynamicColor() async {
try {
corePalette = await DynamicColorPlugin.getCorePalette();
accentColor = await DynamicColorPlugin.getAccentColor() ??
@@ -87,7 +92,7 @@ class GlobalState {
} catch (_) {}
}
init() async {
Future<void> init() async {
packageInfo = await PackageInfo.fromPlatform();
config = await preferences.getConfig() ??
Config(
@@ -102,7 +107,7 @@ class GlobalState {
String get ua => config.patchClashConfig.globalUa ?? packageInfo.ua;
startUpdateTasks([UpdateTasks? tasks]) async {
Future<void> startUpdateTasks([UpdateTasks? tasks]) async {
if (timer != null && timer!.isActive == true) return;
if (tasks != null) {
this.tasks = tasks;
@@ -113,20 +118,20 @@ class GlobalState {
});
}
executorUpdateTask() async {
Future<void> executorUpdateTask() async {
for (final task in tasks) {
await task();
}
timer = null;
}
stopUpdateTasks() {
void stopUpdateTasks() {
if (timer == null || timer?.isActive == false) return;
timer?.cancel();
timer = null;
}
handleStart([UpdateTasks? tasks]) async {
Future<void> handleStart([UpdateTasks? tasks]) async {
startTime ??= DateTime.now();
await clashCore.startListener();
await service?.startVpn();
@@ -227,38 +232,14 @@ class GlobalState {
);
}
Future<T?> safeRun<T>(
FutureOr<T> Function() futureFunction, {
String? title,
bool silence = true,
}) async {
try {
final res = await futureFunction();
return res;
} catch (e) {
commonPrint.log("$e");
if (silence) {
showNotifier(e.toString());
} else {
showMessage(
title: title ?? appLocalizations.tip,
message: TextSpan(
text: e.toString(),
),
);
}
return null;
}
}
showNotifier(String text) {
void showNotifier(String text) {
if (text.isEmpty) {
return;
}
navigatorKey.currentContext?.showNotifier(text);
}
openUrl(String url) async {
Future<void> openUrl(String url) async {
final res = await showMessage(
message: TextSpan(text: url),
title: appLocalizations.externalLink,
@@ -286,7 +267,7 @@ class GlobalState {
return CoreState(
vpnProps: config.vpnProps,
onlyStatisticsProxy: config.appSetting.onlyStatisticsProxy,
currentProfileName: currentProfile?.label ?? currentProfile?.id ?? "",
currentProfileName: currentProfile?.label ?? currentProfile?.id ?? '',
bypassDomain: config.networkProps.bypassDomain,
);
}
@@ -318,112 +299,112 @@ class GlobalState {
final realPatchConfig = patchConfig.copyWith(
tun: patchConfig.tun.getRealTun(config.networkProps.routeMode),
);
rawConfig["external-controller"] = realPatchConfig.externalController.value;
rawConfig["external-ui"] = "";
rawConfig["interface-name"] = "";
rawConfig["external-ui-url"] = "";
rawConfig["tcp-concurrent"] = realPatchConfig.tcpConcurrent;
rawConfig["unified-delay"] = realPatchConfig.unifiedDelay;
rawConfig["ipv6"] = realPatchConfig.ipv6;
rawConfig["log-level"] = realPatchConfig.logLevel.name;
rawConfig["port"] = 0;
rawConfig["socks-port"] = 0;
rawConfig["keep-alive-interval"] = realPatchConfig.keepAliveInterval;
rawConfig["mixed-port"] = realPatchConfig.mixedPort;
rawConfig["port"] = realPatchConfig.port;
rawConfig["socks-port"] = realPatchConfig.socksPort;
rawConfig["redir-port"] = realPatchConfig.redirPort;
rawConfig["tproxy-port"] = realPatchConfig.tproxyPort;
rawConfig["find-process-mode"] = realPatchConfig.findProcessMode.name;
rawConfig["allow-lan"] = realPatchConfig.allowLan;
rawConfig["mode"] = realPatchConfig.mode.name;
if (rawConfig["tun"] == null) {
rawConfig["tun"] = {};
rawConfig['external-controller'] = realPatchConfig.externalController.value;
rawConfig['external-ui'] = '';
rawConfig['interface-name'] = '';
rawConfig['external-ui-url'] = '';
rawConfig['tcp-concurrent'] = realPatchConfig.tcpConcurrent;
rawConfig['unified-delay'] = realPatchConfig.unifiedDelay;
rawConfig['ipv6'] = realPatchConfig.ipv6;
rawConfig['log-level'] = realPatchConfig.logLevel.name;
rawConfig['port'] = 0;
rawConfig['socks-port'] = 0;
rawConfig['keep-alive-interval'] = realPatchConfig.keepAliveInterval;
rawConfig['mixed-port'] = realPatchConfig.mixedPort;
rawConfig['port'] = realPatchConfig.port;
rawConfig['socks-port'] = realPatchConfig.socksPort;
rawConfig['redir-port'] = realPatchConfig.redirPort;
rawConfig['tproxy-port'] = realPatchConfig.tproxyPort;
rawConfig['find-process-mode'] = realPatchConfig.findProcessMode.name;
rawConfig['allow-lan'] = realPatchConfig.allowLan;
rawConfig['mode'] = realPatchConfig.mode.name;
if (rawConfig['tun'] == null) {
rawConfig['tun'] = {};
}
rawConfig["tun"]["enable"] = realPatchConfig.tun.enable;
rawConfig["tun"]["device"] = realPatchConfig.tun.device;
rawConfig["tun"]["dns-hijack"] = realPatchConfig.tun.dnsHijack;
rawConfig["tun"]["stack"] = realPatchConfig.tun.stack.name;
rawConfig["tun"]["route-address"] = realPatchConfig.tun.routeAddress;
rawConfig["tun"]["auto-route"] = realPatchConfig.tun.autoRoute;
rawConfig["geodata-loader"] = realPatchConfig.geodataLoader.name;
if (rawConfig["sniffer"]?["sniff"] != null) {
for (final value in (rawConfig["sniffer"]?["sniff"] as Map).values) {
if (value["ports"] != null && value["ports"] is List) {
value["ports"] =
value["ports"]?.map((item) => item.toString()).toList() ?? [];
rawConfig['tun']['enable'] = realPatchConfig.tun.enable;
rawConfig['tun']['device'] = realPatchConfig.tun.device;
rawConfig['tun']['dns-hijack'] = realPatchConfig.tun.dnsHijack;
rawConfig['tun']['stack'] = realPatchConfig.tun.stack.name;
rawConfig['tun']['route-address'] = realPatchConfig.tun.routeAddress;
rawConfig['tun']['auto-route'] = realPatchConfig.tun.autoRoute;
rawConfig['geodata-loader'] = realPatchConfig.geodataLoader.name;
if (rawConfig['sniffer']?['sniff'] != null) {
for (final value in (rawConfig['sniffer']?['sniff'] as Map).values) {
if (value['ports'] != null && value['ports'] is List) {
value['ports'] =
value['ports']?.map((item) => item.toString()).toList() ?? [];
}
}
}
if (rawConfig["profile"] == null) {
rawConfig["profile"] = {};
if (rawConfig['profile'] == null) {
rawConfig['profile'] = {};
}
if (rawConfig["proxy-providers"] != null) {
final proxyProviders = rawConfig["proxy-providers"] as Map;
if (rawConfig['proxy-providers'] != null) {
final proxyProviders = rawConfig['proxy-providers'] as Map;
for (final key in proxyProviders.keys) {
final proxyProvider = proxyProviders[key];
if (proxyProvider["type"] != "http") {
if (proxyProvider['type'] != 'http') {
continue;
}
if (proxyProvider["url"] != null) {
proxyProvider["path"] = await appPath.getProvidersFilePath(
if (proxyProvider['url'] != null) {
proxyProvider['path'] = await appPath.getProvidersFilePath(
profile.id,
"proxies",
proxyProvider["url"],
'proxies',
proxyProvider['url'],
);
}
}
}
if (rawConfig["rule-providers"] != null) {
final ruleProviders = rawConfig["rule-providers"] as Map;
if (rawConfig['rule-providers'] != null) {
final ruleProviders = rawConfig['rule-providers'] as Map;
for (final key in ruleProviders.keys) {
final ruleProvider = ruleProviders[key];
if (ruleProvider["type"] != "http") {
if (ruleProvider['type'] != 'http') {
continue;
}
if (ruleProvider["url"] != null) {
ruleProvider["path"] = await appPath.getProvidersFilePath(
if (ruleProvider['url'] != null) {
ruleProvider['path'] = await appPath.getProvidersFilePath(
profile.id,
"rules",
ruleProvider["url"],
'rules',
ruleProvider['url'],
);
}
}
}
rawConfig["profile"]["store-selected"] = false;
rawConfig["geox-url"] = realPatchConfig.geoXUrl.toJson();
rawConfig["global-ua"] = realPatchConfig.globalUa;
if (rawConfig["hosts"] == null) {
rawConfig["hosts"] = {};
rawConfig['profile']['store-selected'] = false;
rawConfig['geox-url'] = realPatchConfig.geoXUrl.toJson();
rawConfig['global-ua'] = realPatchConfig.globalUa;
if (rawConfig['hosts'] == null) {
rawConfig['hosts'] = {};
}
for (final host in realPatchConfig.hosts.entries) {
rawConfig["hosts"][host.key] = host.value.splitByMultipleSeparators;
rawConfig['hosts'][host.key] = host.value.splitByMultipleSeparators;
}
if (rawConfig["dns"] == null) {
rawConfig["dns"] = {};
if (rawConfig['dns'] == null) {
rawConfig['dns'] = {};
}
final isEnableDns = rawConfig["dns"]["enable"] == true;
final isEnableDns = rawConfig['dns']['enable'] == true;
final overrideDns = globalState.config.overrideDns;
if (overrideDns || !isEnableDns) {
final dns = switch (!isEnableDns) {
true => realPatchConfig.dns.copyWith(
nameserver: [...realPatchConfig.dns.nameserver, "system://"]),
nameserver: [...realPatchConfig.dns.nameserver, 'system://']),
false => realPatchConfig.dns,
};
rawConfig["dns"] = dns.toJson();
rawConfig["dns"]["nameserver-policy"] = {};
rawConfig['dns'] = dns.toJson();
rawConfig['dns']['nameserver-policy'] = {};
for (final entry in dns.nameserverPolicy.entries) {
rawConfig["dns"]["nameserver-policy"][entry.key] =
rawConfig['dns']['nameserver-policy'][entry.key] =
entry.value.splitByMultipleSeparators;
}
}
var rules = [];
if (rawConfig["rules"] != null) {
rules = rawConfig["rules"];
if (rawConfig['rules'] != null) {
rules = rawConfig['rules'];
}
rawConfig.remove("rules");
rawConfig.remove('rules');
final overrideData = profile.overrideData;
if (overrideData.enable && config.scriptProps.currentScript == null) {
@@ -433,7 +414,7 @@ class GlobalState {
rules = [...overrideData.runningRule, ...rules];
}
}
rawConfig["rule"] = rules;
rawConfig['rule'] = rules;
return rawConfig;
}
@@ -442,8 +423,8 @@ class GlobalState {
true => clashLibHandler!.getConfig(profileId),
false => clashCore.getConfig(profileId),
};
configMap["rules"] = configMap["rule"];
configMap.remove("rule");
configMap['rules'] = configMap['rule'];
configMap.remove('rule');
return configMap;
}
@@ -454,15 +435,15 @@ class GlobalState {
if (currentScript == null) {
return config;
}
if (config["proxy-providers"] == null) {
config["proxy-providers"] = {};
if (config['proxy-providers'] == null) {
config['proxy-providers'] = {};
}
final configJs = json.encode(config);
final runtime = getJavascriptRuntime();
final res = await runtime.evaluateAsync("""
final res = await runtime.evaluateAsync('''
${currentScript.content}
main($configJs)
""");
''');
if (res.isError) {
throw res.stringResult;
}
@@ -484,7 +465,6 @@ class DetectionState {
final state = ValueNotifier<NetworkDetectionState>(
const NetworkDetectionState(
isTesting: false,
isLoading: true,
ipInfo: null,
),
@@ -497,7 +477,7 @@ class DetectionState {
return _instance!;
}
startCheck() {
void startCheck() {
debouncer.call(
FunctionTag.checkIp,
_checkIp,
@@ -507,7 +487,13 @@ class DetectionState {
);
}
_checkIp() async {
void tryStartCheck() {
if (state.value.isLoading == false && state.value.ipInfo == null) {
startCheck();
}
}
Future<void> _checkIp() async {
final appState = globalState.appState;
final isInit = appState.isInit;
if (!isInit) return;
@@ -528,9 +514,6 @@ class DetectionState {
cancelToken = null;
}
cancelToken = CancelToken();
state.value = state.value.copyWith(
isTesting: true,
);
final res = await request.checkIp(cancelToken: cancelToken);
if (res.isError) {
state.value = state.value.copyWith(
@@ -540,9 +523,6 @@ class DetectionState {
return;
}
final ipInfo = res.data;
state.value = state.value.copyWith(
isTesting: false,
);
if (ipInfo != null) {
state.value = state.value.copyWith(
isLoading: false,
@@ -559,7 +539,7 @@ class DetectionState {
});
}
_clearSetTimeoutTimer() {
void _clearSetTimeoutTimer() {
if (_setTimeoutTimer != null) {
_setTimeoutTimer?.cancel();
_setTimeoutTimer = null;