Compare commits

..

3 Commits

Author SHA1 Message Date
chen08209
d798628fcf Fix windows tun issues
Optimize android get system dns
2025-06-14 11:50:03 +08:00
chen08209
a06e813249 Update changelog 2025-06-07 16:08:38 +00:00
chen08209
afbc5adb05 Support override script
Support proxies search

Support svg display

Optimize config persistence

Add some scenes auto close connections

Update core

Optimize more details
2025-06-07 23:52:27 +08:00
34 changed files with 410 additions and 70 deletions

View File

@@ -1,3 +1,19 @@
## v0.8.85
- Support override script
- Support proxies search
- Support svg display
- Optimize config persistence
- Add some scenes auto close connections
- Update core
- Optimize more details
## v0.8.84
- Fix windows service verify issues

View File

@@ -104,7 +104,7 @@ fun ConnectivityManager.resolveDns(network: Network?): List<String> {
fun InetAddress.asSocketAddressText(port: Int): String {
return when (this) {
is Inet6Address ->
"[${numericToTextFormat(this.address)}]:$port"
"[${numericToTextFormat(this)}]:$port"
is Inet4Address ->
"${this.hostAddress}:$port"
@@ -141,7 +141,8 @@ fun Context.getActionPendingIntent(action: String): PendingIntent {
}
}
private fun numericToTextFormat(src: ByteArray): String {
private fun numericToTextFormat(address: Inet6Address): String {
val src = address.address
val sb = StringBuilder(39)
for (i in 0 until 8) {
sb.append(
@@ -154,6 +155,10 @@ private fun numericToTextFormat(src: ByteArray): String {
sb.append(":")
}
}
if (address.scopeId > 0) {
sb.append("%")
sb.append(address.scopeId)
}
return sb.toString()
}

View File

@@ -100,6 +100,7 @@ data object VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
}
fun handleStart(options: VpnOptions): Boolean {
onUpdateNetwork();
if (options.enable != this.options?.enable) {
this.flClashService = null
}

View File

@@ -405,5 +405,6 @@
"portConflictTip": "Please enter a different port",
"import": "Import",
"importFile": "Import from file",
"importUrl": "Import from URL"
"importUrl": "Import from URL",
"autoSetSystemDns": "Auto set system DNS"
}

View File

@@ -406,5 +406,6 @@
"portConflictTip": "別のポートを入力してください",
"import": "インポート",
"importFile": "ファイルからインポート",
"importUrl": "URLからインポート"
"importUrl": "URLからインポート",
"autoSetSystemDns": "オートセットシステムDNS"
}

View File

@@ -406,5 +406,6 @@
"portConflictTip": "Введите другой порт",
"import": "Импорт",
"importFile": "Импорт из файла",
"importUrl": "Импорт по URL"
"importUrl": "Импорт по URL",
"autoSetSystemDns": "Автоматическая настройка системного DNS"
}

View File

@@ -406,5 +406,6 @@
"portConflictTip": "请输入不同的端口",
"import": "导入",
"importFile": "通过文件导入",
"importUrl": "通过URL导入"
"importUrl": "通过URL导入",
"autoSetSystemDns": "自动设置系统DNS"
}

View File

@@ -66,6 +66,11 @@ class ClashCore {
Future<bool> init() async {
await initGeo();
if (globalState.config.appSetting.openLogs) {
clashCore.startLog();
} else {
clashCore.stopLog();
}
final homeDirPath = await appPath.homeDirPath;
return await clashInterface.init(
InitParams(

View File

@@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
class System {
static System? _instance;
List<String>? originDns;
System._internal();
@@ -104,6 +105,100 @@ class System {
return AuthorizeCode.error;
}
Future<String?> getMacOSDefaultServiceName() async {
if (!Platform.isMacOS) {
return null;
}
final result = await Process.run('route', ['-n', 'get', 'default']);
final output = result.stdout.toString();
final deviceLine = output
.split('\n')
.firstWhere((s) => s.contains('interface:'), orElse: () => "");
final lineSplits = deviceLine.trim().split(' ');
if (lineSplits.length != 2) {
return null;
}
final device = lineSplits[1];
final serviceResult = await Process.run(
'networksetup',
['-listnetworkserviceorder'],
);
final serviceResultOutput = serviceResult.stdout.toString();
final currentService = serviceResultOutput.split('\n\n').firstWhere(
(s) => s.contains("Device: $device"),
orElse: () => "",
);
if (currentService.isEmpty) {
return null;
}
final currentServiceNameLine = currentService.split("\n").firstWhere(
(line) => RegExp(r'^\(\d+\).*').hasMatch(line),
orElse: () => "");
final currentServiceNameLineSplits =
currentServiceNameLine.trim().split(' ');
if (currentServiceNameLineSplits.length < 2) {
return null;
}
return currentServiceNameLineSplits[1];
}
Future<List<String>?> getMacOSOriginDns() async {
if (!Platform.isMacOS) {
return null;
}
final deviceServiceName = await getMacOSDefaultServiceName();
if (deviceServiceName == null) {
return null;
}
final result = await Process.run(
'networksetup',
['-getdnsservers', deviceServiceName],
);
final output = result.stdout.toString().trim();
if (output.startsWith("There aren't any DNS Servers set on")) {
originDns = [];
} else {
originDns = output.split("\n");
}
return originDns;
}
setMacOSDns(bool restore) async {
if (!Platform.isMacOS) {
return;
}
final serviceName = await getMacOSDefaultServiceName();
if (serviceName == null) {
return;
}
List<String>? nextDns;
if (restore) {
nextDns = originDns;
} else {
final originDns = await system.getMacOSOriginDns();
if (originDns == null) {
return;
}
final needAddDns = "223.5.5.5";
if (originDns.contains(needAddDns)) {
return;
}
nextDns = List.from(originDns)..add(needAddDns);
}
if (nextDns == null) {
return;
}
await Process.run(
'networksetup',
[
'-setdnsservers',
serviceName,
if (nextDns.isNotEmpty) ...nextDns,
if (nextDns.isEmpty) "Empty",
],
);
}
back() async {
await app?.moveTaskToBack();
await window?.hide();

View File

@@ -22,7 +22,6 @@ import 'models/models.dart';
import 'views/profiles/override_profile.dart';
class AppController {
bool lastTunEnable = false;
int? lastProfileModified;
final BuildContext context;
@@ -263,29 +262,31 @@ class AppController {
if (res.isError) {
return;
}
lastTunEnable = res.data == true;
final realTunEnable = _ref.read(realTunEnableProvider);
final message = await clashCore.updateConfig(
updateParams.copyWith.tun(
enable: lastTunEnable,
enable: realTunEnable,
),
);
if (message.isNotEmpty) throw message;
}
Future<Result<bool>> _requestAdmin(bool enableTun) async {
if (enableTun != lastTunEnable && lastTunEnable == false) {
final realTunEnable = _ref.read(realTunEnableProvider);
if (enableTun != realTunEnable && realTunEnable == false) {
final code = await system.authorizeCore();
switch (code) {
case AuthorizeCode.none:
return Result.success(enableTun);
case AuthorizeCode.success:
await restartCore();
return Result.error("");
case AuthorizeCode.none:
break;
case AuthorizeCode.error:
enableTun = false;
return Result.success(false);
break;
}
}
_ref.read(realTunEnableProvider.notifier).value = enableTun;
return Result.success(enableTun);
}
@@ -304,8 +305,8 @@ class AppController {
if (res.isError) {
return;
}
lastTunEnable = res.data == true;
final realPatchConfig = patchConfig.copyWith.tun(enable: lastTunEnable);
final realTunEnable = _ref.read(realTunEnableProvider);
final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable);
final params = await globalState.getSetupParams(
pathConfig: realPatchConfig,
);
@@ -443,6 +444,7 @@ class AppController {
});
try {
await savePreferences();
await system.setMacOSDns(true);
await proxy?.stopProxy();
await clashCore.shutdown();
await clashService?.destroy();

View File

@@ -123,6 +123,9 @@ class MessageLookup extends MessageLookupByLibrary {
"autoRunDesc": MessageLookupByLibrary.simpleMessage(
"Auto run when the application is opened",
),
"autoSetSystemDns": MessageLookupByLibrary.simpleMessage(
"Auto set system DNS",
),
"autoUpdate": MessageLookupByLibrary.simpleMessage("Auto update"),
"autoUpdateInterval": MessageLookupByLibrary.simpleMessage(
"Auto update interval (minutes)",

View File

@@ -93,6 +93,7 @@ class MessageLookup extends MessageLookupByLibrary {
"autoLaunchDesc": MessageLookupByLibrary.simpleMessage("システムの自動起動に従う"),
"autoRun": MessageLookupByLibrary.simpleMessage("自動実行"),
"autoRunDesc": MessageLookupByLibrary.simpleMessage("アプリ起動時に自動実行"),
"autoSetSystemDns": MessageLookupByLibrary.simpleMessage("オートセットシステムDNS"),
"autoUpdate": MessageLookupByLibrary.simpleMessage("自動更新"),
"autoUpdateInterval": MessageLookupByLibrary.simpleMessage("自動更新間隔(分)"),
"backup": MessageLookupByLibrary.simpleMessage("バックアップ"),

View File

@@ -120,6 +120,9 @@ class MessageLookup extends MessageLookupByLibrary {
"autoRunDesc": MessageLookupByLibrary.simpleMessage(
"Автоматический запуск при открытии приложения",
),
"autoSetSystemDns": MessageLookupByLibrary.simpleMessage(
"Автоматическая настройка системного DNS",
),
"autoUpdate": MessageLookupByLibrary.simpleMessage("Автообновление"),
"autoUpdateInterval": MessageLookupByLibrary.simpleMessage(
"Интервал автообновления (минуты)",

View File

@@ -87,6 +87,7 @@ class MessageLookup extends MessageLookupByLibrary {
"autoLaunchDesc": MessageLookupByLibrary.simpleMessage("跟随系统自启动"),
"autoRun": MessageLookupByLibrary.simpleMessage("自动运行"),
"autoRunDesc": MessageLookupByLibrary.simpleMessage("应用打开时自动运行"),
"autoSetSystemDns": MessageLookupByLibrary.simpleMessage("自动设置系统DNS"),
"autoUpdate": MessageLookupByLibrary.simpleMessage("自动更新"),
"autoUpdateInterval": MessageLookupByLibrary.simpleMessage("自动更新间隔(分钟)"),
"backup": MessageLookupByLibrary.simpleMessage("备份"),

View File

@@ -3129,6 +3129,16 @@ class AppLocalizations {
args: [],
);
}
/// `Auto set system DNS`
String get autoSetSystemDns {
return Intl.message(
'Auto set system DNS',
name: 'autoSetSystemDns',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -62,6 +62,7 @@ Future<void> _service(List<String> flags) async {
vpn?.addListener(
_VpnListenerWithService(
onDnsChanged: (String dns) {
print("handle dns $dns");
clashLibHandler.updateDns(dns);
},
),

View File

@@ -46,10 +46,29 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
globalState.appController.savePreferencesDebounce();
}
});
ref.listenManual(
autoSetSystemDnsStateProvider,
(prev, next) async {
if (prev == next) {
return;
}
if (next.a == true && next.b == true) {
system.setMacOSDns(false);
} else {
system.setMacOSDns(true);
}
},
);
}
@override
void dispose() {
reassemble() {
super.reassemble();
}
@override
void dispose() async {
await system.setMacOSDns(true);
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

View File

@@ -57,7 +57,6 @@ class _ClashContainerState extends ConsumerState<ClashManager>
clashCore.stopLog();
}
},
fireImmediately: true,
);
}

View File

@@ -32,6 +32,7 @@ class AppState with _$AppState {
required FixedList<Traffic> traffics,
required Traffic totalTraffic,
@Default("") String proxiesQuery,
@Default(false) bool realTunEnable,
}) = _AppState;
}

View File

@@ -200,6 +200,24 @@ class Tun with _$Tun {
}
}
extension TunExt on Tun {
Tun getRealTun(RouteMode routeMode) {
final mRouteAddress = routeMode == RouteMode.bypassPrivate
? defaultBypassPrivateRouteAddress
: routeAddress;
return switch (system.isDesktop) {
true => copyWith(
autoRoute: true,
routeAddress: [],
),
false => copyWith(
autoRoute: mRouteAddress.isEmpty ? true : false,
routeAddress: mRouteAddress,
),
};
}
}
@freezed
class FallbackFilter with _$FallbackFilter {
const factory FallbackFilter({

View File

@@ -152,7 +152,8 @@ class NetworkProps with _$NetworkProps {
const factory NetworkProps({
@Default(true) bool systemProxy,
@Default(defaultBypassDomain) List<String> bypassDomain,
@Default(RouteMode.bypassPrivate) RouteMode routeMode,
@Default(RouteMode.config) RouteMode routeMode,
@Default(true) bool autoSetSystemDns,
}) = _NetworkProps;
factory NetworkProps.fromJson(Map<String, Object?>? json) =>

View File

@@ -36,6 +36,7 @@ mixin _$AppState {
FixedList<Traffic> get traffics => throw _privateConstructorUsedError;
Traffic get totalTraffic => throw _privateConstructorUsedError;
String get proxiesQuery => throw _privateConstructorUsedError;
bool get realTunEnable => throw _privateConstructorUsedError;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@@ -68,7 +69,8 @@ abstract class $AppStateCopyWith<$Res> {
FixedList<Log> logs,
FixedList<Traffic> traffics,
Traffic totalTraffic,
String proxiesQuery});
String proxiesQuery,
bool realTunEnable});
}
/// @nodoc
@@ -105,6 +107,7 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
Object? traffics = null,
Object? totalTraffic = null,
Object? proxiesQuery = null,
Object? realTunEnable = null,
}) {
return _then(_value.copyWith(
isInit: null == isInit
@@ -183,6 +186,10 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
? _value.proxiesQuery
: proxiesQuery // ignore: cast_nullable_to_non_nullable
as String,
realTunEnable: null == realTunEnable
? _value.realTunEnable
: realTunEnable // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@@ -214,7 +221,8 @@ abstract class _$$AppStateImplCopyWith<$Res>
FixedList<Log> logs,
FixedList<Traffic> traffics,
Traffic totalTraffic,
String proxiesQuery});
String proxiesQuery,
bool realTunEnable});
}
/// @nodoc
@@ -249,6 +257,7 @@ class __$$AppStateImplCopyWithImpl<$Res>
Object? traffics = null,
Object? totalTraffic = null,
Object? proxiesQuery = null,
Object? realTunEnable = null,
}) {
return _then(_$AppStateImpl(
isInit: null == isInit
@@ -327,6 +336,10 @@ class __$$AppStateImplCopyWithImpl<$Res>
? _value.proxiesQuery
: proxiesQuery // ignore: cast_nullable_to_non_nullable
as String,
realTunEnable: null == realTunEnable
? _value.realTunEnable
: realTunEnable // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@@ -353,7 +366,8 @@ class _$AppStateImpl implements _AppState {
required this.logs,
required this.traffics,
required this.totalTraffic,
this.proxiesQuery = ""})
this.proxiesQuery = "",
this.realTunEnable = false})
: _packages = packages,
_delayMap = delayMap,
_groups = groups,
@@ -431,10 +445,13 @@ class _$AppStateImpl implements _AppState {
@override
@JsonKey()
final String proxiesQuery;
@override
@JsonKey()
final bool realTunEnable;
@override
String toString() {
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, proxiesQuery: $proxiesQuery)';
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, proxiesQuery: $proxiesQuery, realTunEnable: $realTunEnable)';
}
@override
@@ -470,7 +487,9 @@ class _$AppStateImpl implements _AppState {
(identical(other.totalTraffic, totalTraffic) ||
other.totalTraffic == totalTraffic) &&
(identical(other.proxiesQuery, proxiesQuery) ||
other.proxiesQuery == proxiesQuery));
other.proxiesQuery == proxiesQuery) &&
(identical(other.realTunEnable, realTunEnable) ||
other.realTunEnable == realTunEnable));
}
@override
@@ -494,7 +513,8 @@ class _$AppStateImpl implements _AppState {
logs,
traffics,
totalTraffic,
proxiesQuery
proxiesQuery,
realTunEnable
]);
/// Create a copy of AppState
@@ -526,7 +546,8 @@ abstract class _AppState implements AppState {
required final FixedList<Log> logs,
required final FixedList<Traffic> traffics,
required final Traffic totalTraffic,
final String proxiesQuery}) = _$AppStateImpl;
final String proxiesQuery,
final bool realTunEnable}) = _$AppStateImpl;
@override
bool get isInit;
@@ -566,6 +587,8 @@ abstract class _AppState implements AppState {
Traffic get totalTraffic;
@override
String get proxiesQuery;
@override
bool get realTunEnable;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.

View File

@@ -1325,6 +1325,7 @@ mixin _$NetworkProps {
bool get systemProxy => throw _privateConstructorUsedError;
List<String> get bypassDomain => throw _privateConstructorUsedError;
RouteMode get routeMode => throw _privateConstructorUsedError;
bool get autoSetSystemDns => throw _privateConstructorUsedError;
/// Serializes this NetworkProps to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -1342,7 +1343,11 @@ abstract class $NetworkPropsCopyWith<$Res> {
NetworkProps value, $Res Function(NetworkProps) then) =
_$NetworkPropsCopyWithImpl<$Res, NetworkProps>;
@useResult
$Res call({bool systemProxy, List<String> bypassDomain, RouteMode routeMode});
$Res call(
{bool systemProxy,
List<String> bypassDomain,
RouteMode routeMode,
bool autoSetSystemDns});
}
/// @nodoc
@@ -1363,6 +1368,7 @@ class _$NetworkPropsCopyWithImpl<$Res, $Val extends NetworkProps>
Object? systemProxy = null,
Object? bypassDomain = null,
Object? routeMode = null,
Object? autoSetSystemDns = null,
}) {
return _then(_value.copyWith(
systemProxy: null == systemProxy
@@ -1377,6 +1383,10 @@ class _$NetworkPropsCopyWithImpl<$Res, $Val extends NetworkProps>
? _value.routeMode
: routeMode // ignore: cast_nullable_to_non_nullable
as RouteMode,
autoSetSystemDns: null == autoSetSystemDns
? _value.autoSetSystemDns
: autoSetSystemDns // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@@ -1389,7 +1399,11 @@ abstract class _$$NetworkPropsImplCopyWith<$Res>
__$$NetworkPropsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({bool systemProxy, List<String> bypassDomain, RouteMode routeMode});
$Res call(
{bool systemProxy,
List<String> bypassDomain,
RouteMode routeMode,
bool autoSetSystemDns});
}
/// @nodoc
@@ -1408,6 +1422,7 @@ class __$$NetworkPropsImplCopyWithImpl<$Res>
Object? systemProxy = null,
Object? bypassDomain = null,
Object? routeMode = null,
Object? autoSetSystemDns = null,
}) {
return _then(_$NetworkPropsImpl(
systemProxy: null == systemProxy
@@ -1422,6 +1437,10 @@ class __$$NetworkPropsImplCopyWithImpl<$Res>
? _value.routeMode
: routeMode // ignore: cast_nullable_to_non_nullable
as RouteMode,
autoSetSystemDns: null == autoSetSystemDns
? _value.autoSetSystemDns
: autoSetSystemDns // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@@ -1432,7 +1451,8 @@ class _$NetworkPropsImpl implements _NetworkProps {
const _$NetworkPropsImpl(
{this.systemProxy = true,
final List<String> bypassDomain = defaultBypassDomain,
this.routeMode = RouteMode.bypassPrivate})
this.routeMode = RouteMode.config,
this.autoSetSystemDns = true})
: _bypassDomain = bypassDomain;
factory _$NetworkPropsImpl.fromJson(Map<String, dynamic> json) =>
@@ -1453,10 +1473,13 @@ class _$NetworkPropsImpl implements _NetworkProps {
@override
@JsonKey()
final RouteMode routeMode;
@override
@JsonKey()
final bool autoSetSystemDns;
@override
String toString() {
return 'NetworkProps(systemProxy: $systemProxy, bypassDomain: $bypassDomain, routeMode: $routeMode)';
return 'NetworkProps(systemProxy: $systemProxy, bypassDomain: $bypassDomain, routeMode: $routeMode, autoSetSystemDns: $autoSetSystemDns)';
}
@override
@@ -1469,13 +1492,19 @@ class _$NetworkPropsImpl implements _NetworkProps {
const DeepCollectionEquality()
.equals(other._bypassDomain, _bypassDomain) &&
(identical(other.routeMode, routeMode) ||
other.routeMode == routeMode));
other.routeMode == routeMode) &&
(identical(other.autoSetSystemDns, autoSetSystemDns) ||
other.autoSetSystemDns == autoSetSystemDns));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, systemProxy,
const DeepCollectionEquality().hash(_bypassDomain), routeMode);
int get hashCode => Object.hash(
runtimeType,
systemProxy,
const DeepCollectionEquality().hash(_bypassDomain),
routeMode,
autoSetSystemDns);
/// Create a copy of NetworkProps
/// with the given fields replaced by the non-null parameter values.
@@ -1497,7 +1526,8 @@ abstract class _NetworkProps implements NetworkProps {
const factory _NetworkProps(
{final bool systemProxy,
final List<String> bypassDomain,
final RouteMode routeMode}) = _$NetworkPropsImpl;
final RouteMode routeMode,
final bool autoSetSystemDns}) = _$NetworkPropsImpl;
factory _NetworkProps.fromJson(Map<String, dynamic> json) =
_$NetworkPropsImpl.fromJson;
@@ -1508,6 +1538,8 @@ abstract class _NetworkProps implements NetworkProps {
List<String> get bypassDomain;
@override
RouteMode get routeMode;
@override
bool get autoSetSystemDns;
/// Create a copy of NetworkProps
/// with the given fields replaced by the non-null parameter values.

View File

@@ -160,7 +160,8 @@ _$NetworkPropsImpl _$$NetworkPropsImplFromJson(Map<String, dynamic> json) =>
.toList() ??
defaultBypassDomain,
routeMode: $enumDecodeNullable(_$RouteModeEnumMap, json['routeMode']) ??
RouteMode.bypassPrivate,
RouteMode.config,
autoSetSystemDns: json['autoSetSystemDns'] as bool? ?? true,
);
Map<String, dynamic> _$$NetworkPropsImplToJson(_$NetworkPropsImpl instance) =>
@@ -168,6 +169,7 @@ Map<String, dynamic> _$$NetworkPropsImplToJson(_$NetworkPropsImpl instance) =>
'systemProxy': instance.systemProxy,
'bypassDomain': instance.bypassDomain,
'routeMode': _$RouteModeEnumMap[instance.routeMode]!,
'autoSetSystemDns': instance.autoSetSystemDns,
};
const _$RouteModeEnumMap = {

View File

@@ -8,6 +8,21 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'generated/app.g.dart';
@riverpod
class RealTunEnable extends _$RealTunEnable with AutoDisposeNotifierMixin {
@override
bool build() {
return globalState.appState.realTunEnable;
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
realTunEnable: value,
);
}
}
@riverpod
class Logs extends _$Logs with AutoDisposeNotifierMixin {
@override

View File

@@ -70,6 +70,22 @@ final viewHeightProvider = AutoDisposeProvider<double>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef ViewHeightRef = AutoDisposeProviderRef<double>;
String _$realTunEnableHash() => r'a4e995c86deca4c8307966470e69d93d64a40df6';
/// See also [RealTunEnable].
@ProviderFor(RealTunEnable)
final realTunEnableProvider =
AutoDisposeNotifierProvider<RealTunEnable, bool>.internal(
RealTunEnable.new,
name: r'realTunEnableProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$realTunEnableHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$RealTunEnable = AutoDisposeNotifier<bool>;
String _$logsHash() => r'56fb8aa9d62a97b026b749d204576a7384084737';
/// See also [Logs].

View File

@@ -94,7 +94,7 @@ final coreStateProvider = AutoDisposeProvider<CoreState>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef CoreStateRef = AutoDisposeProviderRef<CoreState>;
String _$updateParamsHash() => r'79fd7a5a8650fabac3a2ca7ce903c1d9eb363ed2';
String _$updateParamsHash() => r'012df72ab0e769a51c573f4692031506d7b1f1b4';
/// See also [updateParams].
@ProviderFor(updateParams)
@@ -126,7 +126,7 @@ final proxyStateProvider = AutoDisposeProvider<ProxyState>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef ProxyStateRef = AutoDisposeProviderRef<ProxyState>;
String _$trayStateHash() => r'39ff84c50ad9c9cc666fa2538fe13ec0d7236b2e';
String _$trayStateHash() => r'61c99bbae2cb7ed69dc9ee0f2149510eb6a87df4';
/// See also [trayState].
@ProviderFor(trayState)
@@ -1975,11 +1975,12 @@ class _GenColorSchemeProviderElement
bool get ignoreConfig => (origin as GenColorSchemeProvider).ignoreConfig;
}
String _$needSetupHash() => r'db01ec73ea3232c99d1c5445c80e31b98785f416';
String _$needSetupHash() => r'3668e8dc9f40a9bea45c94321804eb3afa0e7c51';
/// See also [needSetup].
@ProviderFor(needSetup)
final needSetupProvider = AutoDisposeProvider<VM3>.internal(
final needSetupProvider =
AutoDisposeProvider<VM3<String?, String?, Dns?>>.internal(
needSetup,
name: r'needSetupProvider',
debugGetCreateSourceHash:
@@ -1990,7 +1991,26 @@ final needSetupProvider = AutoDisposeProvider<VM3>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef NeedSetupRef = AutoDisposeProviderRef<VM3>;
typedef NeedSetupRef = AutoDisposeProviderRef<VM3<String?, String?, Dns?>>;
String _$autoSetSystemDnsStateHash() =>
r'2e0976e079100325b1ca797285df48a94c2c066c';
/// See also [autoSetSystemDnsState].
@ProviderFor(autoSetSystemDnsState)
final autoSetSystemDnsStateProvider =
AutoDisposeProvider<VM2<bool, bool>>.internal(
autoSetSystemDnsState,
name: r'autoSetSystemDnsStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$autoSetSystemDnsStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef AutoSetSystemDnsStateRef = AutoDisposeProviderRef<VM2<bool, bool>>;
String _$profileOverrideStateHash() =>
r'fa26570a355ab39e27b1f93d1d2f358717065592';

View File

@@ -105,10 +105,15 @@ CoreState coreState(Ref ref) {
@riverpod
UpdateParams updateParams(Ref ref) {
final routeMode = ref.watch(
networkSettingProvider.select(
(state) => state.routeMode,
),
);
return ref.watch(
patchClashConfigProvider.select(
(state) => UpdateParams(
tun: state.tun,
tun: state.tun.getRealTun(routeMode),
allowLan: state.allowLan,
findProcessMode: state.findProcessMode,
mode: state.mode,
@@ -153,9 +158,11 @@ TrayState trayState(Ref ref) {
final appSetting = ref.watch(
appSettingProvider,
);
final groups = ref.watch(
groupsProvider,
);
final groups = ref
.watch(
currentGroupsStateProvider,
)
.value;
final brightness = ref.watch(
appBrightnessProvider,
);
@@ -622,7 +629,7 @@ ColorScheme genColorScheme(
}
@riverpod
VM3 needSetup(Ref ref) {
VM3<String?, String?, Dns?> needSetup(Ref ref) {
final profileId = ref.watch(currentProfileIdProvider);
final content = ref.watch(
scriptStateProvider.select((state) => state.currentScript?.content));
@@ -638,3 +645,18 @@ VM3 needSetup(Ref ref) {
c: dns,
);
}
@riverpod
VM2<bool, bool> autoSetSystemDnsState(Ref ref) {
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
final realTunEnable = ref.watch(realTunEnableProvider);
final autoSetSystemDns = ref.watch(
networkSettingProvider.select(
(state) => state.autoSetSystemDns,
),
);
return VM2(
a: isStart ? realTunEnable : false,
b: autoSetSystemDns,
);
}

View File

@@ -315,13 +315,8 @@ class GlobalState {
final profileId = profile.id;
final configMap = await getProfileConfig(profileId);
final rawConfig = await handleEvaluate(configMap);
final routeAddress =
config.networkProps.routeMode == RouteMode.bypassPrivate
? defaultBypassPrivateRouteAddress
: patchConfig.tun.routeAddress;
final realPatchConfig = patchConfig.copyWith.tun(
autoRoute: routeAddress.isEmpty ? true : false,
routeAddress: routeAddress,
final realPatchConfig = patchConfig.copyWith(
tun: patchConfig.tun.getRealTun(config.networkProps.routeMode),
);
rawConfig["external-controller"] = realPatchConfig.externalController.value;
rawConfig["external-ui"] = "";
@@ -406,21 +401,23 @@ class GlobalState {
for (final host in realPatchConfig.hosts.entries) {
rawConfig["hosts"][host.key] = host.value.splitByMultipleSeparators;
}
if (rawConfig["dns"] == null) {
rawConfig["dns"] = {};
}
final isEnableDns = rawConfig["dns"]["enable"] == true;
final overrideDns = globalState.config.overrideDns;
if (overrideDns) {
rawConfig["dns"] = realPatchConfig.dns.toJson();
if (overrideDns || !isEnableDns) {
final dns = switch (!isEnableDns) {
true => realPatchConfig.dns.copyWith(
nameserver: [...realPatchConfig.dns.nameserver, "system://"]),
false => realPatchConfig.dns,
};
rawConfig["dns"] = dns.toJson();
rawConfig["dns"]["nameserver-policy"] = {};
for (final entry in realPatchConfig.dns.nameserverPolicy.entries) {
for (final entry in dns.nameserverPolicy.entries) {
rawConfig["dns"]["nameserver-policy"][entry.key] =
entry.value.splitByMultipleSeparators;
}
} else {
if (rawConfig["dns"] == null) {
rawConfig["dns"] = {};
}
if (rawConfig["dns"]["enable"] != false) {
rawConfig["dns"]["enable"] = true;
}
}
var rules = [];
if (rawConfig["rules"] != null) {

View File

@@ -155,6 +155,29 @@ class Ipv6Item extends ConsumerWidget {
}
}
class AutoSetSystemDnsItem extends ConsumerWidget {
const AutoSetSystemDnsItem({super.key});
@override
Widget build(BuildContext context, ref) {
final autoSetSystemDns = ref.watch(
networkSettingProvider.select((state) => state.autoSetSystemDns));
return ListItem.switchItem(
title: Text(appLocalizations.autoSetSystemDns),
delegate: SwitchDelegate(
value: autoSetSystemDns,
onChanged: (bool value) async {
ref.read(networkSettingProvider.notifier).updateState(
(state) => state.copyWith(
autoSetSystemDns: value,
),
);
},
),
);
}
}
class TunStackItem extends ConsumerWidget {
const TunStackItem({super.key});
@@ -349,9 +372,12 @@ final networkItems = [
title: appLocalizations.options,
items: [
if (system.isDesktop) const TUNItem(),
if (Platform.isMacOS) const AutoSetSystemDnsItem(),
const TunStackItem(),
const RouteModeItem(),
const RouteAddressItem(),
if (!system.isDesktop) ...[
const RouteModeItem(),
const RouteAddressItem(),
]
],
),
];

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/views/config/network.dart';
@@ -23,6 +25,7 @@ class TUNButton extends StatelessWidget {
generateSection(
items: [
if (system.isDesktop) const TUNItem(),
if (Platform.isMacOS) const AutoSetSystemDnsItem(),
const TunStackItem(),
],
),

View File

@@ -1,4 +1,3 @@
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
@@ -27,7 +26,7 @@ class _OverrideProfileViewState extends State<OverrideProfileView> {
_initState(WidgetRef ref) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(Duration(milliseconds: 300), () async {
final rawConfig = await clashCore.getConfig(widget.profileId);
final rawConfig = await globalState.getProfileConfig(widget.profileId);
final snippet = ClashConfigSnippet.fromJson(rawConfig);
final overrideData = ref.read(
getProfileOverrideDataProvider(widget.profileId),
@@ -598,7 +597,7 @@ class RuleContent extends ConsumerWidget {
tag: CacheTag.rules,
itemBuilder: (context, index) {
final rule = rules[index];
return ReorderableDragStartListener(
return ReorderableDelayedDragStartListener(
key: ObjectKey(rule),
index: index,
child: _buildItem(

View File

@@ -288,7 +288,7 @@ class ListInputPage extends StatelessWidget {
final e = items[index];
return _InputItem(
key: ValueKey(e),
ReorderableDragStartListener(
ReorderableDelayedDragStartListener(
index: index,
child: CommonCard(
child: ListItem(
@@ -440,7 +440,7 @@ class MapInputPage extends StatelessWidget {
final e = items[index];
return _InputItem(
key: ValueKey(e.key),
ReorderableDragStartListener(
ReorderableDelayedDragStartListener(
index: index,
child: CommonCard(
child: ListItem(
@@ -613,7 +613,7 @@ class _InputItem extends StatelessWidget {
color: Colors.transparent,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16),
margin: EdgeInsets.symmetric(vertical: 4),
margin: EdgeInsets.symmetric(vertical: 8),
child: child,
),
);

View File

@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.85+202506062
version: 0.8.86+202506141
environment:
sdk: '>=3.1.0 <4.0.0'