Add DNS override

Fixed some bugs
Optimize more detail
This commit is contained in:
chen08209
2024-08-26 20:44:30 +08:00
parent 3783c3c650
commit aca4a3e979
59 changed files with 4053 additions and 1000 deletions

BIN
assets/fonts/Icons.ttf Normal file

Binary file not shown.

View File

@@ -39,6 +39,7 @@ type ConfigExtendedParams struct {
IsCompatible bool `json:"is-compatible"` IsCompatible bool `json:"is-compatible"`
SelectedMap map[string]string `json:"selected-map"` SelectedMap map[string]string `json:"selected-map"`
TestURL *string `json:"test-url"` TestURL *string `json:"test-url"`
OverrideDns bool `json:"override-dns"`
} }
type GenerateConfigParams struct { type GenerateConfigParams struct {
@@ -380,6 +381,12 @@ func generateProxyGroupAndRule(proxyGroup *[]map[string]any, rule *[]string) {
*rule = computedRule *rule = computedRule
} }
func genHosts(hosts, patchHosts map[string]any) {
for k, v := range patchHosts {
hosts[k] = v
}
}
func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig) { func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig) {
targetConfig.ExternalController = patchConfig.ExternalController targetConfig.ExternalController = patchConfig.ExternalController
targetConfig.ExternalUI = "" targetConfig.ExternalUI = ""
@@ -387,7 +394,6 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.ExternalUIURL = "" targetConfig.ExternalUIURL = ""
targetConfig.TCPConcurrent = patchConfig.TCPConcurrent targetConfig.TCPConcurrent = patchConfig.TCPConcurrent
targetConfig.UnifiedDelay = patchConfig.UnifiedDelay targetConfig.UnifiedDelay = patchConfig.UnifiedDelay
//targetConfig.GeodataMode = false
targetConfig.IPv6 = patchConfig.IPv6 targetConfig.IPv6 = patchConfig.IPv6
targetConfig.LogLevel = patchConfig.LogLevel targetConfig.LogLevel = patchConfig.LogLevel
targetConfig.Port = 0 targetConfig.Port = 0
@@ -405,7 +411,11 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.Profile.StoreSelected = false targetConfig.Profile.StoreSelected = false
targetConfig.GeoXUrl = patchConfig.GeoXUrl targetConfig.GeoXUrl = patchConfig.GeoXUrl
targetConfig.GlobalUA = patchConfig.GlobalUA targetConfig.GlobalUA = patchConfig.GlobalUA
if targetConfig.DNS.Enable == false { //if targetConfig.DNS.Enable == false {
// targetConfig.DNS = patchConfig.DNS
//}
genHosts(targetConfig.Hosts, patchConfig.Hosts)
if configParams.OverrideDns {
targetConfig.DNS = patchConfig.DNS targetConfig.DNS = patchConfig.DNS
} }
//if runtime.GOOS == "android" { //if runtime.GOOS == "android" {
@@ -413,11 +423,11 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
//} else if runtime.GOOS == "windows" { //} else if runtime.GOOS == "windows" {
// targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, dns.SystemDNSPlaceholder) // targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, dns.SystemDNSPlaceholder)
//} //}
if configParams.IsCompatible == false { //if configParams.IsCompatible == false {
targetConfig.ProxyProvider = make(map[string]map[string]any) // targetConfig.ProxyProvider = make(map[string]map[string]any)
targetConfig.RuleProvider = make(map[string]map[string]any) // targetConfig.RuleProvider = make(map[string]map[string]any)
generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule) // generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule)
} //}
} }
func patchConfig(general *config.General) { func patchConfig(general *config.General) {
@@ -440,6 +450,11 @@ var isRunning = false
var runLock sync.Mutex var runLock sync.Mutex
func updateListeners(general *config.General, listeners map[string]constant.InboundListener) { func updateListeners(general *config.General, listeners map[string]constant.InboundListener) {
if !isRunning {
return
}
runLock.Lock()
defer runLock.Unlock()
listener.PatchInboundListeners(listeners, tunnel.Tunnel, true) listener.PatchInboundListeners(listeners, tunnel.Tunnel, true)
listener.SetAllowLan(general.AllowLan) listener.SetAllowLan(general.AllowLan)
inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes) inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)
@@ -525,10 +540,8 @@ func applyConfig() error {
hub.UltraApplyConfig(cfg) hub.UltraApplyConfig(cfg)
patchSelectGroup() patchSelectGroup()
} }
if isRunning {
updateListeners(cfg.General, cfg.Listeners) updateListeners(cfg.General, cfg.Listeners)
hcCompatibleProvider(cfg.Providers) hcCompatibleProvider(cfg.Providers)
}
externalProviders = getExternalProvidersRaw() externalProviders = getExternalProvidersRaw()
return err return err
} }

View File

@@ -48,9 +48,11 @@ func start() {
//export stop //export stop
func stop() { func stop() {
runLock.Lock() runLock.Lock()
go func() {
defer runLock.Unlock() defer runLock.Unlock()
isRunning = false isRunning = false
stopListeners() stopListeners()
}()
} }
//export initClash //export initClash
@@ -236,6 +238,7 @@ func asyncTestDelay(s *C.char, port C.longlong) {
proxies := tunnel.ProxiesWithProviders() proxies := tunnel.ProxiesWithProviders()
proxy := proxies[params.ProxyName] proxy := proxies[params.ProxyName]
proxy.Name()
delayData := &Delay{ delayData := &Delay{
Name: params.ProxyName, Name: params.ProxyName,

View File

@@ -109,6 +109,17 @@ class ApplicationState extends State<Application> {
); );
} }
_buildPage(Widget page) {
if (system.isDesktop) {
return WindowHeaderContainer(
child: page,
);
}
return VpnContainer(
child: page,
);
}
_updateSystemColorSchemes( _updateSystemColorSchemes(
ColorScheme? lightDynamic, ColorScheme? lightDynamic,
ColorScheme? darkDynamic, ColorScheme? darkDynamic,
@@ -147,10 +158,7 @@ class ApplicationState extends State<Application> {
GlobalWidgetsLocalizations.delegate GlobalWidgetsLocalizations.delegate
], ],
builder: (_, child) { builder: (_, child) {
if (system.isDesktop) { return _buildPage(child!);
return WindowHeaderContainer(child: child!);
}
return child!;
}, },
scrollBehavior: BaseScrollBehavior(), scrollBehavior: BaseScrollBehavior(),
title: appName, title: appName,

View File

@@ -26,3 +26,5 @@ export 'measure.dart';
export 'windows.dart'; export 'windows.dart';
export 'iterable.dart'; export 'iterable.dart';
export 'scroll.dart'; export 'scroll.dart';
export 'icons.dart';
export 'http.dart';

19
lib/common/http.dart Normal file
View File

@@ -0,0 +1,19 @@
import 'dart:io';
import '../state.dart';
class FlClashHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
final client = super.createHttpClient(context);
client.badCertificateCallback = (_, __, ___) => true;
client.findProxy = (url) {
final port = globalState.appController.clashConfig.mixedPort;
final isStart = globalState.appController.appState.isStart;
if(!isStart) return "DIRECT";
return "PROXY localhost:$port;DIRECT";
};
return client;
}
}

6
lib/common/icons.dart Normal file
View File

@@ -0,0 +1,6 @@
import 'package:flutter/material.dart';
class IconsExt{
static const IconData target =
IconData(0xe900, fontFamily: "Icons");
}

View File

@@ -6,6 +6,7 @@ import 'dart:typed_data';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lpinyin/lpinyin.dart';
import 'package:zxing2/qrcode.dart'; import 'package:zxing2/qrcode.dart';
import 'package:image/image.dart' as img; import 'package:image/image.dart' as img;
@@ -130,6 +131,12 @@ class Other {
return build1.compareTo(build2); return build1.compareTo(build2);
} }
String getPinyin(String value) {
return value.isNotEmpty
? PinyinHelper.getFirstWordPinyin(value.substring(0, 1))
: "";
}
Future<String?> parseQRCode(Uint8List? bytes) { Future<String?> parseQRCode(Uint8List? bytes) {
return Isolate.run<String?>(() { return Isolate.run<String?>(() {
if (bytes == null) return null; if (bytes == null) return null;

View File

@@ -1,50 +1,27 @@
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/ip.dart'; import 'package:fl_clash/models/ip.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/cupertino.dart';
class Request { class Request {
late final Dio _dio; late final Dio _dio;
int? _port; String? userAgent;
bool _isStart = false;
Request() { Request() {
_dio = Dio(); _dio = Dio();
_dio.interceptors.add( _dio.interceptors.add(
InterceptorsWrapper( InterceptorsWrapper(
onRequest: (options, handler) { onRequest: (options, handler) {
_updateAdapter();
return handler.next(options); // 继续请求 return handler.next(options); // 继续请求
}, },
), ),
); );
} }
_updateAdapter() {
final port = globalState.appController.clashConfig.mixedPort;
final isStart = globalState.appController.appState.isStart;
if (_port != port || isStart != _isStart) {
_port = port;
_isStart = isStart;
_dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
if (!_isStart) return client;
client.userAgent = globalState.appController.clashConfig.globalUa;
client.findProxy = (url) {
return "PROXY localhost:$_port;DIRECT";
};
return client;
},
validateCertificate: (_, __, ___) => true,
);
}
}
Future<Response> getFileResponseForUrl(String url) async { Future<Response> getFileResponseForUrl(String url) async {
final response = await _dio final response = await _dio
.get( .get(
@@ -62,6 +39,19 @@ class Request {
return response; return response;
} }
Future<MemoryImage?> getImage(String url) async {
if (url.isEmpty) return null;
final response = await _dio.get<Uint8List>(
url,
options: Options(
responseType: ResponseType.bytes,
),
);
final data = response.data;
if (data == null) return null;
return MemoryImage(data);
}
Future<Map<String, dynamic>?> checkForUpdate() async { Future<Map<String, dynamic>?> checkForUpdate() async {
final response = await _dio.get( final response = await _dio.get(
"https://api.github.com/repos/$repository/releases/latest", "https://api.github.com/repos/$repository/releases/latest",
@@ -101,7 +91,7 @@ class Request {
return source.value(response.data!); return source.value(response.data!);
} }
} catch (e) { } catch (e) {
if(cancelToken?.isCancelled == true){ if (cancelToken?.isCancelled == true) {
throw "cancelled"; throw "cancelled";
} }
continue; continue;

View File

@@ -34,7 +34,7 @@ class Window {
// await windowManager.setTitleBarStyle(TitleBarStyle.hidden); // await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
// } // }
await windowManager.waitUntilReadyToShow(windowOptions, () async { await windowManager.waitUntilReadyToShow(windowOptions, () async {
// await windowManager.setPreventClose(true); await windowManager.setPreventClose(true);
}); });
} }

View File

@@ -125,6 +125,10 @@ class AppController {
); );
} }
updateTray(){
}
Future applyProfile({bool isPrue = false}) async { Future applyProfile({bool isPrue = false}) async {
if (isPrue) { if (isPrue) {
await globalState.applyProfile( await globalState.applyProfile(
@@ -232,6 +236,7 @@ class AppController {
handleExit() async { handleExit() async {
await updateStatus(false); await updateStatus(false);
await proxy?.stopProxy();
await savePreferences(); await savePreferences();
clashCore.shutdown(); clashCore.shutdown();
system.exit(); system.exit();
@@ -433,8 +438,8 @@ class AppController {
return List.of(proxies) return List.of(proxies)
..sort( ..sort(
(a, b) => other.sortByChar( (a, b) => other.sortByChar(
PinyinHelper.getPinyin(a.name), other.getPinyin(a.name),
PinyinHelper.getPinyin(b.name), other.getPinyin(b.name),
), ),
); );
} }

View File

@@ -1,5 +1,7 @@
// ignore_for_file: constant_identifier_names // ignore_for_file: constant_identifier_names
import 'package:freezed_annotation/freezed_annotation.dart';
enum GroupType { Selector, URLTest, Fallback, LoadBalance, Relay } enum GroupType { Selector, URLTest, Fallback, LoadBalance, Relay }
enum GroupName { GLOBAL, Proxy, Auto, Fallback } enum GroupName { GLOBAL, Proxy, Auto, Fallback }
@@ -86,6 +88,17 @@ enum CommonCardType { plain, filled }
enum ProxiesType { tab, list } enum ProxiesType { tab, list }
enum ProxiesLayout{ loose, standard, tight } enum ProxiesLayout { loose, standard, tight }
enum ProxyCardType { expand, shrink, min } enum ProxyCardType { expand, shrink, min }
enum DnsMode {
normal,
@JsonValue("fake-ip")
fakeIp,
@JsonValue("redir-host")
redirHost,
hosts
}

View File

@@ -1,8 +1,11 @@
import 'package:fl_clash/common/app_localizations.dart'; import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/config.dart'; import 'package:fl_clash/models/config.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart'; import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path/path.dart' show dirname, join;
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class CloseConnectionsSwitch extends StatelessWidget { class CloseConnectionsSwitch extends StatelessWidget {
@@ -55,7 +58,32 @@ class UsageSwitch extends StatelessWidget {
} }
} }
const appItems = [ class UWPLoopbackUtil extends StatelessWidget {
CloseConnectionsSwitch(), const UWPLoopbackUtil({super.key});
UsageSwitch(),
@override
Widget build(BuildContext context) {
return Selector<Config, bool>(
selector: (_, config) => config.onlyProxy,
builder: (_, onlyProxy, __) {
return ListItem(
leading: const Icon(Icons.lock_open),
title: Text(appLocalizations.loopback),
subtitle: Text(appLocalizations.loopbackDesc),
onTap: () {
windows?.runas(
'"${join(dirname(Platform.resolvedExecutable), "EnableLoopback.exe")}"',
"",
);
},
);
},
);
}
}
final appItems = [
if (Platform.isWindows) const UWPLoopbackUtil(),
const CloseConnectionsSwitch(),
const UsageSwitch(),
]; ];

View File

@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/fragments/config/app.dart'; import 'package:fl_clash/fragments/config/app.dart';
import 'package:fl_clash/fragments/config/dns.dart';
import 'package:fl_clash/fragments/config/general.dart'; import 'package:fl_clash/fragments/config/general.dart';
import 'package:fl_clash/fragments/config/vpn.dart'; import 'package:fl_clash/fragments/config/vpn.dart';
import 'package:fl_clash/widgets/widgets.dart'; import 'package:fl_clash/widgets/widgets.dart';
@@ -24,6 +25,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
leading: const Icon(Icons.settings_applications), leading: const Icon(Icons.settings_applications),
delegate: OpenDelegate( delegate: OpenDelegate(
title: appLocalizations.app, title: appLocalizations.app,
isBlur: false,
widget: generateListView( widget: generateListView(
appItems appItems
.separated( .separated(
@@ -42,14 +44,9 @@ class _ConfigFragmentState extends State<ConfigFragment> {
leading: const Icon(Icons.vpn_key), leading: const Icon(Icons.vpn_key),
delegate: OpenDelegate( delegate: OpenDelegate(
title: "VPN", title: "VPN",
isBlur: false,
widget: generateListView( widget: generateListView(
vpnItems vpnItems,
.separated(
const Divider(
height: 0,
),
)
.toList(),
), ),
), ),
), ),
@@ -60,21 +57,24 @@ class _ConfigFragmentState extends State<ConfigFragment> {
delegate: OpenDelegate( delegate: OpenDelegate(
title: appLocalizations.general, title: appLocalizations.general,
widget: generateListView( widget: generateListView(
generalItems generalItems,
.separated(
const Divider(
height: 0,
),
)
.toList(),
), ),
isBlur: false,
extendPageWidth: 360, extendPageWidth: 360,
), ),
), ),
ListItem( ListItem.open(
title: const Text("DNS"), title: const Text("DNS"),
subtitle: Text(appLocalizations.dnsDesc), subtitle: Text(appLocalizations.dnsDesc),
leading: const Icon(Icons.dns), leading: const Icon(Icons.dns),
delegate: OpenDelegate(
title: "DNS",
widget: generateListView(
dnsItems,
),
isBlur: false,
extendPageWidth: 360,
),
) )
]; ];
return generateListView( return generateListView(

View File

@@ -0,0 +1,824 @@
import 'package:collection/collection.dart';
import 'package:fl_clash/common/app_localizations.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class OverrideItem extends StatelessWidget {
const OverrideItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<Config, bool>(
selector: (_, config) => config.overrideDns,
builder: (_, override, __) {
return ListItem.switchItem(
title: Text(appLocalizations.overrideDns),
subtitle: Text(appLocalizations.overrideDnsDesc),
delegate: SwitchDelegate(
value: override,
onChanged: (bool value) async {
final config = globalState.appController.config;
config.overrideDns = value;
},
),
);
},
);
}
}
class DnsDisabledContainer extends StatelessWidget {
final Widget child;
const DnsDisabledContainer(
this.child, {
super.key,
});
@override
Widget build(BuildContext context) {
return Selector<Config, bool>(
selector: (_, config) => config.overrideDns,
builder: (_, enable, child) {
return AbsorbPointer(
absorbing: !enable,
child: DisabledMask(
status: !enable,
child: Container(
color: context.colorScheme.surface,
child: child!,
),
),
);
},
child: child,
);
}
}
class StatusItem extends StatelessWidget {
const StatusItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.dns.enable,
builder: (_, enable, __) {
return ListItem.switchItem(
title: Text(appLocalizations.status),
subtitle: Text(appLocalizations.statusDesc),
delegate: SwitchDelegate(
value: enable,
onChanged: (bool value) async {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
enable: value,
);
},
),
);
},
);
}
}
class PreferH3Item extends StatelessWidget {
const PreferH3Item({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.dns.preferH3,
builder: (_, preferH3, __) {
return ListItem.switchItem(
title: const Text("PreferH3"),
subtitle: Text(appLocalizations.preferH3Desc),
delegate: SwitchDelegate(
value: preferH3,
onChanged: (bool value) async {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
preferH3: value,
);
},
),
);
},
);
}
}
class IPv6Item extends StatelessWidget {
const IPv6Item({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.dns.ipv6,
builder: (_, ipv6, __) {
return ListItem.switchItem(
title: const Text("IPv6"),
delegate: SwitchDelegate(
value: ipv6,
onChanged: (bool value) async {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
ipv6: value,
);
},
),
);
},
);
}
}
class RespectRulesItem extends StatelessWidget {
const RespectRulesItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.dns.respectRules,
builder: (_, respectRules, __) {
return ListItem.switchItem(
title: Text(appLocalizations.respectRules),
subtitle: Text(appLocalizations.respectRulesDesc),
delegate: SwitchDelegate(
value: respectRules,
onChanged: (bool value) async {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
respectRules: value,
);
},
),
);
},
);
}
}
class DnsModeItem extends StatelessWidget {
const DnsModeItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, DnsMode>(
selector: (_, clashConfig) => clashConfig.dns.enhancedMode,
builder: (_, enhancedMode, __) {
return ListItem<DnsMode>.options(
title: Text(appLocalizations.dnsMode),
subtitle: Text(enhancedMode.name),
delegate: OptionsDelegate(
title: appLocalizations.dnsMode,
options: DnsMode.values,
onChanged: (value) {
if (value == null) {
return;
}
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(enhancedMode: value);
},
textBuilder: (dnsMode) => dnsMode.name,
value: enhancedMode,
),
);
},
);
}
}
class FakeIpRangeItem extends StatelessWidget {
const FakeIpRangeItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, String>(
selector: (_, clashConfig) => clashConfig.dns.fakeIpRange,
builder: (_, fakeIpRange, __) {
return ListItem.input(
title: Text(appLocalizations.fakeipRange),
subtitle: Text(fakeIpRange),
delegate: InputDelegate(
title: appLocalizations.fakeipRange,
value: fakeIpRange,
onChanged: (String? value) {
if (value != null) {
try {
final clashConfig = globalState.appController.clashConfig;
clashConfig.dns = clashConfig.dns.copyWith(
fakeIpRange: value,
);
} catch (e) {
globalState.showMessage(
title: appLocalizations.fakeipRange,
message: TextSpan(
text: e.toString(),
),
);
}
}
},
),
);
},
);
}
}
class FakeIpFilterItem extends StatelessWidget {
const FakeIpFilterItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.fakeipFilter),
delegate: OpenDelegate(
isBlur: false,
title: appLocalizations.fakeipFilter,
widget: Selector<ClashConfig, List<String>>(
selector: (_, clashConfig) => clashConfig.dns.fakeIpFilter,
shouldRebuild: (prev, next) =>
!const ListEquality<String>().equals(prev, next),
builder: (_, fakeIpFilter, __) {
return UpdatePage(
title: appLocalizations.fakeipFilter,
items: fakeIpFilter,
titleBuilder: (item) => Text(item),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fakeIpFilter: List.from(dns.fakeIpFilter)..remove(value),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
if (fakeIpFilter.contains(value)) return;
clashConfig.dns = dns.copyWith(
fakeIpFilter: List.from(dns.fakeIpFilter)..add(value),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class DefaultNameserverItem extends StatelessWidget {
const DefaultNameserverItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.defaultNameserver),
subtitle: Text(appLocalizations.defaultNameserverDesc),
delegate: OpenDelegate(
isBlur: false,
title: appLocalizations.defaultNameserver,
widget: Selector<ClashConfig, List<String>>(
selector: (_, clashConfig) => clashConfig.dns.defaultNameserver,
shouldRebuild: (prev, next) =>
!const ListEquality<String>().equals(prev, next),
builder: (_, defaultNameserver, __) {
return UpdatePage(
title: appLocalizations.defaultNameserver,
items: defaultNameserver,
titleBuilder: (item) => Text(item),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
defaultNameserver: List.from(dns.defaultNameserver)
..remove(value),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
if (defaultNameserver.contains(value)) return;
clashConfig.dns = dns.copyWith(
defaultNameserver: List.from(dns.defaultNameserver)
..add(value),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class NameserverItem extends StatelessWidget {
const NameserverItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.nameserver),
subtitle: Text(appLocalizations.nameserverDesc),
delegate: OpenDelegate(
title: appLocalizations.nameserver,
isBlur: false,
widget: Selector<ClashConfig, List<String>>(
selector: (_, clashConfig) => clashConfig.dns.nameserver,
shouldRebuild: (prev, next) =>
!const ListEquality<String>().equals(prev, next),
builder: (_, nameserver, __) {
return UpdatePage(
title: "域名服务器",
items: nameserver,
titleBuilder: (item) => Text(item),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
nameserver: List.from(dns.nameserver)..remove(value),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
if (nameserver.contains(value)) return;
clashConfig.dns = dns.copyWith(
nameserver: List.from(dns.nameserver)..add(value),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class UseHostsItem extends StatelessWidget {
const UseHostsItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.dns.useHosts,
builder: (_, useHosts, __) {
return ListItem.switchItem(
title: Text(appLocalizations.useHosts),
delegate: SwitchDelegate(
value: useHosts,
onChanged: (bool value) async {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
useHosts: value,
);
},
),
);
},
);
}
}
class UseSystemHostsItem extends StatelessWidget {
const UseSystemHostsItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.dns.useSystemHosts,
builder: (_, useSystemHosts, __) {
return ListItem.switchItem(
title: Text(appLocalizations.useSystemHosts),
delegate: SwitchDelegate(
value: useSystemHosts,
onChanged: (bool value) async {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
useSystemHosts: value,
);
},
),
);
},
);
}
}
class NameserverPolicyItem extends StatelessWidget {
const NameserverPolicyItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.nameserverPolicy),
subtitle: Text(appLocalizations.nameserverPolicyDesc),
delegate: OpenDelegate(
isBlur: false,
title: appLocalizations.nameserverPolicy,
widget: Selector<ClashConfig, Map<String, String>>(
selector: (_, clashConfig) => clashConfig.dns.nameserverPolicy,
shouldRebuild: (prev, next) =>
!const MapEquality<String, String>().equals(prev, next),
builder: (_, nameserverPolicy, __) {
return UpdatePage(
title: appLocalizations.nameserverPolicy,
items: nameserverPolicy.entries,
titleBuilder: (item) => Text(item.key),
subtitleBuilder: (item) => Text(item.value),
isMap: true,
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
nameserverPolicy: Map.from(dns.nameserverPolicy)
..remove(value.key),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
nameserverPolicy: Map.from(dns.nameserverPolicy)
..addEntries([value]),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class ProxyServerNameserverItem extends StatelessWidget {
const ProxyServerNameserverItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.proxyNameserver),
subtitle: Text(appLocalizations.proxyNameserverDesc),
delegate: OpenDelegate(
isBlur: false,
title: appLocalizations.proxyNameserver,
widget: Selector<ClashConfig, List<String>>(
selector: (_, clashConfig) => clashConfig.dns.proxyServerNameserver,
shouldRebuild: (prev, next) =>
!const ListEquality<String>().equals(prev, next),
builder: (_, proxyServerNameserver, __) {
return UpdatePage(
title: appLocalizations.proxyNameserver,
items: proxyServerNameserver,
titleBuilder: (item) => Text(item),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
proxyServerNameserver: List.from(dns.proxyServerNameserver)
..remove(value),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
if (proxyServerNameserver.contains(value)) return;
clashConfig.dns = dns.copyWith(
proxyServerNameserver: List.from(dns.proxyServerNameserver)
..add(value),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class FallbackItem extends StatelessWidget {
const FallbackItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.fallback),
subtitle: Text(appLocalizations.fallbackDesc),
delegate: OpenDelegate(
isBlur: false,
title: appLocalizations.fallback,
widget: Selector<ClashConfig, List<String>>(
selector: (_, clashConfig) => clashConfig.dns.fallback,
shouldRebuild: (prev, next) =>
!const ListEquality<String>().equals(prev, next),
builder: (_, fallback, __) {
return UpdatePage(
title: appLocalizations.fallback,
items: fallback,
titleBuilder: (item) => Text(item),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallback: List.from(dns.fallback)..remove(value),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
if (fallback.contains(value)) return;
clashConfig.dns = dns.copyWith(
fallback: List.from(dns.fallback)..add(value),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class GeoipItem extends StatelessWidget {
const GeoipItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.dns.fallbackFilter.geoip,
builder: (_, geoip, __) {
return ListItem.switchItem(
title: const Text("Geoip"),
delegate: SwitchDelegate(
value: geoip,
onChanged: (bool value) async {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallbackFilter: dns.fallbackFilter.copyWith(geoip: value),
);
},
),
);
},
);
}
}
class GeoipCodeItem extends StatelessWidget {
const GeoipCodeItem({super.key});
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, String>(
selector: (_, clashConfig) => clashConfig.dns.fallbackFilter.geoipCode,
builder: (_, geoipCode, __) {
return ListItem.input(
title: Text(appLocalizations.geoipCode),
subtitle: Text(geoipCode),
delegate: InputDelegate(
title: appLocalizations.geoipCode,
value: geoipCode,
onChanged: (String? value) {
if (value != null) {
try {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallbackFilter: dns.fallbackFilter.copyWith(
geoipCode: value,
),
);
} catch (e) {
globalState.showMessage(
title: appLocalizations.geoipCode,
message: TextSpan(
text: e.toString(),
),
);
}
}
},
),
);
},
);
}
}
class GeositeItem extends StatelessWidget {
const GeositeItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: const Text("Geosite"),
delegate: OpenDelegate(
isBlur: false,
title: "Geosite",
widget: Selector<ClashConfig, List<String>>(
selector: (_, clashConfig) => clashConfig.dns.fallbackFilter.geosite,
shouldRebuild: (prev, next) =>
!const ListEquality<String>().equals(prev, next),
builder: (_, geosite, __) {
return UpdatePage(
title: "Geosite",
items: geosite,
titleBuilder: (item) => Text(item),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallbackFilter: dns.fallbackFilter.copyWith(
geosite: List.from(geosite)..remove(value),
),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallbackFilter: dns.fallbackFilter.copyWith(
geosite: List.from(geosite)..add(value),
),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class IpcidrItem extends StatelessWidget {
const IpcidrItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.ipcidr),
delegate: OpenDelegate(
isBlur: false,
title: appLocalizations.ipcidr,
widget: Selector<ClashConfig, List<String>>(
selector: (_, clashConfig) => clashConfig.dns.fallbackFilter.ipcidr,
shouldRebuild: (prev, next) =>
!const ListEquality<String>().equals(prev, next),
builder: (_, ipcidr, __) {
return UpdatePage(
title: appLocalizations.ipcidr,
items: ipcidr,
titleBuilder: (item) => Text(item),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallbackFilter: dns.fallbackFilter.copyWith(
ipcidr: List.from(ipcidr)..remove(value),
),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallbackFilter: dns.fallbackFilter.copyWith(
ipcidr: List.from(ipcidr)..add(value),
),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class DomainItem extends StatelessWidget {
const DomainItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.domain),
delegate: OpenDelegate(
isBlur: false,
title: appLocalizations.domain,
widget: Selector<ClashConfig, List<String>>(
selector: (_, clashConfig) => clashConfig.dns.fallbackFilter.domain,
shouldRebuild: (prev, next) =>
!const ListEquality<String>().equals(prev, next),
builder: (_, domain, __) {
return UpdatePage(
title: appLocalizations.domain,
items: domain,
titleBuilder: (item) => Text(item),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallbackFilter: dns.fallbackFilter.copyWith(
domain: List.from(domain)..remove(value),
),
);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
final dns = clashConfig.dns;
clashConfig.dns = dns.copyWith(
fallbackFilter: dns.fallbackFilter.copyWith(
domain: List.from(domain)..add(value),
),
);
},
);
},
),
extendPageWidth: 360,
),
);
}
}
class DnsOptions extends StatelessWidget {
const DnsOptions({super.key});
@override
Widget build(BuildContext context) {
return DnsDisabledContainer(
Column(
children: generateSection(
title: appLocalizations.options,
items: [
const StatusItem(),
const UseHostsItem(),
const UseSystemHostsItem(),
const IPv6Item(),
const RespectRulesItem(),
const PreferH3Item(),
const DnsModeItem(),
const FakeIpRangeItem(),
const FakeIpFilterItem(),
const DefaultNameserverItem(),
const NameserverPolicyItem(),
const NameserverItem(),
const FallbackItem(),
const ProxyServerNameserverItem(),
],
),
),
);
}
}
class FallbackFilterOptions extends StatelessWidget {
const FallbackFilterOptions({super.key});
@override
Widget build(BuildContext context) {
return DnsDisabledContainer(
Column(
children: generateSection(
title: appLocalizations.fallbackFilter,
items: [
const GeoipItem(),
const GeoipCodeItem(),
const GeositeItem(),
const IpcidrItem(),
const DomainItem(),
],
),
),
);
}
}
const dnsItems = <Widget>[
OverrideItem(),
DnsOptions(),
FallbackFilterOptions(),
];

View File

@@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/models/models.dart';
@@ -6,184 +7,133 @@ import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class LogLevelMenu extends StatelessWidget { class LogLevelItem extends StatelessWidget {
const LogLevelMenu({super.key}); const LogLevelItem({super.key});
_showLogLevelDialog(BuildContext context, LogLevel value) {
globalState.showCommonDialog(
child: AlertDialog(
title: Text(appLocalizations.logLevel),
contentPadding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
content: SizedBox(
width: 250,
child: Wrap(
children: [
for (final logLevel in LogLevel.values)
ListItem.radio(
delegate: RadioDelegate<LogLevel>(
value: logLevel,
groupValue: value,
onChanged: (LogLevel? value) {
if (value == null) {
return;
}
final appController = globalState.appController;
appController.clashConfig.logLevel = value;
Navigator.of(context).pop();
},
),
title: Text(logLevel.name),
)
],
),
),
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<ClashConfig, LogLevel>( return Selector<ClashConfig, LogLevel>(
selector: (_, clashConfig) => clashConfig.logLevel, selector: (_, clashConfig) => clashConfig.logLevel,
builder: (_, value, __) { builder: (_, value, __) {
return ListItem( return ListItem<LogLevel>.options(
leading: const Icon(Icons.info_outline), leading: const Icon(Icons.info_outline),
title: Text(appLocalizations.logLevel), title: Text(appLocalizations.logLevel),
subtitle: Text(value.name), subtitle: Text(value.name),
onTap: () { delegate: OptionsDelegate<LogLevel>(
_showLogLevelDialog(context, value); title: appLocalizations.logLevel,
options: LogLevel.values,
onChanged: (LogLevel? value) {
if (value == null) {
return;
}
final appController = globalState.appController;
appController.clashConfig.logLevel = value;
}, },
textBuilder: (logLevel) => logLevel.name,
value: value,
),
); );
}, },
); );
} }
} }
class UaMenu extends StatelessWidget { class UaItem extends StatelessWidget {
const UaMenu({super.key}); const UaItem({super.key});
_showUaDialog(BuildContext context, String? value) {
const uas = [
null,
"clash-verge/v1.6.6",
"ClashforWindows/0.19.23",
];
globalState.showCommonDialog(
child: AlertDialog(
title: const Text("UA"),
contentPadding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
content: SizedBox(
width: 250,
child: Wrap(
children: [
for (final ua in uas)
ListItem.radio(
delegate: RadioDelegate<String?>(
value: ua,
groupValue: value,
onChanged: (String? value) {
final appController = globalState.appController;
appController.clashConfig.globalRealUa = value;
Navigator.of(context).pop();
},
),
title: Text(ua ?? appLocalizations.defaultText),
)
],
),
),
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<ClashConfig, String?>( return Selector<ClashConfig, String?>(
selector: (_, clashConfig) => clashConfig.globalRealUa, selector: (_, clashConfig) => clashConfig.globalRealUa,
builder: (_, value, __) { builder: (_, value, __) {
return ListItem( return ListItem<String?>.options(
leading: const Icon(Icons.computer_outlined), leading: const Icon(Icons.computer_outlined),
title: const Text("UA"), title: const Text("UA"),
subtitle: Text(value ?? appLocalizations.defaultText), subtitle: Text(value ?? appLocalizations.defaultText),
onTap: () { delegate: OptionsDelegate<String?>(
_showUaDialog(context, value); title: "UA",
options: [
null,
"clash-verge/v1.6.6",
"ClashforWindows/0.19.23",
],
value: value,
onChanged: (ua) {
final appController = globalState.appController;
appController.clashConfig.globalRealUa = ua;
}, },
textBuilder: (ua) => ua ?? appLocalizations.defaultText,
),
); );
}, },
); );
} }
} }
class KeepAliveIntervalInput extends StatelessWidget { class KeepAliveIntervalItem extends StatelessWidget {
const KeepAliveIntervalInput({super.key}); const KeepAliveIntervalItem({super.key});
_updateKeepAliveInterval(int keepAliveInterval) async {
final newKeepAliveIntervalString =
await globalState.showCommonDialog<String>(
child: KeepAliveIntervalFormDialog(
keepAliveInterval: keepAliveInterval,
),
);
if (newKeepAliveIntervalString != null &&
newKeepAliveIntervalString != "$keepAliveInterval") {
try {
final newKeepAliveInterval = int.parse(newKeepAliveIntervalString);
if (newKeepAliveInterval <= 0) {
throw "Invalid keepAliveInterval";
}
globalState.appController.clashConfig.keepAliveInterval =
newKeepAliveInterval;
globalState.appController.updateClashConfigDebounce();
} catch (e) {
globalState.showMessage(
title: appLocalizations.testUrl,
message: TextSpan(
text: e.toString(),
),
);
}
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<ClashConfig, int>( return Selector<ClashConfig, int>(
selector: (_, config) => config.keepAliveInterval, selector: (_, config) => config.keepAliveInterval,
builder: (_, value, __) { builder: (_, value, __) {
return ListItem( return ListItem.input(
leading: const Icon(Icons.timer_outlined), leading: const Icon(Icons.timer_outlined),
title: Text(appLocalizations.keepAliveIntervalDesc), title: Text(appLocalizations.keepAliveIntervalDesc),
subtitle: Text("$value ${appLocalizations.seconds}"), subtitle: Text("$value ${appLocalizations.seconds}"),
onTap: () { delegate: InputDelegate(
_updateKeepAliveInterval(value); title: appLocalizations.keepAliveIntervalDesc,
suffixText: appLocalizations.seconds,
value: value.toString(),
onChanged: (String? value) {
if (value != null) {
try {
final intValue = int.parse(value);
if (intValue <= 0) {
throw "Invalid keepAliveInterval";
}
globalState.appController.clashConfig.keepAliveInterval =
intValue;
} catch (e) {
globalState.showMessage(
title: appLocalizations.keepAliveIntervalDesc,
message: TextSpan(
text: e.toString(),
),
);
}
}
}, },
),
); );
}, },
); );
} }
} }
class TestUrlInput extends StatelessWidget { class TestUrlItem extends StatelessWidget {
const TestUrlInput({super.key}); const TestUrlItem({super.key});
_modifyTestUrl(String testUrl) async { @override
final newTestUrl = await globalState.showCommonDialog<String>( Widget build(BuildContext context) {
child: TestUrlFormDialog( return Selector<Config, String>(
testUrl: testUrl, selector: (_, config) => config.testUrl,
), builder: (_, value, __) {
); return ListItem.input(
if (newTestUrl != null && newTestUrl != testUrl) { leading: const Icon(Icons.timeline),
title: Text(appLocalizations.testUrl),
subtitle: Text(value),
delegate: InputDelegate(
title: appLocalizations.testUrl,
value: value,
onChanged: (String? value) {
if (value != null) {
try { try {
if (!newTestUrl.isUrl) { if (!value.isUrl) {
throw "Invalid url"; throw "Invalid url";
} }
globalState.appController.config.testUrl = newTestUrl; globalState.appController.config.testUrl = value;
} catch (e) { } catch (e) {
globalState.showMessage( globalState.showMessage(
title: appLocalizations.testUrl, title: appLocalizations.testUrl,
@@ -193,39 +143,36 @@ class TestUrlInput extends StatelessWidget {
); );
} }
} }
}
@override
Widget build(BuildContext context) {
return Selector<Config, String>(
selector: (_, config) => config.testUrl,
builder: (_, value, __) {
return ListItem(
leading: const Icon(Icons.timeline),
title: Text(appLocalizations.testUrl),
subtitle: Text(value),
onTap: () {
_modifyTestUrl(value);
}, },
),
); );
}, },
); );
} }
} }
class MixedPortInput extends StatelessWidget { class MixedPortItem extends StatelessWidget {
const MixedPortInput({super.key}); const MixedPortItem({super.key});
_modifyMixedPort(num mixedPort) async { @override
final port = await globalState.showCommonDialog( Widget build(BuildContext context) {
child: MixedPortFormDialog( return Selector<ClashConfig, int>(
mixedPort: mixedPort, selector: (_, clashConfig) => clashConfig.mixedPort,
), builder: (_, value, __) {
); return ListItem.input(
if (port != null && port != mixedPort) { leading: const Icon(Icons.adjust_outlined),
title: Text(appLocalizations.proxyPort),
subtitle: Text("$value"),
delegate: InputDelegate(
title: appLocalizations.proxyPort,
value: value.toString(),
onChanged: (String? value) {
if (value != null) {
try { try {
final mixedPort = int.parse(port); final mixedPort = int.parse(value);
if (mixedPort < 1024 || mixedPort > 49151) throw "Invalid port"; if (mixedPort < 1024 || mixedPort > 49151) {
throw "Invalid port";
}
globalState.appController.clashConfig.mixedPort = mixedPort; globalState.appController.clashConfig.mixedPort = mixedPort;
} catch (e) { } catch (e) {
globalState.showMessage( globalState.showMessage(
@@ -236,20 +183,8 @@ class MixedPortInput extends StatelessWidget {
); );
} }
} }
}
@override
Widget build(BuildContext context) {
return Selector<ClashConfig, int>(
selector: (_, clashConfig) => clashConfig.mixedPort,
builder: (_, mixedPort, __) {
return ListItem(
onTap: () {
_modifyMixedPort(mixedPort);
}, },
leading: const Icon(Icons.adjust_outlined), ),
title: Text(appLocalizations.proxyPort),
subtitle: Text("$mixedPort"),
); );
}, },
); );
@@ -264,18 +199,42 @@ class HostsItem extends StatelessWidget {
return ListItem.open( return ListItem.open(
leading: const Icon(Icons.view_list_outlined), leading: const Icon(Icons.view_list_outlined),
title: const Text("Hosts"), title: const Text("Hosts"),
subtitle: Text("编辑追加hosts"), subtitle: Text(appLocalizations.hostsDesc),
delegate: OpenDelegate( delegate: OpenDelegate(
isBlur: false,
title: "Hosts", title: "Hosts",
widget: HostsForm(), widget: Selector<ClashConfig, HostsMap>(
selector: (_, clashConfig) => clashConfig.hosts,
shouldRebuild: (prev, next) =>
!const MapEquality<String, String>().equals(prev, next),
builder: (_, hosts, ___) {
final entries = hosts.entries;
return UpdatePage(
title: "Hosts",
items: entries,
titleBuilder: (item) => Text(item.key),
subtitleBuilder: (item) => Text(item.value),
onRemove: (value) {
final clashConfig = globalState.appController.clashConfig;
clashConfig.hosts = Map.from(hosts)..remove(value.key);
},
onAdd: (value) {
final clashConfig = globalState.appController.clashConfig;
clashConfig.hosts = Map.from(clashConfig.hosts)
..addEntries([value]);
},
isMap: true,
);
},
),
extendPageWidth: 360, extendPageWidth: 360,
), ),
); );
} }
} }
class Ipv6Switch extends StatelessWidget { class Ipv6Item extends StatelessWidget {
const Ipv6Switch({super.key}); const Ipv6Item({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -299,8 +258,8 @@ class Ipv6Switch extends StatelessWidget {
} }
} }
class AllowLanSwitch extends StatelessWidget { class AllowLanItem extends StatelessWidget {
const AllowLanSwitch({super.key}); const AllowLanItem({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -324,8 +283,8 @@ class AllowLanSwitch extends StatelessWidget {
} }
} }
class UnifiedDelaySwitch extends StatelessWidget { class UnifiedDelayItem extends StatelessWidget {
const UnifiedDelaySwitch({super.key}); const UnifiedDelayItem({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -349,8 +308,8 @@ class UnifiedDelaySwitch extends StatelessWidget {
} }
} }
class FindProcessSwitch extends StatelessWidget { class FindProcessItem extends StatelessWidget {
const FindProcessSwitch({super.key}); const FindProcessItem({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -376,8 +335,8 @@ class FindProcessSwitch extends StatelessWidget {
} }
} }
class TcpConcurrentSwitch extends StatelessWidget { class TcpConcurrentItem extends StatelessWidget {
const TcpConcurrentSwitch({super.key}); const TcpConcurrentItem({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -401,8 +360,8 @@ class TcpConcurrentSwitch extends StatelessWidget {
} }
} }
class GeodataLoaderSwitch extends StatelessWidget { class GeodataLoaderItem extends StatelessWidget {
const GeodataLoaderSwitch({super.key}); const GeodataLoaderItem({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -428,8 +387,8 @@ class GeodataLoaderSwitch extends StatelessWidget {
} }
} }
class ExternalControllerSwitch extends StatelessWidget { class ExternalControllerItem extends StatelessWidget {
const ExternalControllerSwitch({super.key}); const ExternalControllerItem({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -454,331 +413,24 @@ class ExternalControllerSwitch extends StatelessWidget {
} }
} }
const generalItems = [ final generalItems = const [
LogLevelMenu(), LogLevelItem(),
UaMenu(), UaItem(),
KeepAliveIntervalInput(), KeepAliveIntervalItem(),
TestUrlInput(), TestUrlItem(),
MixedPortInput(), MixedPortItem(),
HostsItem(), HostsItem(),
Ipv6Switch(), Ipv6Item(),
AllowLanSwitch(), AllowLanItem(),
UnifiedDelaySwitch(), UnifiedDelayItem(),
FindProcessSwitch(), FindProcessItem(),
TcpConcurrentSwitch(), TcpConcurrentItem(),
GeodataLoaderSwitch(), GeodataLoaderItem(),
ExternalControllerSwitch(), ExternalControllerItem(),
]; ]
.separated(
class MixedPortFormDialog extends StatefulWidget { const Divider(
final num mixedPort; height: 0,
const MixedPortFormDialog({super.key, required this.mixedPort});
@override
State<MixedPortFormDialog> createState() => _MixedPortFormDialogState();
}
class _MixedPortFormDialogState extends State<MixedPortFormDialog> {
late TextEditingController portController;
@override
void initState() {
super.initState();
portController = TextEditingController(text: "${widget.mixedPort}");
}
_handleUpdate() async {
final port = portController.value.text;
if (port.isEmpty) return;
Navigator.of(context).pop<String>(port);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(appLocalizations.proxyPort),
content: SizedBox(
width: 300,
child: Wrap(
runSpacing: 16,
children: [
TextField(
controller: portController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
), ),
),
],
),
),
actions: [
TextButton(
onPressed: _handleUpdate,
child: Text(appLocalizations.submit),
) )
], .toList();
);
}
}
class TestUrlFormDialog extends StatefulWidget {
final String testUrl;
const TestUrlFormDialog({
super.key,
required this.testUrl,
});
@override
State<TestUrlFormDialog> createState() => _TestUrlFormDialogState();
}
class _TestUrlFormDialogState extends State<TestUrlFormDialog> {
late TextEditingController testUrlController;
@override
void initState() {
super.initState();
testUrlController = TextEditingController(text: widget.testUrl);
}
_handleUpdate() async {
final testUrl = testUrlController.value.text;
if (testUrl.isEmpty) return;
Navigator.of(context).pop<String>(testUrl);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(appLocalizations.testUrl),
content: SizedBox(
width: 300,
child: Wrap(
runSpacing: 16,
children: [
TextField(
maxLines: 5,
minLines: 1,
controller: testUrlController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
],
),
),
actions: [
TextButton(
onPressed: _handleUpdate,
child: Text(appLocalizations.submit),
)
],
);
}
}
class KeepAliveIntervalFormDialog extends StatefulWidget {
final int keepAliveInterval;
const KeepAliveIntervalFormDialog({
super.key,
required this.keepAliveInterval,
});
@override
State<KeepAliveIntervalFormDialog> createState() =>
_KeepAliveIntervalFormDialogState();
}
class _KeepAliveIntervalFormDialogState
extends State<KeepAliveIntervalFormDialog> {
late TextEditingController keepAliveIntervalController;
@override
void initState() {
super.initState();
keepAliveIntervalController = TextEditingController(
text: "${widget.keepAliveInterval}",
);
}
_handleUpdate() async {
final keepAliveInterval = keepAliveIntervalController.value.text;
if (keepAliveInterval.isEmpty) return;
Navigator.of(context).pop<String>(keepAliveInterval);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(appLocalizations.keepAliveIntervalDesc),
content: SizedBox(
width: 300,
child: Wrap(
runSpacing: 16,
children: [
TextField(
maxLines: 1,
minLines: 1,
controller: keepAliveIntervalController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
suffixText: appLocalizations.seconds,
),
),
],
),
),
actions: [
TextButton(
onPressed: _handleUpdate,
child: Text(appLocalizations.submit),
)
],
);
}
}
class HostsForm extends StatelessWidget {
const HostsForm({super.key});
@override
Widget build(BuildContext context) {
return FloatLayout(
floatingWidget: FloatWrapper(
child: FloatingActionButton(
onPressed: () async {
final entry =
await globalState.showCommonDialog<MapEntry<String, String>>(
child: const AddHostDialog(),
);
if (entry == null) return;
final clashConfig = globalState.appController.clashConfig;
clashConfig.hosts = Map.from(clashConfig.hosts)
..addEntries([entry]);
},
child: const Icon(Icons.add),
),
),
child: Selector<ClashConfig, HostsMap>(
selector: (_, clashConfig) => clashConfig.hosts,
builder: (_, hosts, ___) {
final entries = hosts.entries;
return ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: entries.length,
itemBuilder: (_, index) {
final e = entries.toList()[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: CommonCard(
child: ListItem(
title: Text(e.key),
subtitle: Text(e.value),
trailing: IconButton(
icon: const Icon(Icons.delete_outline),
onPressed: () {
final clashConfig =
globalState.appController.clashConfig;
clashConfig.hosts = Map.from(hosts)..remove(e.key);
},
),
),
onPressed: () {},
),
);
},
);
},
),
);
}
}
class AddHostDialog extends StatefulWidget {
const AddHostDialog({super.key});
@override
State<AddHostDialog> createState() => _AddHostDialogState();
}
class _AddHostDialogState extends State<AddHostDialog> {
late TextEditingController keyController;
late TextEditingController valueController;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
keyController = TextEditingController();
valueController = TextEditingController();
}
_submit() {
if (!_formKey.currentState!.validate()) return;
Navigator.of(context).pop<MapEntry<String, String>>(
MapEntry(
keyController.text,
valueController.text,
),
);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text("Hosts"),
content: Form(
key: _formKey,
child: SizedBox(
width: dialogCommonWidth,
child: Wrap(
runSpacing: 16,
children: [
TextFormField(
maxLines: 2,
minLines: 1,
controller: keyController,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.key),
border: const OutlineInputBorder(),
labelText: appLocalizations.key,
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.keyNotEmpty;
}
return null;
},
),
TextFormField(
maxLines: 3,
minLines: 1,
controller: valueController,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.label),
border: const OutlineInputBorder(),
labelText: appLocalizations.value,
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.valueNotEmpty;
}
return null;
},
),
],
),
),
),
actions: [
TextButton(
onPressed: _submit,
child: Text(appLocalizations.confirm),
)
],
);
}
}

View File

@@ -1,10 +1,64 @@
import 'package:fl_clash/common/app_localizations.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart'; import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class VPNSwitch extends StatelessWidget {
const VPNSwitch({super.key});
@override
Widget build(BuildContext context) {
return Selector<Config, bool>(
selector: (_, config) => config.vpnProps.enable,
builder: (_, enable, __) {
return ListItem.switchItem(
leading: const Icon(Icons.stacked_line_chart),
title: const Text("VPN"),
subtitle: Text(appLocalizations.vpnEnableDesc),
delegate: SwitchDelegate(
value: enable,
onChanged: (bool value) async {
final config = globalState.appController.config;
final vpnProps = config.vpnProps;
config.vpnProps = vpnProps.copyWith(
enable: value,
);
},
),
);
},
);
}
}
class VPNDisabledContainer extends StatelessWidget {
final Widget child;
const VPNDisabledContainer(
this.child, {
super.key,
});
@override
Widget build(BuildContext context) {
return Selector<Config, bool>(
selector: (_, config) => config.vpnProps.enable,
builder: (_, enable, child) {
return AbsorbPointer(
absorbing: !enable,
child: DisabledMask(
status: !enable,
child: child!,
),
);
},
child: child,
);
}
}
class AllowBypassSwitch extends StatelessWidget { class AllowBypassSwitch extends StatelessWidget {
const AllowBypassSwitch({super.key}); const AllowBypassSwitch({super.key});
@@ -61,7 +115,26 @@ class SystemProxySwitch extends StatelessWidget {
} }
} }
const vpnItems = [ class VpnOptions extends StatelessWidget {
AllowBypassSwitch(), const VpnOptions({super.key});
SystemProxySwitch(),
@override
Widget build(BuildContext context) {
return VPNDisabledContainer(
Column(
children: generateSection(
title: appLocalizations.options,
items: [
const SystemProxySwitch(),
const AllowBypassSwitch(),
],
),
),
);
}
}
final vpnItems = [
const VPNSwitch(),
const VpnOptions(),
]; ];

View File

@@ -48,11 +48,11 @@ class _DashboardFragmentState extends State<DashboardFragment> {
crossAxisCellCount: 8, crossAxisCellCount: 8,
child: NetworkSpeed(), child: NetworkSpeed(),
), ),
if (Platform.isAndroid) // if (Platform.isAndroid)
GridItem( // GridItem(
crossAxisCellCount: switchCount, // crossAxisCellCount: switchCount,
child: const VPNSwitch(), // child: const VPNSwitch(),
), // ),
if (system.isDesktop) ...[ if (system.isDesktop) ...[
GridItem( GridItem(
crossAxisCellCount: switchCount, crossAxisCellCount: switchCount,

View File

@@ -5,34 +5,34 @@ import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class VPNSwitch extends StatelessWidget { // class VPNSwitch extends StatelessWidget {
const VPNSwitch({super.key}); // const VPNSwitch({super.key});
//
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
return SwitchContainer( // return SwitchContainer(
info: const Info( // info: const Info(
label: "VPN", // label: "VPN",
iconData: Icons.stacked_line_chart, // iconData: Icons.stacked_line_chart,
), // ),
child: Selector<Config, bool>( // child: Selector<Config, bool>(
selector: (_, config) => config.vpnProps.enable, // selector: (_, config) => config.vpnProps.enable,
builder: (_, enable, __) { // builder: (_, enable, __) {
return Switch( // return Switch(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, // materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: enable, // value: enable,
onChanged: (value) { // onChanged: (value) {
final config = globalState.appController.config; // final config = globalState.appController.config;
config.vpnProps = config.vpnProps.copyWith( // config.vpnProps = config.vpnProps.copyWith(
enable: value, // enable: value,
); // );
}, // },
); // );
}, // },
), // ),
); // );
} // }
} // }
class TUNSwitch extends StatelessWidget { class TUNSwitch extends StatelessWidget {
const TUNSwitch({super.key}); const TUNSwitch({super.key});

View File

@@ -1,9 +1,11 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/card.dart'; import 'package:fl_clash/widgets/card.dart';
import 'package:fl_clash/widgets/fade_box.dart';
import 'package:fl_clash/widgets/text.dart'; import 'package:fl_clash/widgets/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -353,6 +355,8 @@ class _ListHeaderState extends State<ListHeader>
late Animation<double> _iconTurns; late Animation<double> _iconTurns;
var isLock = false; var isLock = false;
String get icon => widget.group.icon;
String get groupName => widget.group.name; String get groupName => widget.group.name;
String get groupType => widget.group.type.name; String get groupType => widget.group.type.name;
@@ -412,12 +416,44 @@ class _ListHeaderState extends State<ListHeader>
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonCard( return CommonCard(
key: widget.key, key: widget.key,
radius: 24,
type: CommonCardType.filled, type: CommonCardType.filled,
child: Container( child: Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Flexible(
child: Row(
children: [
const SizedBox(
width: 4,
),
Container(
height: 48,
width: 48,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: context.colorScheme.secondaryContainer,
borderRadius: BorderRadius.circular(16),
),
clipBehavior: Clip.antiAlias,
child: icon.isNotEmpty
? CachedNetworkImage(
imageUrl: icon,
errorWidget: (_, __, ___) => const Icon(
IconsExt.target,
size: 32,
),
)
: const Icon(
IconsExt.target,
size: 32,
),
),
const SizedBox(
width: 16,
),
Flexible( Flexible(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -448,8 +484,10 @@ class _ListHeaderState extends State<ListHeader>
builder: (currentGroupName) { builder: (currentGroupName) {
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment:
crossAxisAlignment: CrossAxisAlignment.center, MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [ children: [
if (currentGroupName.isNotEmpty) ...[ if (currentGroupName.isNotEmpty) ...[
Flexible( Flexible(
@@ -457,8 +495,8 @@ class _ListHeaderState extends State<ListHeader>
child: EmojiText( child: EmojiText(
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
" · $currentGroupName", " · $currentGroupName",
style: context style: context.textTheme
.textTheme.labelMedium?.toLight, .labelMedium?.toLight,
), ),
), ),
] ]
@@ -470,6 +508,12 @@ class _ListHeaderState extends State<ListHeader>
], ],
), ),
), ),
const SizedBox(
width: 4,
),
],
),
)
], ],
), ),
), ),

View File

@@ -68,19 +68,30 @@ class _ProvidersState extends State<Providers> {
return Selector<AppState, List<ExternalProvider>>( return Selector<AppState, List<ExternalProvider>>(
selector: (_, appState) => appState.providers, selector: (_, appState) => appState.providers,
builder: (_, providers, ___) { builder: (_, providers, ___) {
return ListView.separated( final proxyProviders =
itemBuilder: (_, index) { providers.where((item) => item.type == "Proxy").map(
return ProviderItem( (item) => ProviderItem(
provider: providers[index], provider: item,
),
); );
}, final ruleProviders =
separatorBuilder: (_, index) { providers.where((item) => item.type == "Rule").map(
return const Divider( (item) => ProviderItem(
height: 0, provider: item,
),
); );
}, final proxySection = generateSection(
itemCount: providers.length, title: appLocalizations.proxyProviders,
items: proxyProviders,
); );
final ruleSection = generateSection(
title: appLocalizations.ruleProviders,
items: ruleProviders,
);
return generateListView([
...proxySection,
...ruleSection,
]);
}, },
); );
} }

View File

@@ -33,7 +33,7 @@ class _ProxiesFragmentState extends State<ProxiesFragment> {
extendPageWidth: 360, extendPageWidth: 360,
context, context,
body: const Providers(), body: const Providers(),
title: appLocalizations.externalResources, title: appLocalizations.providers,
); );
}, },
icon: const Icon( icon: const Icon(

View File

@@ -82,44 +82,23 @@ class _ToolboxFragmentState extends State<ToolsFragment> {
builder: (_, localeString, __) { builder: (_, localeString, __) {
final subTitle = localeString ?? appLocalizations.defaultText; final subTitle = localeString ?? appLocalizations.defaultText;
final currentLocale = other.getLocaleForString(localeString); final currentLocale = other.getLocaleForString(localeString);
return ListTile( return ListItem<Locale?>.options(
leading: const Icon(Icons.language_outlined), leading: const Icon(Icons.language_outlined),
title: Text(appLocalizations.language), title: Text(appLocalizations.language),
subtitle: Text(Intl.message(subTitle)), subtitle: Text(Intl.message(subTitle)),
onTap: () { delegate: OptionsDelegate(
globalState.showCommonDialog( title: appLocalizations.language,
child: AlertDialog( options: [
title: Text(appLocalizations.language),
contentPadding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
content: SizedBox(
width: 250,
child: Wrap(
children: [
for (final locale in [
null, null,
...AppLocalizations.delegate.supportedLocales ...AppLocalizations.delegate.supportedLocales
]) ],
ListItem.radio(
delegate: RadioDelegate<Locale?>(
value: locale,
groupValue: currentLocale,
onChanged: (Locale? value) { onChanged: (Locale? value) {
final config = context.read<Config>(); final config = context.read<Config>();
config.locale = value?.toString(); config.locale = value?.toString();
Navigator.of(context).pop();
}, },
textBuilder: (locale) => _getLocaleString(locale),
value: currentLocale,
), ),
title: Text(_getLocaleString(locale)),
)
],
),
),
),
);
},
); );
}, },
), ),

View File

@@ -251,5 +251,40 @@
"key": "Key", "key": "Key",
"value": "Value", "value": "Value",
"keyNotEmpty": "The key cannot be empty", "keyNotEmpty": "The key cannot be empty",
"valueNotEmpty": "The value cannot be empty" "valueNotEmpty": "The value cannot be empty",
"hostsDesc": "Add Hosts",
"vpnTip": "Changes take effect after restarting the VPN",
"vpnEnableDesc": "Auto routes all system traffic through VpnService",
"options": "Options",
"loopback": "Loopback unlock tool",
"loopbackDesc": "Used for UWP loopback unlocking",
"providers": "Providers",
"proxyProviders": "Proxy providers",
"ruleProviders": "Rule providers",
"overrideDns": "Override Dns",
"overrideDnsDesc": "Turning it on will override the DNS options in the profile",
"status": "Status",
"statusDesc": "System DNS will be used when turned off",
"preferH3Desc": "Prioritize the use of DOH's http/3",
"respectRules": "Respect rules",
"respectRulesDesc": "DNS connection following rules, need to configure proxy-server-nameserver",
"dnsMode": "DNS mode",
"fakeipRange": "Fakeip range",
"fakeipFilter": "Fakeip filter",
"defaultNameserver": "Default nameserver",
"defaultNameserverDesc": "For resolving DNS server",
"nameserver": "Nameserver",
"nameserverDesc": "For resolving domain",
"useHosts": "Use hosts",
"useSystemHosts": "Use system hosts",
"nameserverPolicy": "Nameserver policy",
"nameserverPolicyDesc": "Specify the corresponding nameserver policy",
"proxyNameserver": "Proxy nameserver",
"proxyNameserverDesc": "Domain for resolving proxy nodes",
"fallback": "Fallback",
"fallbackDesc": "Generally use offshore DNS",
"fallbackFilter": "Fallback filter",
"geoipCode": "Geoip code",
"ipcidr": "Ipcidr",
"domain": "Domain"
} }

View File

@@ -251,5 +251,40 @@
"key": "键", "key": "键",
"value": "值", "value": "值",
"keyNotEmpty": "键不能为空", "keyNotEmpty": "键不能为空",
"valueNotEmpty": "值不能为空" "valueNotEmpty": "值不能为空",
"hostsDesc": "追加Hosts",
"vpnTip": "重启VPN后改变生效",
"vpnEnableDesc": "通过VpnService自动路由系统所有流量",
"options": "选项",
"loopback": "回环解锁工具",
"loopbackDesc": "用于UWP回环解锁",
"providers": "提供者",
"proxyProviders": "代理提供者",
"ruleProviders": "规则提供者",
"overrideDns": "覆写DNS",
"overrideDnsDesc": "开启后将覆盖配置中的DNS选项",
"status": "状态",
"statusDesc": "关闭后将使用系统DNS",
"preferH3Desc": "优先使用DOH的http/3",
"respectRules": "遵守规则",
"respectRulesDesc": "DNS连接跟随rules,需配置proxy-server-nameserver",
"dnsMode": "DNS模式",
"fakeipRange": "Fakeip范围",
"fakeipFilter": "Fakeip过滤",
"defaultNameserver": "默认域名服务器",
"defaultNameserverDesc": "用于解析DNS服务器",
"nameserver": "域名服务器",
"nameserverDesc": "用于解析域名",
"useHosts": "使用Hosts",
"useSystemHosts": "使用系统Hosts",
"nameserverPolicy": "域名服务器策略",
"nameserverPolicyDesc": "指定对应域名服务器策略",
"proxyNameserver": "代理域名服务器",
"proxyNameserverDesc": "用于解析代理节点的域名",
"fallback": "Fallback",
"fallbackDesc": "一般情况下使用境外DNS",
"fallbackFilter": "Fallback过滤",
"geoipCode": "Geoip代码",
"ipcidr": "IP/掩码",
"domain": "域名"
} }

View File

@@ -116,6 +116,10 @@ class MessageLookup extends MessageLookupByLibrary {
"dark": MessageLookupByLibrary.simpleMessage("Dark"), "dark": MessageLookupByLibrary.simpleMessage("Dark"),
"dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"), "dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"),
"days": MessageLookupByLibrary.simpleMessage("Days"), "days": MessageLookupByLibrary.simpleMessage("Days"),
"defaultNameserver":
MessageLookupByLibrary.simpleMessage("Default nameserver"),
"defaultNameserverDesc":
MessageLookupByLibrary.simpleMessage("For resolving DNS server"),
"defaultSort": MessageLookupByLibrary.simpleMessage("Sort by default"), "defaultSort": MessageLookupByLibrary.simpleMessage("Sort by default"),
"defaultText": MessageLookupByLibrary.simpleMessage("Default"), "defaultText": MessageLookupByLibrary.simpleMessage("Default"),
"delay": MessageLookupByLibrary.simpleMessage("Delay"), "delay": MessageLookupByLibrary.simpleMessage("Delay"),
@@ -132,8 +136,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Discovery a new version"), MessageLookupByLibrary.simpleMessage("Discovery a new version"),
"dnsDesc": "dnsDesc":
MessageLookupByLibrary.simpleMessage("Update DNS related settings"), MessageLookupByLibrary.simpleMessage("Update DNS related settings"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"),
"doYouWantToPass": "doYouWantToPass":
MessageLookupByLibrary.simpleMessage("Do you want to pass"), MessageLookupByLibrary.simpleMessage("Do you want to pass"),
"domain": MessageLookupByLibrary.simpleMessage("Domain"),
"download": MessageLookupByLibrary.simpleMessage("Download"), "download": MessageLookupByLibrary.simpleMessage("Download"),
"edit": MessageLookupByLibrary.simpleMessage("Edit"), "edit": MessageLookupByLibrary.simpleMessage("Edit"),
"en": MessageLookupByLibrary.simpleMessage("English"), "en": MessageLookupByLibrary.simpleMessage("English"),
@@ -153,6 +159,13 @@ class MessageLookup extends MessageLookupByLibrary {
"externalLink": MessageLookupByLibrary.simpleMessage("External link"), "externalLink": MessageLookupByLibrary.simpleMessage("External link"),
"externalResources": "externalResources":
MessageLookupByLibrary.simpleMessage("External resources"), MessageLookupByLibrary.simpleMessage("External resources"),
"fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip filter"),
"fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip range"),
"fallback": MessageLookupByLibrary.simpleMessage("Fallback"),
"fallbackDesc":
MessageLookupByLibrary.simpleMessage("Generally use offshore DNS"),
"fallbackFilter":
MessageLookupByLibrary.simpleMessage("Fallback filter"),
"file": MessageLookupByLibrary.simpleMessage("File"), "file": MessageLookupByLibrary.simpleMessage("File"),
"fileDesc": "fileDesc":
MessageLookupByLibrary.simpleMessage("Directly upload profile"), MessageLookupByLibrary.simpleMessage("Directly upload profile"),
@@ -170,9 +183,11 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Geo Low Memory Mode"), MessageLookupByLibrary.simpleMessage("Geo Low Memory Mode"),
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage( "geodataLoaderDesc": MessageLookupByLibrary.simpleMessage(
"Enabling will use the Geo low memory loader"), "Enabling will use the Geo low memory loader"),
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip code"),
"global": MessageLookupByLibrary.simpleMessage("Global"), "global": MessageLookupByLibrary.simpleMessage("Global"),
"go": MessageLookupByLibrary.simpleMessage("Go"), "go": MessageLookupByLibrary.simpleMessage("Go"),
"goDownload": MessageLookupByLibrary.simpleMessage("Go to download"), "goDownload": MessageLookupByLibrary.simpleMessage("Go to download"),
"hostsDesc": MessageLookupByLibrary.simpleMessage("Add Hosts"),
"hours": MessageLookupByLibrary.simpleMessage("Hours"), "hours": MessageLookupByLibrary.simpleMessage("Hours"),
"importFromURL": "importFromURL":
MessageLookupByLibrary.simpleMessage("Import from URL"), MessageLookupByLibrary.simpleMessage("Import from URL"),
@@ -182,6 +197,7 @@ class MessageLookup extends MessageLookupByLibrary {
"intelligentSelected": "intelligentSelected":
MessageLookupByLibrary.simpleMessage("Intelligent selection"), MessageLookupByLibrary.simpleMessage("Intelligent selection"),
"intranetIP": MessageLookupByLibrary.simpleMessage("Intranet IP"), "intranetIP": MessageLookupByLibrary.simpleMessage("Intranet IP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("Ipcidr"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage( "ipv6Desc": MessageLookupByLibrary.simpleMessage(
"When turned on it will be able to receive IPv6 traffic"), "When turned on it will be able to receive IPv6 traffic"),
"just": MessageLookupByLibrary.simpleMessage("Just"), "just": MessageLookupByLibrary.simpleMessage("Just"),
@@ -205,6 +221,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Disabling will hide the log entry"), "Disabling will hide the log entry"),
"logs": MessageLookupByLibrary.simpleMessage("Logs"), "logs": MessageLookupByLibrary.simpleMessage("Logs"),
"logsDesc": MessageLookupByLibrary.simpleMessage("Log capture records"), "logsDesc": MessageLookupByLibrary.simpleMessage("Log capture records"),
"loopback":
MessageLookupByLibrary.simpleMessage("Loopback unlock tool"),
"loopbackDesc": MessageLookupByLibrary.simpleMessage(
"Used for UWP loopback unlocking"),
"loose": MessageLookupByLibrary.simpleMessage("Loose"), "loose": MessageLookupByLibrary.simpleMessage("Loose"),
"min": MessageLookupByLibrary.simpleMessage("Min"), "min": MessageLookupByLibrary.simpleMessage("Min"),
"minimizeOnExit": "minimizeOnExit":
@@ -217,6 +237,13 @@ class MessageLookup extends MessageLookupByLibrary {
"more": MessageLookupByLibrary.simpleMessage("More"), "more": MessageLookupByLibrary.simpleMessage("More"),
"name": MessageLookupByLibrary.simpleMessage("Name"), "name": MessageLookupByLibrary.simpleMessage("Name"),
"nameSort": MessageLookupByLibrary.simpleMessage("Sort by name"), "nameSort": MessageLookupByLibrary.simpleMessage("Sort by name"),
"nameserver": MessageLookupByLibrary.simpleMessage("Nameserver"),
"nameserverDesc":
MessageLookupByLibrary.simpleMessage("For resolving domain"),
"nameserverPolicy":
MessageLookupByLibrary.simpleMessage("Nameserver policy"),
"nameserverPolicyDesc": MessageLookupByLibrary.simpleMessage(
"Specify the corresponding nameserver policy"),
"networkDetection": "networkDetection":
MessageLookupByLibrary.simpleMessage("Network detection"), MessageLookupByLibrary.simpleMessage("Network detection"),
"networkSpeed": MessageLookupByLibrary.simpleMessage("Network speed"), "networkSpeed": MessageLookupByLibrary.simpleMessage("Network speed"),
@@ -242,6 +269,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Only statistics proxy"), MessageLookupByLibrary.simpleMessage("Only statistics proxy"),
"onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage( "onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage(
"When turned on, only statistics proxy traffic"), "When turned on, only statistics proxy traffic"),
"options": MessageLookupByLibrary.simpleMessage("Options"),
"other": MessageLookupByLibrary.simpleMessage("Other"), "other": MessageLookupByLibrary.simpleMessage("Other"),
"otherContributors": "otherContributors":
MessageLookupByLibrary.simpleMessage("Other contributors"), MessageLookupByLibrary.simpleMessage("Other contributors"),
@@ -249,6 +277,9 @@ class MessageLookup extends MessageLookupByLibrary {
"override": MessageLookupByLibrary.simpleMessage("Override"), "override": MessageLookupByLibrary.simpleMessage("Override"),
"overrideDesc": MessageLookupByLibrary.simpleMessage( "overrideDesc": MessageLookupByLibrary.simpleMessage(
"Override Proxy related config"), "Override Proxy related config"),
"overrideDns": MessageLookupByLibrary.simpleMessage("Override Dns"),
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"Turning it on will override the DNS options in the profile"),
"password": MessageLookupByLibrary.simpleMessage("Password"), "password": MessageLookupByLibrary.simpleMessage("Password"),
"passwordTip": "passwordTip":
MessageLookupByLibrary.simpleMessage("Password cannot be empty"), MessageLookupByLibrary.simpleMessage("Password cannot be empty"),
@@ -260,6 +291,8 @@ class MessageLookup extends MessageLookupByLibrary {
"pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage( "pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage(
"Please upload a valid QR code"), "Please upload a valid QR code"),
"port": MessageLookupByLibrary.simpleMessage("Port"), "port": MessageLookupByLibrary.simpleMessage("Port"),
"preferH3Desc": MessageLookupByLibrary.simpleMessage(
"Prioritize the use of DOH\'s http/3"),
"preview": MessageLookupByLibrary.simpleMessage("Preview"), "preview": MessageLookupByLibrary.simpleMessage("Preview"),
"profile": MessageLookupByLibrary.simpleMessage("Profile"), "profile": MessageLookupByLibrary.simpleMessage("Profile"),
"profileAutoUpdateIntervalInvalidValidationDesc": "profileAutoUpdateIntervalInvalidValidationDesc":
@@ -279,13 +312,20 @@ class MessageLookup extends MessageLookupByLibrary {
"profiles": MessageLookupByLibrary.simpleMessage("Profiles"), "profiles": MessageLookupByLibrary.simpleMessage("Profiles"),
"profilesSort": MessageLookupByLibrary.simpleMessage("Profiles sort"), "profilesSort": MessageLookupByLibrary.simpleMessage("Profiles sort"),
"project": MessageLookupByLibrary.simpleMessage("Project"), "project": MessageLookupByLibrary.simpleMessage("Project"),
"providers": MessageLookupByLibrary.simpleMessage("Providers"),
"proxies": MessageLookupByLibrary.simpleMessage("Proxies"), "proxies": MessageLookupByLibrary.simpleMessage("Proxies"),
"proxiesSetting": "proxiesSetting":
MessageLookupByLibrary.simpleMessage("Proxies setting"), MessageLookupByLibrary.simpleMessage("Proxies setting"),
"proxyGroup": MessageLookupByLibrary.simpleMessage("Proxy group"), "proxyGroup": MessageLookupByLibrary.simpleMessage("Proxy group"),
"proxyNameserver":
MessageLookupByLibrary.simpleMessage("Proxy nameserver"),
"proxyNameserverDesc": MessageLookupByLibrary.simpleMessage(
"Domain for resolving proxy nodes"),
"proxyPort": MessageLookupByLibrary.simpleMessage("ProxyPort"), "proxyPort": MessageLookupByLibrary.simpleMessage("ProxyPort"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage( "proxyPortDesc": MessageLookupByLibrary.simpleMessage(
"Set the Clash listening port"), "Set the Clash listening port"),
"proxyProviders":
MessageLookupByLibrary.simpleMessage("Proxy providers"),
"prueBlackMode": "prueBlackMode":
MessageLookupByLibrary.simpleMessage("Prue black mode"), MessageLookupByLibrary.simpleMessage("Prue black mode"),
"qrcode": MessageLookupByLibrary.simpleMessage("QR code"), "qrcode": MessageLookupByLibrary.simpleMessage("QR code"),
@@ -309,7 +349,11 @@ class MessageLookup extends MessageLookupByLibrary {
"resources": MessageLookupByLibrary.simpleMessage("Resources"), "resources": MessageLookupByLibrary.simpleMessage("Resources"),
"resourcesDesc": MessageLookupByLibrary.simpleMessage( "resourcesDesc": MessageLookupByLibrary.simpleMessage(
"External resource related info"), "External resource related info"),
"respectRules": MessageLookupByLibrary.simpleMessage("Respect rules"),
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
"DNS connection following rules, need to configure proxy-server-nameserver"),
"rule": MessageLookupByLibrary.simpleMessage("Rule"), "rule": MessageLookupByLibrary.simpleMessage("Rule"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("Rule providers"),
"save": MessageLookupByLibrary.simpleMessage("Save"), "save": MessageLookupByLibrary.simpleMessage("Save"),
"search": MessageLookupByLibrary.simpleMessage("Search"), "search": MessageLookupByLibrary.simpleMessage("Search"),
"seconds": MessageLookupByLibrary.simpleMessage("Seconds"), "seconds": MessageLookupByLibrary.simpleMessage("Seconds"),
@@ -327,6 +371,9 @@ class MessageLookup extends MessageLookupByLibrary {
"standard": MessageLookupByLibrary.simpleMessage("Standard"), "standard": MessageLookupByLibrary.simpleMessage("Standard"),
"start": MessageLookupByLibrary.simpleMessage("Start"), "start": MessageLookupByLibrary.simpleMessage("Start"),
"startVpn": MessageLookupByLibrary.simpleMessage("Staring VPN..."), "startVpn": MessageLookupByLibrary.simpleMessage("Staring VPN..."),
"status": MessageLookupByLibrary.simpleMessage("Status"),
"statusDesc": MessageLookupByLibrary.simpleMessage(
"System DNS will be used when turned off"),
"stop": MessageLookupByLibrary.simpleMessage("Stop"), "stop": MessageLookupByLibrary.simpleMessage("Stop"),
"stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."), "stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."),
"style": MessageLookupByLibrary.simpleMessage("Style"), "style": MessageLookupByLibrary.simpleMessage("Style"),
@@ -370,12 +417,19 @@ class MessageLookup extends MessageLookupByLibrary {
"url": MessageLookupByLibrary.simpleMessage("URL"), "url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": "urlDesc":
MessageLookupByLibrary.simpleMessage("Obtain profile through URL"), MessageLookupByLibrary.simpleMessage("Obtain profile through URL"),
"useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"),
"useSystemHosts":
MessageLookupByLibrary.simpleMessage("Use system hosts"),
"value": MessageLookupByLibrary.simpleMessage("Value"), "value": MessageLookupByLibrary.simpleMessage("Value"),
"valueNotEmpty": "valueNotEmpty":
MessageLookupByLibrary.simpleMessage("The value cannot be empty"), MessageLookupByLibrary.simpleMessage("The value cannot be empty"),
"view": MessageLookupByLibrary.simpleMessage("View"), "view": MessageLookupByLibrary.simpleMessage("View"),
"vpnDesc": "vpnDesc":
MessageLookupByLibrary.simpleMessage("Modify VPN related settings"), MessageLookupByLibrary.simpleMessage("Modify VPN related settings"),
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
"Auto routes all system traffic through VpnService"),
"vpnTip": MessageLookupByLibrary.simpleMessage(
"Changes take effect after restarting the VPN"),
"webDAVConfiguration": "webDAVConfiguration":
MessageLookupByLibrary.simpleMessage("WebDAV configuration"), MessageLookupByLibrary.simpleMessage("WebDAV configuration"),
"whitelistMode": MessageLookupByLibrary.simpleMessage("Whitelist mode"), "whitelistMode": MessageLookupByLibrary.simpleMessage("Whitelist mode"),

View File

@@ -95,6 +95,9 @@ class MessageLookup extends MessageLookupByLibrary {
"dark": MessageLookupByLibrary.simpleMessage("深色"), "dark": MessageLookupByLibrary.simpleMessage("深色"),
"dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"), "dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"),
"days": MessageLookupByLibrary.simpleMessage(""), "days": MessageLookupByLibrary.simpleMessage(""),
"defaultNameserver": MessageLookupByLibrary.simpleMessage("默认域名服务器"),
"defaultNameserverDesc":
MessageLookupByLibrary.simpleMessage("用于解析DNS服务器"),
"defaultSort": MessageLookupByLibrary.simpleMessage("按默认排序"), "defaultSort": MessageLookupByLibrary.simpleMessage("按默认排序"),
"defaultText": MessageLookupByLibrary.simpleMessage("默认"), "defaultText": MessageLookupByLibrary.simpleMessage("默认"),
"delay": MessageLookupByLibrary.simpleMessage("延迟"), "delay": MessageLookupByLibrary.simpleMessage("延迟"),
@@ -107,7 +110,9 @@ class MessageLookup extends MessageLookupByLibrary {
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"), "discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"),
"discovery": MessageLookupByLibrary.simpleMessage("发现新版本"), "discovery": MessageLookupByLibrary.simpleMessage("发现新版本"),
"dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"), "dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"), "doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"),
"domain": MessageLookupByLibrary.simpleMessage("域名"),
"download": MessageLookupByLibrary.simpleMessage("下载"), "download": MessageLookupByLibrary.simpleMessage("下载"),
"edit": MessageLookupByLibrary.simpleMessage("编辑"), "edit": MessageLookupByLibrary.simpleMessage("编辑"),
"en": MessageLookupByLibrary.simpleMessage("英语"), "en": MessageLookupByLibrary.simpleMessage("英语"),
@@ -123,6 +128,11 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("开启后将可以通过9090端口控制Clash内核"), MessageLookupByLibrary.simpleMessage("开启后将可以通过9090端口控制Clash内核"),
"externalLink": MessageLookupByLibrary.simpleMessage("外部链接"), "externalLink": MessageLookupByLibrary.simpleMessage("外部链接"),
"externalResources": MessageLookupByLibrary.simpleMessage("外部资源"), "externalResources": MessageLookupByLibrary.simpleMessage("外部资源"),
"fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip过滤"),
"fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip范围"),
"fallback": MessageLookupByLibrary.simpleMessage("Fallback"),
"fallbackDesc": MessageLookupByLibrary.simpleMessage("一般情况下使用境外DNS"),
"fallbackFilter": MessageLookupByLibrary.simpleMessage("Fallback过滤"),
"file": MessageLookupByLibrary.simpleMessage("文件"), "file": MessageLookupByLibrary.simpleMessage("文件"),
"fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"), "fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"),
"filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"), "filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"),
@@ -136,15 +146,18 @@ class MessageLookup extends MessageLookupByLibrary {
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"), "geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"),
"geodataLoaderDesc": "geodataLoaderDesc":
MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"), MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"),
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip代码"),
"global": MessageLookupByLibrary.simpleMessage("全局"), "global": MessageLookupByLibrary.simpleMessage("全局"),
"go": MessageLookupByLibrary.simpleMessage("前往"), "go": MessageLookupByLibrary.simpleMessage("前往"),
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"), "goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
"hostsDesc": MessageLookupByLibrary.simpleMessage("追加Hosts"),
"hours": MessageLookupByLibrary.simpleMessage("小时"), "hours": MessageLookupByLibrary.simpleMessage("小时"),
"importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"), "importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"),
"infiniteTime": MessageLookupByLibrary.simpleMessage("长期有效"), "infiniteTime": MessageLookupByLibrary.simpleMessage("长期有效"),
"init": MessageLookupByLibrary.simpleMessage("初始化"), "init": MessageLookupByLibrary.simpleMessage("初始化"),
"intelligentSelected": MessageLookupByLibrary.simpleMessage("智能选择"), "intelligentSelected": MessageLookupByLibrary.simpleMessage("智能选择"),
"intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"), "intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("IP/掩码"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"), "ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"),
"just": MessageLookupByLibrary.simpleMessage("刚刚"), "just": MessageLookupByLibrary.simpleMessage("刚刚"),
"keepAliveIntervalDesc": "keepAliveIntervalDesc":
@@ -163,6 +176,8 @@ class MessageLookup extends MessageLookupByLibrary {
"logcatDesc": MessageLookupByLibrary.simpleMessage("禁用将会隐藏日志入口"), "logcatDesc": MessageLookupByLibrary.simpleMessage("禁用将会隐藏日志入口"),
"logs": MessageLookupByLibrary.simpleMessage("日志"), "logs": MessageLookupByLibrary.simpleMessage("日志"),
"logsDesc": MessageLookupByLibrary.simpleMessage("日志捕获记录"), "logsDesc": MessageLookupByLibrary.simpleMessage("日志捕获记录"),
"loopback": MessageLookupByLibrary.simpleMessage("回环解锁工具"),
"loopbackDesc": MessageLookupByLibrary.simpleMessage("用于UWP回环解锁"),
"loose": MessageLookupByLibrary.simpleMessage("紧凑"), "loose": MessageLookupByLibrary.simpleMessage("紧凑"),
"min": MessageLookupByLibrary.simpleMessage("最小"), "min": MessageLookupByLibrary.simpleMessage("最小"),
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"), "minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"),
@@ -174,6 +189,11 @@ class MessageLookup extends MessageLookupByLibrary {
"more": MessageLookupByLibrary.simpleMessage("更多"), "more": MessageLookupByLibrary.simpleMessage("更多"),
"name": MessageLookupByLibrary.simpleMessage("名称"), "name": MessageLookupByLibrary.simpleMessage("名称"),
"nameSort": MessageLookupByLibrary.simpleMessage("按名称排序"), "nameSort": MessageLookupByLibrary.simpleMessage("按名称排序"),
"nameserver": MessageLookupByLibrary.simpleMessage("域名服务器"),
"nameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析域名"),
"nameserverPolicy": MessageLookupByLibrary.simpleMessage("域名服务器策略"),
"nameserverPolicyDesc":
MessageLookupByLibrary.simpleMessage("指定对应域名服务器策略"),
"networkDetection": MessageLookupByLibrary.simpleMessage("网络检测"), "networkDetection": MessageLookupByLibrary.simpleMessage("网络检测"),
"networkSpeed": MessageLookupByLibrary.simpleMessage("网络速度"), "networkSpeed": MessageLookupByLibrary.simpleMessage("网络速度"),
"noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"), "noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"),
@@ -193,11 +213,15 @@ class MessageLookup extends MessageLookupByLibrary {
"onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage("仅统计代理"), "onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage("仅统计代理"),
"onlyStatisticsProxyDesc": "onlyStatisticsProxyDesc":
MessageLookupByLibrary.simpleMessage("开启后,将只统计代理流量"), MessageLookupByLibrary.simpleMessage("开启后,将只统计代理流量"),
"options": MessageLookupByLibrary.simpleMessage("选项"),
"other": MessageLookupByLibrary.simpleMessage("其他"), "other": MessageLookupByLibrary.simpleMessage("其他"),
"otherContributors": MessageLookupByLibrary.simpleMessage("其他贡献者"), "otherContributors": MessageLookupByLibrary.simpleMessage("其他贡献者"),
"outboundMode": MessageLookupByLibrary.simpleMessage("出站模式"), "outboundMode": MessageLookupByLibrary.simpleMessage("出站模式"),
"override": MessageLookupByLibrary.simpleMessage("覆写"), "override": MessageLookupByLibrary.simpleMessage("覆写"),
"overrideDesc": MessageLookupByLibrary.simpleMessage("覆写代理相关配置"), "overrideDesc": MessageLookupByLibrary.simpleMessage("覆写代理相关配置"),
"overrideDns": MessageLookupByLibrary.simpleMessage("覆写DNS"),
"overrideDnsDesc":
MessageLookupByLibrary.simpleMessage("开启后将覆盖配置中的DNS选项"),
"password": MessageLookupByLibrary.simpleMessage("密码"), "password": MessageLookupByLibrary.simpleMessage("密码"),
"passwordTip": MessageLookupByLibrary.simpleMessage("密码不能为空"), "passwordTip": MessageLookupByLibrary.simpleMessage("密码不能为空"),
"paste": MessageLookupByLibrary.simpleMessage("粘贴"), "paste": MessageLookupByLibrary.simpleMessage("粘贴"),
@@ -206,6 +230,7 @@ class MessageLookup extends MessageLookupByLibrary {
"pleaseUploadValidQrcode": "pleaseUploadValidQrcode":
MessageLookupByLibrary.simpleMessage("请上传有效的二维码"), MessageLookupByLibrary.simpleMessage("请上传有效的二维码"),
"port": MessageLookupByLibrary.simpleMessage("端口"), "port": MessageLookupByLibrary.simpleMessage("端口"),
"preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"),
"preview": MessageLookupByLibrary.simpleMessage("预览"), "preview": MessageLookupByLibrary.simpleMessage("预览"),
"profile": MessageLookupByLibrary.simpleMessage("配置"), "profile": MessageLookupByLibrary.simpleMessage("配置"),
"profileAutoUpdateIntervalInvalidValidationDesc": "profileAutoUpdateIntervalInvalidValidationDesc":
@@ -223,11 +248,16 @@ class MessageLookup extends MessageLookupByLibrary {
"profiles": MessageLookupByLibrary.simpleMessage("配置"), "profiles": MessageLookupByLibrary.simpleMessage("配置"),
"profilesSort": MessageLookupByLibrary.simpleMessage("配置排序"), "profilesSort": MessageLookupByLibrary.simpleMessage("配置排序"),
"project": MessageLookupByLibrary.simpleMessage("项目"), "project": MessageLookupByLibrary.simpleMessage("项目"),
"providers": MessageLookupByLibrary.simpleMessage("提供者"),
"proxies": MessageLookupByLibrary.simpleMessage("代理"), "proxies": MessageLookupByLibrary.simpleMessage("代理"),
"proxiesSetting": MessageLookupByLibrary.simpleMessage("代理设置"), "proxiesSetting": MessageLookupByLibrary.simpleMessage("代理设置"),
"proxyGroup": MessageLookupByLibrary.simpleMessage("代理组"), "proxyGroup": MessageLookupByLibrary.simpleMessage("代理组"),
"proxyNameserver": MessageLookupByLibrary.simpleMessage("代理域名服务器"),
"proxyNameserverDesc":
MessageLookupByLibrary.simpleMessage("用于解析代理节点的域名"),
"proxyPort": MessageLookupByLibrary.simpleMessage("代理端口"), "proxyPort": MessageLookupByLibrary.simpleMessage("代理端口"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage("设置Clash监听端口"), "proxyPortDesc": MessageLookupByLibrary.simpleMessage("设置Clash监听端口"),
"proxyProviders": MessageLookupByLibrary.simpleMessage("代理提供者"),
"prueBlackMode": MessageLookupByLibrary.simpleMessage("纯黑模式"), "prueBlackMode": MessageLookupByLibrary.simpleMessage("纯黑模式"),
"qrcode": MessageLookupByLibrary.simpleMessage("二维码"), "qrcode": MessageLookupByLibrary.simpleMessage("二维码"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage("扫描二维码获取配置文件"), "qrcodeDesc": MessageLookupByLibrary.simpleMessage("扫描二维码获取配置文件"),
@@ -243,7 +273,11 @@ class MessageLookup extends MessageLookupByLibrary {
"requestsDesc": MessageLookupByLibrary.simpleMessage("查看最近请求记录"), "requestsDesc": MessageLookupByLibrary.simpleMessage("查看最近请求记录"),
"resources": MessageLookupByLibrary.simpleMessage("资源"), "resources": MessageLookupByLibrary.simpleMessage("资源"),
"resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"), "resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"),
"respectRules": MessageLookupByLibrary.simpleMessage("遵守规则"),
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
"DNS连接跟随rules,需配置proxy-server-nameserver"),
"rule": MessageLookupByLibrary.simpleMessage("规则"), "rule": MessageLookupByLibrary.simpleMessage("规则"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("规则提供者"),
"save": MessageLookupByLibrary.simpleMessage("保存"), "save": MessageLookupByLibrary.simpleMessage("保存"),
"search": MessageLookupByLibrary.simpleMessage("搜索"), "search": MessageLookupByLibrary.simpleMessage("搜索"),
"seconds": MessageLookupByLibrary.simpleMessage(""), "seconds": MessageLookupByLibrary.simpleMessage(""),
@@ -260,6 +294,8 @@ class MessageLookup extends MessageLookupByLibrary {
"standard": MessageLookupByLibrary.simpleMessage("标准"), "standard": MessageLookupByLibrary.simpleMessage("标准"),
"start": MessageLookupByLibrary.simpleMessage("启动"), "start": MessageLookupByLibrary.simpleMessage("启动"),
"startVpn": MessageLookupByLibrary.simpleMessage("正在启动VPN..."), "startVpn": MessageLookupByLibrary.simpleMessage("正在启动VPN..."),
"status": MessageLookupByLibrary.simpleMessage("状态"),
"statusDesc": MessageLookupByLibrary.simpleMessage("关闭后将使用系统DNS"),
"stop": MessageLookupByLibrary.simpleMessage("暂停"), "stop": MessageLookupByLibrary.simpleMessage("暂停"),
"stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."), "stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."),
"style": MessageLookupByLibrary.simpleMessage("风格"), "style": MessageLookupByLibrary.simpleMessage("风格"),
@@ -297,10 +333,15 @@ class MessageLookup extends MessageLookupByLibrary {
"upload": MessageLookupByLibrary.simpleMessage("上传"), "upload": MessageLookupByLibrary.simpleMessage("上传"),
"url": MessageLookupByLibrary.simpleMessage("URL"), "url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage("通过URL获取配置文件"), "urlDesc": MessageLookupByLibrary.simpleMessage("通过URL获取配置文件"),
"useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"),
"value": MessageLookupByLibrary.simpleMessage(""), "value": MessageLookupByLibrary.simpleMessage(""),
"valueNotEmpty": MessageLookupByLibrary.simpleMessage("值不能为空"), "valueNotEmpty": MessageLookupByLibrary.simpleMessage("值不能为空"),
"view": MessageLookupByLibrary.simpleMessage("查看"), "view": MessageLookupByLibrary.simpleMessage("查看"),
"vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"), "vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"),
"vpnEnableDesc":
MessageLookupByLibrary.simpleMessage("通过VpnService自动路由系统所有流量"),
"vpnTip": MessageLookupByLibrary.simpleMessage("重启VPN后改变生效"),
"webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV配置"), "webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV配置"),
"whitelistMode": MessageLookupByLibrary.simpleMessage("白名单模式"), "whitelistMode": MessageLookupByLibrary.simpleMessage("白名单模式"),
"years": MessageLookupByLibrary.simpleMessage(""), "years": MessageLookupByLibrary.simpleMessage(""),

View File

@@ -2579,6 +2579,356 @@ class AppLocalizations {
args: [], args: [],
); );
} }
/// `Add Hosts`
String get hostsDesc {
return Intl.message(
'Add Hosts',
name: 'hostsDesc',
desc: '',
args: [],
);
}
/// `Changes take effect after restarting the VPN`
String get vpnTip {
return Intl.message(
'Changes take effect after restarting the VPN',
name: 'vpnTip',
desc: '',
args: [],
);
}
/// `Auto routes all system traffic through VpnService`
String get vpnEnableDesc {
return Intl.message(
'Auto routes all system traffic through VpnService',
name: 'vpnEnableDesc',
desc: '',
args: [],
);
}
/// `Options`
String get options {
return Intl.message(
'Options',
name: 'options',
desc: '',
args: [],
);
}
/// `Loopback unlock tool`
String get loopback {
return Intl.message(
'Loopback unlock tool',
name: 'loopback',
desc: '',
args: [],
);
}
/// `Used for UWP loopback unlocking`
String get loopbackDesc {
return Intl.message(
'Used for UWP loopback unlocking',
name: 'loopbackDesc',
desc: '',
args: [],
);
}
/// `Providers`
String get providers {
return Intl.message(
'Providers',
name: 'providers',
desc: '',
args: [],
);
}
/// `Proxy providers`
String get proxyProviders {
return Intl.message(
'Proxy providers',
name: 'proxyProviders',
desc: '',
args: [],
);
}
/// `Rule providers`
String get ruleProviders {
return Intl.message(
'Rule providers',
name: 'ruleProviders',
desc: '',
args: [],
);
}
/// `Override Dns`
String get overrideDns {
return Intl.message(
'Override Dns',
name: 'overrideDns',
desc: '',
args: [],
);
}
/// `Turning it on will override the DNS options in the profile`
String get overrideDnsDesc {
return Intl.message(
'Turning it on will override the DNS options in the profile',
name: 'overrideDnsDesc',
desc: '',
args: [],
);
}
/// `Status`
String get status {
return Intl.message(
'Status',
name: 'status',
desc: '',
args: [],
);
}
/// `System DNS will be used when turned off`
String get statusDesc {
return Intl.message(
'System DNS will be used when turned off',
name: 'statusDesc',
desc: '',
args: [],
);
}
/// `Prioritize the use of DOH's http/3`
String get preferH3Desc {
return Intl.message(
'Prioritize the use of DOH\'s http/3',
name: 'preferH3Desc',
desc: '',
args: [],
);
}
/// `Respect rules`
String get respectRules {
return Intl.message(
'Respect rules',
name: 'respectRules',
desc: '',
args: [],
);
}
/// `DNS connection following rules, need to configure proxy-server-nameserver`
String get respectRulesDesc {
return Intl.message(
'DNS connection following rules, need to configure proxy-server-nameserver',
name: 'respectRulesDesc',
desc: '',
args: [],
);
}
/// `DNS mode`
String get dnsMode {
return Intl.message(
'DNS mode',
name: 'dnsMode',
desc: '',
args: [],
);
}
/// `Fakeip range`
String get fakeipRange {
return Intl.message(
'Fakeip range',
name: 'fakeipRange',
desc: '',
args: [],
);
}
/// `Fakeip filter`
String get fakeipFilter {
return Intl.message(
'Fakeip filter',
name: 'fakeipFilter',
desc: '',
args: [],
);
}
/// `Default nameserver`
String get defaultNameserver {
return Intl.message(
'Default nameserver',
name: 'defaultNameserver',
desc: '',
args: [],
);
}
/// `For resolving DNS server`
String get defaultNameserverDesc {
return Intl.message(
'For resolving DNS server',
name: 'defaultNameserverDesc',
desc: '',
args: [],
);
}
/// `Nameserver`
String get nameserver {
return Intl.message(
'Nameserver',
name: 'nameserver',
desc: '',
args: [],
);
}
/// `For resolving domain`
String get nameserverDesc {
return Intl.message(
'For resolving domain',
name: 'nameserverDesc',
desc: '',
args: [],
);
}
/// `Use hosts`
String get useHosts {
return Intl.message(
'Use hosts',
name: 'useHosts',
desc: '',
args: [],
);
}
/// `Use system hosts`
String get useSystemHosts {
return Intl.message(
'Use system hosts',
name: 'useSystemHosts',
desc: '',
args: [],
);
}
/// `Nameserver policy`
String get nameserverPolicy {
return Intl.message(
'Nameserver policy',
name: 'nameserverPolicy',
desc: '',
args: [],
);
}
/// `Specify the corresponding nameserver policy`
String get nameserverPolicyDesc {
return Intl.message(
'Specify the corresponding nameserver policy',
name: 'nameserverPolicyDesc',
desc: '',
args: [],
);
}
/// `Proxy nameserver`
String get proxyNameserver {
return Intl.message(
'Proxy nameserver',
name: 'proxyNameserver',
desc: '',
args: [],
);
}
/// `Domain for resolving proxy nodes`
String get proxyNameserverDesc {
return Intl.message(
'Domain for resolving proxy nodes',
name: 'proxyNameserverDesc',
desc: '',
args: [],
);
}
/// `Fallback`
String get fallback {
return Intl.message(
'Fallback',
name: 'fallback',
desc: '',
args: [],
);
}
/// `Generally use offshore DNS`
String get fallbackDesc {
return Intl.message(
'Generally use offshore DNS',
name: 'fallbackDesc',
desc: '',
args: [],
);
}
/// `Fallback filter`
String get fallbackFilter {
return Intl.message(
'Fallback filter',
name: 'fallbackFilter',
desc: '',
args: [],
);
}
/// `Geoip code`
String get geoipCode {
return Intl.message(
'Geoip code',
name: 'geoipCode',
desc: '',
args: [],
);
}
/// `Ipcidr`
String get ipcidr {
return Intl.message(
'Ipcidr',
name: 'ipcidr',
desc: '',
args: [],
);
}
/// `Domain`
String get domain {
return Intl.message(
'Domain',
name: 'domain',
desc: '',
args: [],
);
}
} }
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> { class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:fl_clash/clash/clash.dart'; import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/http.dart';
import 'package:fl_clash/plugins/app.dart'; import 'package:fl_clash/plugins/app.dart';
import 'package:fl_clash/plugins/tile.dart'; import 'package:fl_clash/plugins/tile.dart';
import 'package:fl_clash/plugins/vpn.dart'; import 'package:fl_clash/plugins/vpn.dart';
@@ -36,6 +37,7 @@ Future<void> main() async {
config: config, config: config,
clashConfig: clashConfig, clashConfig: clashConfig,
); );
HttpOverrides.global = FlClashHttpOverrides();
runAppWithPreferences( runAppWithPreferences(
const Application(), const Application(),
appState: appState, appState: appState,

View File

@@ -26,84 +26,84 @@ class Tun with _$Tun {
factory Tun.fromJson(Map<String, Object?> json) => _$TunFromJson(json); factory Tun.fromJson(Map<String, Object?> json) => _$TunFromJson(json);
} }
@JsonSerializable() @freezed
class Dns { class FallbackFilter with _$FallbackFilter {
bool enable; const factory FallbackFilter({
bool ipv6; @Default(true) bool geoip,
@JsonKey(name: "default-nameserver") @Default("CN") @JsonKey(name: "geoip-code") String geoipCode,
List<String> defaultNameserver; @Default(["gfw"]) List<String> geosite,
@JsonKey(name: "enhanced-mode") @Default(["240.0.0.0/4"]) List<String> ipcidr,
String enhancedMode; @Default([
@JsonKey(name: "fake-ip-range") "+.google.com",
String fakeIpRange; "+.facebook.com",
@JsonKey(name: "use-hosts") "+.youtube.com",
bool useHosts; ])
List<String> nameserver; List<String> domain,
List<String> fallback; }) = _FallbackFilter;
@JsonKey(name: "fake-ip-filter")
List<String> fakeIpFilter;
Dns() factory FallbackFilter.fromJson(Map<String, Object?> json) =>
: enable = true, _$FallbackFilterFromJson(json);
ipv6 = false, }
defaultNameserver = [
"223.5.5.5", @freezed
"119.29.29.29", class Dns with _$Dns {
"8.8.4.4", const factory Dns({
"1.0.0.1", @Default(true) bool enable,
], @Default(false) @JsonKey(name: "prefer-h3") bool preferH3,
enhancedMode = "fake-ip", @Default(true) @JsonKey(name: "use-hosts") bool useHosts,
fakeIpRange = "198.18.0.1/16", @Default(true) @JsonKey(name: "use-system-hosts") bool useSystemHosts,
useHosts = true, @Default(true) @JsonKey(name: "respect-rules") bool respectRules,
nameserver = [ @Default(false) bool ipv6,
"8.8.8.8", @Default(["223.5.5.5"])
"114.114.114.114", @JsonKey(name: "default-nameserver")
List<String> defaultNameserver,
@Default(DnsMode.fakeIp)
@JsonKey(name: "enhanced-mode")
DnsMode enhancedMode,
@Default("198.18.0.1/16")
@JsonKey(name: "fake-ip-range")
String fakeIpRange,
@Default([
"*.lan",
"localhost.ptlogin2.qq.com",
])
@JsonKey(name: "fake-ip-filter")
List<String> fakeIpFilter,
@Default({
"www.baidu.com": "114.114.114.114",
"+.internal.crop.com": "10.0.0.1",
"geosite:cn": "https://doh.pub/dns-query"
})
@JsonKey(name: "nameserver-policy")
Map<String, String> nameserverPolicy,
@Default([
"https://doh.pub/dns-query", "https://doh.pub/dns-query",
"https://dns.alidns.com/dns-query", "https://dns.alidns.com/dns-query",
], ])
fallback = [ List<String> nameserver,
'https://doh.dns.sb/dns-query', @Default([
'https://dns.cloudflare.com/dns-query', "tls://8.8.4.4",
'https://dns.twnic.tw/dns-query', "tls://1.1.1.1",
'tls://8.8.4.4:853', ])
], List<String> fallback,
fakeIpFilter = [ @Default([
// Stun Services "https://doh.pub/dns-query",
"+.stun.*.*", ])
"+.stun.*.*.*", @JsonKey(name: "proxy-server-nameserver")
"+.stun.*.*.*.*", List<String> proxyServerNameserver,
"+.stun.*.*.*.*.*", @Default(FallbackFilter())
@JsonKey(name: "fallback-filter")
FallbackFilter fallbackFilter,
}) = _Dns;
// Google Voices factory Dns.fromJson(Map<String, Object?> json) => _$DnsFromJson(json);
"lens.l.google.com",
// Nintendo Switch STUN factory Dns.safeDnsFromJson(Map<String, Object?> json) {
"*.n.n.srv.nintendo.net", try {
return Dns.fromJson(json);
// PlayStation STUN } catch (_) {
"+.stun.playstation.net", return const Dns();
// XBox
"xbox.*.*.microsoft.com",
"*.*.xboxlive.com",
// Microsoft Captive Portal
"*.msftncsi.com",
"*.msftconnecttest.com",
// Bilibili CDN
"*.mcdn.bilivideo.cn",
// Windows Default LAN WorkGroup
"WORKGROUP",
];
factory Dns.fromJson(Map<String, dynamic> json) {
return _$DnsFromJson(json);
} }
Map<String, dynamic> toJson() {
return _$DnsToJson(this);
} }
} }
@@ -144,7 +144,7 @@ class ClashConfig extends ChangeNotifier {
_geodataLoader = geodataLoaderMemconservative, _geodataLoader = geodataLoaderMemconservative,
_externalController = '', _externalController = '',
_keepAliveInterval = 30, _keepAliveInterval = 30,
_dns = Dns(), _dns = const Dns(),
_geoXUrl = defaultGeoXMap, _geoXUrl = defaultGeoXMap,
_rules = [], _rules = [],
_hosts = {}; _hosts = {};
@@ -273,6 +273,7 @@ class ClashConfig extends ChangeNotifier {
} }
} }
@JsonKey(fromJson: Dns.safeDnsFromJson)
Dns get dns => _dns; Dns get dns => _dns;
set dns(Dns value) { set dns(Dns value) {

View File

@@ -49,6 +49,17 @@ class CoreState with _$CoreState {
_$CoreStateFromJson(json); _$CoreStateFromJson(json);
} }
@freezed
class VPNState with _$VPNState {
const factory VPNState({
required AccessControl? accessControl,
required VpnProps vpnProps,
}) = _VPNState;
factory VPNState.fromJson(Map<String, Object?> json) =>
_$VPNStateFromJson(json);
}
@freezed @freezed
class WindowProps with _$WindowProps { class WindowProps with _$WindowProps {
const factory WindowProps({ const factory WindowProps({
@@ -115,6 +126,7 @@ class Config extends ChangeNotifier {
VpnProps _vpnProps; VpnProps _vpnProps;
DesktopProps _desktopProps; DesktopProps _desktopProps;
bool _showLabel; bool _showLabel;
bool _overrideDns;
Config() Config()
: _profiles = [], : _profiles = [],
@@ -142,7 +154,8 @@ class Config extends ChangeNotifier {
_proxiesLayout = ProxiesLayout.standard, _proxiesLayout = ProxiesLayout.standard,
_vpnProps = const VpnProps(), _vpnProps = const VpnProps(),
_desktopProps = const DesktopProps(), _desktopProps = const DesktopProps(),
_showLabel = false; _showLabel = false,
_overrideDns = false;
deleteProfileById(String id) { deleteProfileById(String id) {
_profiles = profiles.where((element) => element.id != id).toList(); _profiles = profiles.where((element) => element.id != id).toList();
@@ -548,6 +561,16 @@ class Config extends ChangeNotifier {
} }
} }
@JsonKey(defaultValue: false)
bool get overrideDns => _overrideDns;
set overrideDns(bool value) {
if (_overrideDns != value) {
_overrideDns = value;
notifyListeners();
}
}
update([ update([
Config? config, Config? config,
RecoveryOption recoveryOptions = RecoveryOption.all, RecoveryOption recoveryOptions = RecoveryOption.all,
@@ -584,6 +607,7 @@ class Config extends ChangeNotifier {
_isExclude = config._isExclude; _isExclude = config._isExclude;
_windowProps = config._windowProps; _windowProps = config._windowProps;
_vpnProps = config._vpnProps; _vpnProps = config._vpnProps;
_overrideDns = config._overrideDns;
_desktopProps = config._desktopProps; _desktopProps = config._desktopProps;
} }
notifyListeners(); notifyListeners();

View File

@@ -14,6 +14,7 @@ class ConfigExtendedParams with _$ConfigExtendedParams {
@JsonKey(name: "is-patch") required bool isPatch, @JsonKey(name: "is-patch") required bool isPatch,
@JsonKey(name: "is-compatible") required bool isCompatible, @JsonKey(name: "is-compatible") required bool isCompatible,
@JsonKey(name: "selected-map") required SelectedMap selectedMap, @JsonKey(name: "selected-map") required SelectedMap selectedMap,
@JsonKey(name: "override-dns") required bool overrideDns,
@JsonKey(name: "test-url") required String testUrl, @JsonKey(name: "test-url") required String testUrl,
}) = _ConfigExtendedParams; }) = _ConfigExtendedParams;

View File

@@ -220,3 +220,818 @@ abstract class _Tun implements Tun {
_$$TunImplCopyWith<_$TunImpl> get copyWith => _$$TunImplCopyWith<_$TunImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
FallbackFilter _$FallbackFilterFromJson(Map<String, dynamic> json) {
return _FallbackFilter.fromJson(json);
}
/// @nodoc
mixin _$FallbackFilter {
bool get geoip => throw _privateConstructorUsedError;
@JsonKey(name: "geoip-code")
String get geoipCode => throw _privateConstructorUsedError;
List<String> get geosite => throw _privateConstructorUsedError;
List<String> get ipcidr => throw _privateConstructorUsedError;
List<String> get domain => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$FallbackFilterCopyWith<FallbackFilter> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $FallbackFilterCopyWith<$Res> {
factory $FallbackFilterCopyWith(
FallbackFilter value, $Res Function(FallbackFilter) then) =
_$FallbackFilterCopyWithImpl<$Res, FallbackFilter>;
@useResult
$Res call(
{bool geoip,
@JsonKey(name: "geoip-code") String geoipCode,
List<String> geosite,
List<String> ipcidr,
List<String> domain});
}
/// @nodoc
class _$FallbackFilterCopyWithImpl<$Res, $Val extends FallbackFilter>
implements $FallbackFilterCopyWith<$Res> {
_$FallbackFilterCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? geoip = null,
Object? geoipCode = null,
Object? geosite = null,
Object? ipcidr = null,
Object? domain = null,
}) {
return _then(_value.copyWith(
geoip: null == geoip
? _value.geoip
: geoip // ignore: cast_nullable_to_non_nullable
as bool,
geoipCode: null == geoipCode
? _value.geoipCode
: geoipCode // ignore: cast_nullable_to_non_nullable
as String,
geosite: null == geosite
? _value.geosite
: geosite // ignore: cast_nullable_to_non_nullable
as List<String>,
ipcidr: null == ipcidr
? _value.ipcidr
: ipcidr // ignore: cast_nullable_to_non_nullable
as List<String>,
domain: null == domain
? _value.domain
: domain // ignore: cast_nullable_to_non_nullable
as List<String>,
) as $Val);
}
}
/// @nodoc
abstract class _$$FallbackFilterImplCopyWith<$Res>
implements $FallbackFilterCopyWith<$Res> {
factory _$$FallbackFilterImplCopyWith(_$FallbackFilterImpl value,
$Res Function(_$FallbackFilterImpl) then) =
__$$FallbackFilterImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{bool geoip,
@JsonKey(name: "geoip-code") String geoipCode,
List<String> geosite,
List<String> ipcidr,
List<String> domain});
}
/// @nodoc
class __$$FallbackFilterImplCopyWithImpl<$Res>
extends _$FallbackFilterCopyWithImpl<$Res, _$FallbackFilterImpl>
implements _$$FallbackFilterImplCopyWith<$Res> {
__$$FallbackFilterImplCopyWithImpl(
_$FallbackFilterImpl _value, $Res Function(_$FallbackFilterImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? geoip = null,
Object? geoipCode = null,
Object? geosite = null,
Object? ipcidr = null,
Object? domain = null,
}) {
return _then(_$FallbackFilterImpl(
geoip: null == geoip
? _value.geoip
: geoip // ignore: cast_nullable_to_non_nullable
as bool,
geoipCode: null == geoipCode
? _value.geoipCode
: geoipCode // ignore: cast_nullable_to_non_nullable
as String,
geosite: null == geosite
? _value._geosite
: geosite // ignore: cast_nullable_to_non_nullable
as List<String>,
ipcidr: null == ipcidr
? _value._ipcidr
: ipcidr // ignore: cast_nullable_to_non_nullable
as List<String>,
domain: null == domain
? _value._domain
: domain // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$FallbackFilterImpl implements _FallbackFilter {
const _$FallbackFilterImpl(
{this.geoip = true,
@JsonKey(name: "geoip-code") this.geoipCode = "CN",
final List<String> geosite = const ["gfw"],
final List<String> ipcidr = const ["240.0.0.0/4"],
final List<String> domain = const [
"+.google.com",
"+.facebook.com",
"+.youtube.com"
]})
: _geosite = geosite,
_ipcidr = ipcidr,
_domain = domain;
factory _$FallbackFilterImpl.fromJson(Map<String, dynamic> json) =>
_$$FallbackFilterImplFromJson(json);
@override
@JsonKey()
final bool geoip;
@override
@JsonKey(name: "geoip-code")
final String geoipCode;
final List<String> _geosite;
@override
@JsonKey()
List<String> get geosite {
if (_geosite is EqualUnmodifiableListView) return _geosite;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_geosite);
}
final List<String> _ipcidr;
@override
@JsonKey()
List<String> get ipcidr {
if (_ipcidr is EqualUnmodifiableListView) return _ipcidr;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_ipcidr);
}
final List<String> _domain;
@override
@JsonKey()
List<String> get domain {
if (_domain is EqualUnmodifiableListView) return _domain;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_domain);
}
@override
String toString() {
return 'FallbackFilter(geoip: $geoip, geoipCode: $geoipCode, geosite: $geosite, ipcidr: $ipcidr, domain: $domain)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$FallbackFilterImpl &&
(identical(other.geoip, geoip) || other.geoip == geoip) &&
(identical(other.geoipCode, geoipCode) ||
other.geoipCode == geoipCode) &&
const DeepCollectionEquality().equals(other._geosite, _geosite) &&
const DeepCollectionEquality().equals(other._ipcidr, _ipcidr) &&
const DeepCollectionEquality().equals(other._domain, _domain));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType,
geoip,
geoipCode,
const DeepCollectionEquality().hash(_geosite),
const DeepCollectionEquality().hash(_ipcidr),
const DeepCollectionEquality().hash(_domain));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$FallbackFilterImplCopyWith<_$FallbackFilterImpl> get copyWith =>
__$$FallbackFilterImplCopyWithImpl<_$FallbackFilterImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$FallbackFilterImplToJson(
this,
);
}
}
abstract class _FallbackFilter implements FallbackFilter {
const factory _FallbackFilter(
{final bool geoip,
@JsonKey(name: "geoip-code") final String geoipCode,
final List<String> geosite,
final List<String> ipcidr,
final List<String> domain}) = _$FallbackFilterImpl;
factory _FallbackFilter.fromJson(Map<String, dynamic> json) =
_$FallbackFilterImpl.fromJson;
@override
bool get geoip;
@override
@JsonKey(name: "geoip-code")
String get geoipCode;
@override
List<String> get geosite;
@override
List<String> get ipcidr;
@override
List<String> get domain;
@override
@JsonKey(ignore: true)
_$$FallbackFilterImplCopyWith<_$FallbackFilterImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Dns _$DnsFromJson(Map<String, dynamic> json) {
return _Dns.fromJson(json);
}
/// @nodoc
mixin _$Dns {
bool get enable => throw _privateConstructorUsedError;
@JsonKey(name: "prefer-h3")
bool get preferH3 => throw _privateConstructorUsedError;
@JsonKey(name: "use-hosts")
bool get useHosts => throw _privateConstructorUsedError;
@JsonKey(name: "use-system-hosts")
bool get useSystemHosts => throw _privateConstructorUsedError;
@JsonKey(name: "respect-rules")
bool get respectRules => throw _privateConstructorUsedError;
bool get ipv6 => throw _privateConstructorUsedError;
@JsonKey(name: "default-nameserver")
List<String> get defaultNameserver => throw _privateConstructorUsedError;
@JsonKey(name: "enhanced-mode")
DnsMode get enhancedMode => throw _privateConstructorUsedError;
@JsonKey(name: "fake-ip-range")
String get fakeIpRange => throw _privateConstructorUsedError;
@JsonKey(name: "fake-ip-filter")
List<String> get fakeIpFilter => throw _privateConstructorUsedError;
@JsonKey(name: "nameserver-policy")
Map<String, String> get nameserverPolicy =>
throw _privateConstructorUsedError;
List<String> get nameserver => throw _privateConstructorUsedError;
List<String> get fallback => throw _privateConstructorUsedError;
@JsonKey(name: "proxy-server-nameserver")
List<String> get proxyServerNameserver => throw _privateConstructorUsedError;
@JsonKey(name: "fallback-filter")
FallbackFilter get fallbackFilter => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$DnsCopyWith<Dns> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $DnsCopyWith<$Res> {
factory $DnsCopyWith(Dns value, $Res Function(Dns) then) =
_$DnsCopyWithImpl<$Res, Dns>;
@useResult
$Res call(
{bool enable,
@JsonKey(name: "prefer-h3") bool preferH3,
@JsonKey(name: "use-hosts") bool useHosts,
@JsonKey(name: "use-system-hosts") bool useSystemHosts,
@JsonKey(name: "respect-rules") bool respectRules,
bool ipv6,
@JsonKey(name: "default-nameserver") List<String> defaultNameserver,
@JsonKey(name: "enhanced-mode") DnsMode enhancedMode,
@JsonKey(name: "fake-ip-range") String fakeIpRange,
@JsonKey(name: "fake-ip-filter") List<String> fakeIpFilter,
@JsonKey(name: "nameserver-policy") Map<String, String> nameserverPolicy,
List<String> nameserver,
List<String> fallback,
@JsonKey(name: "proxy-server-nameserver")
List<String> proxyServerNameserver,
@JsonKey(name: "fallback-filter") FallbackFilter fallbackFilter});
$FallbackFilterCopyWith<$Res> get fallbackFilter;
}
/// @nodoc
class _$DnsCopyWithImpl<$Res, $Val extends Dns> implements $DnsCopyWith<$Res> {
_$DnsCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? enable = null,
Object? preferH3 = null,
Object? useHosts = null,
Object? useSystemHosts = null,
Object? respectRules = null,
Object? ipv6 = null,
Object? defaultNameserver = null,
Object? enhancedMode = null,
Object? fakeIpRange = null,
Object? fakeIpFilter = null,
Object? nameserverPolicy = null,
Object? nameserver = null,
Object? fallback = null,
Object? proxyServerNameserver = null,
Object? fallbackFilter = null,
}) {
return _then(_value.copyWith(
enable: null == enable
? _value.enable
: enable // ignore: cast_nullable_to_non_nullable
as bool,
preferH3: null == preferH3
? _value.preferH3
: preferH3 // ignore: cast_nullable_to_non_nullable
as bool,
useHosts: null == useHosts
? _value.useHosts
: useHosts // ignore: cast_nullable_to_non_nullable
as bool,
useSystemHosts: null == useSystemHosts
? _value.useSystemHosts
: useSystemHosts // ignore: cast_nullable_to_non_nullable
as bool,
respectRules: null == respectRules
? _value.respectRules
: respectRules // ignore: cast_nullable_to_non_nullable
as bool,
ipv6: null == ipv6
? _value.ipv6
: ipv6 // ignore: cast_nullable_to_non_nullable
as bool,
defaultNameserver: null == defaultNameserver
? _value.defaultNameserver
: defaultNameserver // ignore: cast_nullable_to_non_nullable
as List<String>,
enhancedMode: null == enhancedMode
? _value.enhancedMode
: enhancedMode // ignore: cast_nullable_to_non_nullable
as DnsMode,
fakeIpRange: null == fakeIpRange
? _value.fakeIpRange
: fakeIpRange // ignore: cast_nullable_to_non_nullable
as String,
fakeIpFilter: null == fakeIpFilter
? _value.fakeIpFilter
: fakeIpFilter // ignore: cast_nullable_to_non_nullable
as List<String>,
nameserverPolicy: null == nameserverPolicy
? _value.nameserverPolicy
: nameserverPolicy // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
nameserver: null == nameserver
? _value.nameserver
: nameserver // ignore: cast_nullable_to_non_nullable
as List<String>,
fallback: null == fallback
? _value.fallback
: fallback // ignore: cast_nullable_to_non_nullable
as List<String>,
proxyServerNameserver: null == proxyServerNameserver
? _value.proxyServerNameserver
: proxyServerNameserver // ignore: cast_nullable_to_non_nullable
as List<String>,
fallbackFilter: null == fallbackFilter
? _value.fallbackFilter
: fallbackFilter // ignore: cast_nullable_to_non_nullable
as FallbackFilter,
) as $Val);
}
@override
@pragma('vm:prefer-inline')
$FallbackFilterCopyWith<$Res> get fallbackFilter {
return $FallbackFilterCopyWith<$Res>(_value.fallbackFilter, (value) {
return _then(_value.copyWith(fallbackFilter: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$DnsImplCopyWith<$Res> implements $DnsCopyWith<$Res> {
factory _$$DnsImplCopyWith(_$DnsImpl value, $Res Function(_$DnsImpl) then) =
__$$DnsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{bool enable,
@JsonKey(name: "prefer-h3") bool preferH3,
@JsonKey(name: "use-hosts") bool useHosts,
@JsonKey(name: "use-system-hosts") bool useSystemHosts,
@JsonKey(name: "respect-rules") bool respectRules,
bool ipv6,
@JsonKey(name: "default-nameserver") List<String> defaultNameserver,
@JsonKey(name: "enhanced-mode") DnsMode enhancedMode,
@JsonKey(name: "fake-ip-range") String fakeIpRange,
@JsonKey(name: "fake-ip-filter") List<String> fakeIpFilter,
@JsonKey(name: "nameserver-policy") Map<String, String> nameserverPolicy,
List<String> nameserver,
List<String> fallback,
@JsonKey(name: "proxy-server-nameserver")
List<String> proxyServerNameserver,
@JsonKey(name: "fallback-filter") FallbackFilter fallbackFilter});
@override
$FallbackFilterCopyWith<$Res> get fallbackFilter;
}
/// @nodoc
class __$$DnsImplCopyWithImpl<$Res> extends _$DnsCopyWithImpl<$Res, _$DnsImpl>
implements _$$DnsImplCopyWith<$Res> {
__$$DnsImplCopyWithImpl(_$DnsImpl _value, $Res Function(_$DnsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? enable = null,
Object? preferH3 = null,
Object? useHosts = null,
Object? useSystemHosts = null,
Object? respectRules = null,
Object? ipv6 = null,
Object? defaultNameserver = null,
Object? enhancedMode = null,
Object? fakeIpRange = null,
Object? fakeIpFilter = null,
Object? nameserverPolicy = null,
Object? nameserver = null,
Object? fallback = null,
Object? proxyServerNameserver = null,
Object? fallbackFilter = null,
}) {
return _then(_$DnsImpl(
enable: null == enable
? _value.enable
: enable // ignore: cast_nullable_to_non_nullable
as bool,
preferH3: null == preferH3
? _value.preferH3
: preferH3 // ignore: cast_nullable_to_non_nullable
as bool,
useHosts: null == useHosts
? _value.useHosts
: useHosts // ignore: cast_nullable_to_non_nullable
as bool,
useSystemHosts: null == useSystemHosts
? _value.useSystemHosts
: useSystemHosts // ignore: cast_nullable_to_non_nullable
as bool,
respectRules: null == respectRules
? _value.respectRules
: respectRules // ignore: cast_nullable_to_non_nullable
as bool,
ipv6: null == ipv6
? _value.ipv6
: ipv6 // ignore: cast_nullable_to_non_nullable
as bool,
defaultNameserver: null == defaultNameserver
? _value._defaultNameserver
: defaultNameserver // ignore: cast_nullable_to_non_nullable
as List<String>,
enhancedMode: null == enhancedMode
? _value.enhancedMode
: enhancedMode // ignore: cast_nullable_to_non_nullable
as DnsMode,
fakeIpRange: null == fakeIpRange
? _value.fakeIpRange
: fakeIpRange // ignore: cast_nullable_to_non_nullable
as String,
fakeIpFilter: null == fakeIpFilter
? _value._fakeIpFilter
: fakeIpFilter // ignore: cast_nullable_to_non_nullable
as List<String>,
nameserverPolicy: null == nameserverPolicy
? _value._nameserverPolicy
: nameserverPolicy // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
nameserver: null == nameserver
? _value._nameserver
: nameserver // ignore: cast_nullable_to_non_nullable
as List<String>,
fallback: null == fallback
? _value._fallback
: fallback // ignore: cast_nullable_to_non_nullable
as List<String>,
proxyServerNameserver: null == proxyServerNameserver
? _value._proxyServerNameserver
: proxyServerNameserver // ignore: cast_nullable_to_non_nullable
as List<String>,
fallbackFilter: null == fallbackFilter
? _value.fallbackFilter
: fallbackFilter // ignore: cast_nullable_to_non_nullable
as FallbackFilter,
));
}
}
/// @nodoc
@JsonSerializable()
class _$DnsImpl implements _Dns {
const _$DnsImpl(
{this.enable = true,
@JsonKey(name: "prefer-h3") this.preferH3 = false,
@JsonKey(name: "use-hosts") this.useHosts = true,
@JsonKey(name: "use-system-hosts") this.useSystemHosts = true,
@JsonKey(name: "respect-rules") this.respectRules = true,
this.ipv6 = false,
@JsonKey(name: "default-nameserver")
final List<String> defaultNameserver = const ["223.5.5.5"],
@JsonKey(name: "enhanced-mode") this.enhancedMode = DnsMode.fakeIp,
@JsonKey(name: "fake-ip-range") this.fakeIpRange = "198.18.0.1/16",
@JsonKey(name: "fake-ip-filter") final List<String> fakeIpFilter = const [
"*.lan",
"localhost.ptlogin2.qq.com"
],
@JsonKey(name: "nameserver-policy")
final Map<String, String> nameserverPolicy = const {
"www.baidu.com": "114.114.114.114",
"+.internal.crop.com": "10.0.0.1",
"geosite:cn": "https://doh.pub/dns-query"
},
final List<String> nameserver = const [
"https://doh.pub/dns-query",
"https://dns.alidns.com/dns-query"
],
final List<String> fallback = const ["tls://8.8.4.4", "tls://1.1.1.1"],
@JsonKey(name: "proxy-server-nameserver")
final List<String> proxyServerNameserver = const [
"https://doh.pub/dns-query"
],
@JsonKey(name: "fallback-filter")
this.fallbackFilter = const FallbackFilter()})
: _defaultNameserver = defaultNameserver,
_fakeIpFilter = fakeIpFilter,
_nameserverPolicy = nameserverPolicy,
_nameserver = nameserver,
_fallback = fallback,
_proxyServerNameserver = proxyServerNameserver;
factory _$DnsImpl.fromJson(Map<String, dynamic> json) =>
_$$DnsImplFromJson(json);
@override
@JsonKey()
final bool enable;
@override
@JsonKey(name: "prefer-h3")
final bool preferH3;
@override
@JsonKey(name: "use-hosts")
final bool useHosts;
@override
@JsonKey(name: "use-system-hosts")
final bool useSystemHosts;
@override
@JsonKey(name: "respect-rules")
final bool respectRules;
@override
@JsonKey()
final bool ipv6;
final List<String> _defaultNameserver;
@override
@JsonKey(name: "default-nameserver")
List<String> get defaultNameserver {
if (_defaultNameserver is EqualUnmodifiableListView)
return _defaultNameserver;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_defaultNameserver);
}
@override
@JsonKey(name: "enhanced-mode")
final DnsMode enhancedMode;
@override
@JsonKey(name: "fake-ip-range")
final String fakeIpRange;
final List<String> _fakeIpFilter;
@override
@JsonKey(name: "fake-ip-filter")
List<String> get fakeIpFilter {
if (_fakeIpFilter is EqualUnmodifiableListView) return _fakeIpFilter;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_fakeIpFilter);
}
final Map<String, String> _nameserverPolicy;
@override
@JsonKey(name: "nameserver-policy")
Map<String, String> get nameserverPolicy {
if (_nameserverPolicy is EqualUnmodifiableMapView) return _nameserverPolicy;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_nameserverPolicy);
}
final List<String> _nameserver;
@override
@JsonKey()
List<String> get nameserver {
if (_nameserver is EqualUnmodifiableListView) return _nameserver;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_nameserver);
}
final List<String> _fallback;
@override
@JsonKey()
List<String> get fallback {
if (_fallback is EqualUnmodifiableListView) return _fallback;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_fallback);
}
final List<String> _proxyServerNameserver;
@override
@JsonKey(name: "proxy-server-nameserver")
List<String> get proxyServerNameserver {
if (_proxyServerNameserver is EqualUnmodifiableListView)
return _proxyServerNameserver;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_proxyServerNameserver);
}
@override
@JsonKey(name: "fallback-filter")
final FallbackFilter fallbackFilter;
@override
String toString() {
return 'Dns(enable: $enable, preferH3: $preferH3, useHosts: $useHosts, useSystemHosts: $useSystemHosts, respectRules: $respectRules, ipv6: $ipv6, defaultNameserver: $defaultNameserver, enhancedMode: $enhancedMode, fakeIpRange: $fakeIpRange, fakeIpFilter: $fakeIpFilter, nameserverPolicy: $nameserverPolicy, nameserver: $nameserver, fallback: $fallback, proxyServerNameserver: $proxyServerNameserver, fallbackFilter: $fallbackFilter)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DnsImpl &&
(identical(other.enable, enable) || other.enable == enable) &&
(identical(other.preferH3, preferH3) ||
other.preferH3 == preferH3) &&
(identical(other.useHosts, useHosts) ||
other.useHosts == useHosts) &&
(identical(other.useSystemHosts, useSystemHosts) ||
other.useSystemHosts == useSystemHosts) &&
(identical(other.respectRules, respectRules) ||
other.respectRules == respectRules) &&
(identical(other.ipv6, ipv6) || other.ipv6 == ipv6) &&
const DeepCollectionEquality()
.equals(other._defaultNameserver, _defaultNameserver) &&
(identical(other.enhancedMode, enhancedMode) ||
other.enhancedMode == enhancedMode) &&
(identical(other.fakeIpRange, fakeIpRange) ||
other.fakeIpRange == fakeIpRange) &&
const DeepCollectionEquality()
.equals(other._fakeIpFilter, _fakeIpFilter) &&
const DeepCollectionEquality()
.equals(other._nameserverPolicy, _nameserverPolicy) &&
const DeepCollectionEquality()
.equals(other._nameserver, _nameserver) &&
const DeepCollectionEquality().equals(other._fallback, _fallback) &&
const DeepCollectionEquality()
.equals(other._proxyServerNameserver, _proxyServerNameserver) &&
(identical(other.fallbackFilter, fallbackFilter) ||
other.fallbackFilter == fallbackFilter));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType,
enable,
preferH3,
useHosts,
useSystemHosts,
respectRules,
ipv6,
const DeepCollectionEquality().hash(_defaultNameserver),
enhancedMode,
fakeIpRange,
const DeepCollectionEquality().hash(_fakeIpFilter),
const DeepCollectionEquality().hash(_nameserverPolicy),
const DeepCollectionEquality().hash(_nameserver),
const DeepCollectionEquality().hash(_fallback),
const DeepCollectionEquality().hash(_proxyServerNameserver),
fallbackFilter);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$DnsImplCopyWith<_$DnsImpl> get copyWith =>
__$$DnsImplCopyWithImpl<_$DnsImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$DnsImplToJson(
this,
);
}
}
abstract class _Dns implements Dns {
const factory _Dns(
{final bool enable,
@JsonKey(name: "prefer-h3") final bool preferH3,
@JsonKey(name: "use-hosts") final bool useHosts,
@JsonKey(name: "use-system-hosts") final bool useSystemHosts,
@JsonKey(name: "respect-rules") final bool respectRules,
final bool ipv6,
@JsonKey(name: "default-nameserver") final List<String> defaultNameserver,
@JsonKey(name: "enhanced-mode") final DnsMode enhancedMode,
@JsonKey(name: "fake-ip-range") final String fakeIpRange,
@JsonKey(name: "fake-ip-filter") final List<String> fakeIpFilter,
@JsonKey(name: "nameserver-policy")
final Map<String, String> nameserverPolicy,
final List<String> nameserver,
final List<String> fallback,
@JsonKey(name: "proxy-server-nameserver")
final List<String> proxyServerNameserver,
@JsonKey(name: "fallback-filter")
final FallbackFilter fallbackFilter}) = _$DnsImpl;
factory _Dns.fromJson(Map<String, dynamic> json) = _$DnsImpl.fromJson;
@override
bool get enable;
@override
@JsonKey(name: "prefer-h3")
bool get preferH3;
@override
@JsonKey(name: "use-hosts")
bool get useHosts;
@override
@JsonKey(name: "use-system-hosts")
bool get useSystemHosts;
@override
@JsonKey(name: "respect-rules")
bool get respectRules;
@override
bool get ipv6;
@override
@JsonKey(name: "default-nameserver")
List<String> get defaultNameserver;
@override
@JsonKey(name: "enhanced-mode")
DnsMode get enhancedMode;
@override
@JsonKey(name: "fake-ip-range")
String get fakeIpRange;
@override
@JsonKey(name: "fake-ip-filter")
List<String> get fakeIpFilter;
@override
@JsonKey(name: "nameserver-policy")
Map<String, String> get nameserverPolicy;
@override
List<String> get nameserver;
@override
List<String> get fallback;
@override
@JsonKey(name: "proxy-server-nameserver")
List<String> get proxyServerNameserver;
@override
@JsonKey(name: "fallback-filter")
FallbackFilter get fallbackFilter;
@override
@JsonKey(ignore: true)
_$$DnsImplCopyWith<_$DnsImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -6,35 +6,6 @@ part of '../clash_config.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
Dns _$DnsFromJson(Map<String, dynamic> json) => Dns()
..enable = json['enable'] as bool
..ipv6 = json['ipv6'] as bool
..defaultNameserver = (json['default-nameserver'] as List<dynamic>)
.map((e) => e as String)
.toList()
..enhancedMode = json['enhanced-mode'] as String
..fakeIpRange = json['fake-ip-range'] as String
..useHosts = json['use-hosts'] as bool
..nameserver =
(json['nameserver'] as List<dynamic>).map((e) => e as String).toList()
..fallback =
(json['fallback'] as List<dynamic>).map((e) => e as String).toList()
..fakeIpFilter = (json['fake-ip-filter'] as List<dynamic>)
.map((e) => e as String)
.toList();
Map<String, dynamic> _$DnsToJson(Dns instance) => <String, dynamic>{
'enable': instance.enable,
'ipv6': instance.ipv6,
'default-nameserver': instance.defaultNameserver,
'enhanced-mode': instance.enhancedMode,
'fake-ip-range': instance.fakeIpRange,
'use-hosts': instance.useHosts,
'nameserver': instance.nameserver,
'fallback': instance.fallback,
'fake-ip-filter': instance.fakeIpFilter,
};
ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => ClashConfig() ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => ClashConfig()
..mixedPort = (json['mixed-port'] as num?)?.toInt() ?? 7890 ..mixedPort = (json['mixed-port'] as num?)?.toInt() ?? 7890
..mode = $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule ..mode = $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule
@@ -51,7 +22,7 @@ ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => ClashConfig()
..unifiedDelay = json['unified-delay'] as bool? ?? false ..unifiedDelay = json['unified-delay'] as bool? ?? false
..tcpConcurrent = json['tcp-concurrent'] as bool? ?? false ..tcpConcurrent = json['tcp-concurrent'] as bool? ?? false
..tun = Tun.fromJson(json['tun'] as Map<String, dynamic>) ..tun = Tun.fromJson(json['tun'] as Map<String, dynamic>)
..dns = Dns.fromJson(json['dns'] as Map<String, dynamic>) ..dns = Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>)
..rules = (json['rules'] as List<dynamic>).map((e) => e as String).toList() ..rules = (json['rules'] as List<dynamic>).map((e) => e as String).toList()
..globalRealUa = json['global-real-ua'] as String? ..globalRealUa = json['global-real-ua'] as String?
..geoXUrl = (json['geox-url'] as Map<String, dynamic>?)?.map( ..geoXUrl = (json['geox-url'] as Map<String, dynamic>?)?.map(
@@ -136,3 +107,105 @@ const _$TunStackEnumMap = {
TunStack.system: 'system', TunStack.system: 'system',
TunStack.mixed: 'mixed', TunStack.mixed: 'mixed',
}; };
_$FallbackFilterImpl _$$FallbackFilterImplFromJson(Map<String, dynamic> json) =>
_$FallbackFilterImpl(
geoip: json['geoip'] as bool? ?? true,
geoipCode: json['geoip-code'] as String? ?? "CN",
geosite: (json['geosite'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ["gfw"],
ipcidr: (json['ipcidr'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ["240.0.0.0/4"],
domain: (json['domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ["+.google.com", "+.facebook.com", "+.youtube.com"],
);
Map<String, dynamic> _$$FallbackFilterImplToJson(
_$FallbackFilterImpl instance) =>
<String, dynamic>{
'geoip': instance.geoip,
'geoip-code': instance.geoipCode,
'geosite': instance.geosite,
'ipcidr': instance.ipcidr,
'domain': instance.domain,
};
_$DnsImpl _$$DnsImplFromJson(Map<String, dynamic> json) => _$DnsImpl(
enable: json['enable'] as bool? ?? true,
preferH3: json['prefer-h3'] as bool? ?? false,
useHosts: json['use-hosts'] as bool? ?? true,
useSystemHosts: json['use-system-hosts'] as bool? ?? true,
respectRules: json['respect-rules'] as bool? ?? true,
ipv6: json['ipv6'] as bool? ?? false,
defaultNameserver: (json['default-nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ["223.5.5.5"],
enhancedMode:
$enumDecodeNullable(_$DnsModeEnumMap, json['enhanced-mode']) ??
DnsMode.fakeIp,
fakeIpRange: json['fake-ip-range'] as String? ?? "198.18.0.1/16",
fakeIpFilter: (json['fake-ip-filter'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ["*.lan", "localhost.ptlogin2.qq.com"],
nameserverPolicy:
(json['nameserver-policy'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {
"www.baidu.com": "114.114.114.114",
"+.internal.crop.com": "10.0.0.1",
"geosite:cn": "https://doh.pub/dns-query"
},
nameserver: (json['nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [
"https://doh.pub/dns-query",
"https://dns.alidns.com/dns-query"
],
fallback: (json['fallback'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ["tls://8.8.4.4", "tls://1.1.1.1"],
proxyServerNameserver: (json['proxy-server-nameserver'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const ["https://doh.pub/dns-query"],
fallbackFilter: json['fallback-filter'] == null
? const FallbackFilter()
: FallbackFilter.fromJson(
json['fallback-filter'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$DnsImplToJson(_$DnsImpl instance) => <String, dynamic>{
'enable': instance.enable,
'prefer-h3': instance.preferH3,
'use-hosts': instance.useHosts,
'use-system-hosts': instance.useSystemHosts,
'respect-rules': instance.respectRules,
'ipv6': instance.ipv6,
'default-nameserver': instance.defaultNameserver,
'enhanced-mode': _$DnsModeEnumMap[instance.enhancedMode]!,
'fake-ip-range': instance.fakeIpRange,
'fake-ip-filter': instance.fakeIpFilter,
'nameserver-policy': instance.nameserverPolicy,
'nameserver': instance.nameserver,
'fallback': instance.fallback,
'proxy-server-nameserver': instance.proxyServerNameserver,
'fallback-filter': instance.fallbackFilter,
};
const _$DnsModeEnumMap = {
DnsMode.normal: 'normal',
DnsMode.fakeIp: 'fake-ip',
DnsMode.redirHost: 'redir-host',
DnsMode.hosts: 'hosts',
};

View File

@@ -552,6 +552,189 @@ abstract class _CoreState implements CoreState {
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
VPNState _$VPNStateFromJson(Map<String, dynamic> json) {
return _VPNState.fromJson(json);
}
/// @nodoc
mixin _$VPNState {
AccessControl? get accessControl => throw _privateConstructorUsedError;
VpnProps get vpnProps => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$VPNStateCopyWith<VPNState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $VPNStateCopyWith<$Res> {
factory $VPNStateCopyWith(VPNState value, $Res Function(VPNState) then) =
_$VPNStateCopyWithImpl<$Res, VPNState>;
@useResult
$Res call({AccessControl? accessControl, VpnProps vpnProps});
$AccessControlCopyWith<$Res>? get accessControl;
$VpnPropsCopyWith<$Res> get vpnProps;
}
/// @nodoc
class _$VPNStateCopyWithImpl<$Res, $Val extends VPNState>
implements $VPNStateCopyWith<$Res> {
_$VPNStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? accessControl = freezed,
Object? vpnProps = null,
}) {
return _then(_value.copyWith(
accessControl: freezed == accessControl
? _value.accessControl
: accessControl // ignore: cast_nullable_to_non_nullable
as AccessControl?,
vpnProps: null == vpnProps
? _value.vpnProps
: vpnProps // ignore: cast_nullable_to_non_nullable
as VpnProps,
) as $Val);
}
@override
@pragma('vm:prefer-inline')
$AccessControlCopyWith<$Res>? get accessControl {
if (_value.accessControl == null) {
return null;
}
return $AccessControlCopyWith<$Res>(_value.accessControl!, (value) {
return _then(_value.copyWith(accessControl: value) as $Val);
});
}
@override
@pragma('vm:prefer-inline')
$VpnPropsCopyWith<$Res> get vpnProps {
return $VpnPropsCopyWith<$Res>(_value.vpnProps, (value) {
return _then(_value.copyWith(vpnProps: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$VPNStateImplCopyWith<$Res>
implements $VPNStateCopyWith<$Res> {
factory _$$VPNStateImplCopyWith(
_$VPNStateImpl value, $Res Function(_$VPNStateImpl) then) =
__$$VPNStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({AccessControl? accessControl, VpnProps vpnProps});
@override
$AccessControlCopyWith<$Res>? get accessControl;
@override
$VpnPropsCopyWith<$Res> get vpnProps;
}
/// @nodoc
class __$$VPNStateImplCopyWithImpl<$Res>
extends _$VPNStateCopyWithImpl<$Res, _$VPNStateImpl>
implements _$$VPNStateImplCopyWith<$Res> {
__$$VPNStateImplCopyWithImpl(
_$VPNStateImpl _value, $Res Function(_$VPNStateImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? accessControl = freezed,
Object? vpnProps = null,
}) {
return _then(_$VPNStateImpl(
accessControl: freezed == accessControl
? _value.accessControl
: accessControl // ignore: cast_nullable_to_non_nullable
as AccessControl?,
vpnProps: null == vpnProps
? _value.vpnProps
: vpnProps // ignore: cast_nullable_to_non_nullable
as VpnProps,
));
}
}
/// @nodoc
@JsonSerializable()
class _$VPNStateImpl implements _VPNState {
const _$VPNStateImpl({required this.accessControl, required this.vpnProps});
factory _$VPNStateImpl.fromJson(Map<String, dynamic> json) =>
_$$VPNStateImplFromJson(json);
@override
final AccessControl? accessControl;
@override
final VpnProps vpnProps;
@override
String toString() {
return 'VPNState(accessControl: $accessControl, vpnProps: $vpnProps)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$VPNStateImpl &&
(identical(other.accessControl, accessControl) ||
other.accessControl == accessControl) &&
(identical(other.vpnProps, vpnProps) ||
other.vpnProps == vpnProps));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(runtimeType, accessControl, vpnProps);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$VPNStateImplCopyWith<_$VPNStateImpl> get copyWith =>
__$$VPNStateImplCopyWithImpl<_$VPNStateImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$VPNStateImplToJson(
this,
);
}
}
abstract class _VPNState implements VPNState {
const factory _VPNState(
{required final AccessControl? accessControl,
required final VpnProps vpnProps}) = _$VPNStateImpl;
factory _VPNState.fromJson(Map<String, dynamic> json) =
_$VPNStateImpl.fromJson;
@override
AccessControl? get accessControl;
@override
VpnProps get vpnProps;
@override
@JsonKey(ignore: true)
_$$VPNStateImplCopyWith<_$VPNStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
WindowProps _$WindowPropsFromJson(Map<String, dynamic> json) { WindowProps _$WindowPropsFromJson(Map<String, dynamic> json) {
return _WindowProps.fromJson(json); return _WindowProps.fromJson(json);
} }

View File

@@ -53,7 +53,8 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
..vpnProps = VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?) ..vpnProps = VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?)
..desktopProps = ..desktopProps =
DesktopProps.fromJson(json['desktopProps'] as Map<String, dynamic>?) DesktopProps.fromJson(json['desktopProps'] as Map<String, dynamic>?)
..showLabel = json['showLabel'] as bool? ?? false; ..showLabel = json['showLabel'] as bool? ?? false
..overrideDns = json['overrideDns'] as bool? ?? false;
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
'profiles': instance.profiles, 'profiles': instance.profiles,
@@ -85,6 +86,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
'vpnProps': instance.vpnProps, 'vpnProps': instance.vpnProps,
'desktopProps': instance.desktopProps, 'desktopProps': instance.desktopProps,
'showLabel': instance.showLabel, 'showLabel': instance.showLabel,
'overrideDns': instance.overrideDns,
}; };
const _$ThemeModeEnumMap = { const _$ThemeModeEnumMap = {
@@ -178,6 +180,21 @@ Map<String, dynamic> _$$CoreStateImplToJson(_$CoreStateImpl instance) =>
'onlyProxy': instance.onlyProxy, 'onlyProxy': instance.onlyProxy,
}; };
_$VPNStateImpl _$$VPNStateImplFromJson(Map<String, dynamic> json) =>
_$VPNStateImpl(
accessControl: json['accessControl'] == null
? null
: AccessControl.fromJson(
json['accessControl'] as Map<String, dynamic>),
vpnProps: VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?),
);
Map<String, dynamic> _$$VPNStateImplToJson(_$VPNStateImpl instance) =>
<String, dynamic>{
'accessControl': instance.accessControl,
'vpnProps': instance.vpnProps,
};
_$WindowPropsImpl _$$WindowPropsImplFromJson(Map<String, dynamic> json) => _$WindowPropsImpl _$$WindowPropsImplFromJson(Map<String, dynamic> json) =>
_$WindowPropsImpl( _$WindowPropsImpl(
width: (json['width'] as num?)?.toDouble() ?? 1000, width: (json['width'] as num?)?.toDouble() ?? 1000,

View File

@@ -26,6 +26,8 @@ mixin _$ConfigExtendedParams {
bool get isCompatible => throw _privateConstructorUsedError; bool get isCompatible => throw _privateConstructorUsedError;
@JsonKey(name: "selected-map") @JsonKey(name: "selected-map")
Map<String, String> get selectedMap => throw _privateConstructorUsedError; Map<String, String> get selectedMap => throw _privateConstructorUsedError;
@JsonKey(name: "override-dns")
bool get overrideDns => throw _privateConstructorUsedError;
@JsonKey(name: "test-url") @JsonKey(name: "test-url")
String get testUrl => throw _privateConstructorUsedError; String get testUrl => throw _privateConstructorUsedError;
@@ -45,6 +47,7 @@ abstract class $ConfigExtendedParamsCopyWith<$Res> {
{@JsonKey(name: "is-patch") bool isPatch, {@JsonKey(name: "is-patch") bool isPatch,
@JsonKey(name: "is-compatible") bool isCompatible, @JsonKey(name: "is-compatible") bool isCompatible,
@JsonKey(name: "selected-map") Map<String, String> selectedMap, @JsonKey(name: "selected-map") Map<String, String> selectedMap,
@JsonKey(name: "override-dns") bool overrideDns,
@JsonKey(name: "test-url") String testUrl}); @JsonKey(name: "test-url") String testUrl});
} }
@@ -65,6 +68,7 @@ class _$ConfigExtendedParamsCopyWithImpl<$Res,
Object? isPatch = null, Object? isPatch = null,
Object? isCompatible = null, Object? isCompatible = null,
Object? selectedMap = null, Object? selectedMap = null,
Object? overrideDns = null,
Object? testUrl = null, Object? testUrl = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
@@ -80,6 +84,10 @@ class _$ConfigExtendedParamsCopyWithImpl<$Res,
? _value.selectedMap ? _value.selectedMap
: selectedMap // ignore: cast_nullable_to_non_nullable : selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>, as Map<String, String>,
overrideDns: null == overrideDns
? _value.overrideDns
: overrideDns // ignore: cast_nullable_to_non_nullable
as bool,
testUrl: null == testUrl testUrl: null == testUrl
? _value.testUrl ? _value.testUrl
: testUrl // ignore: cast_nullable_to_non_nullable : testUrl // ignore: cast_nullable_to_non_nullable
@@ -100,6 +108,7 @@ abstract class _$$ConfigExtendedParamsImplCopyWith<$Res>
{@JsonKey(name: "is-patch") bool isPatch, {@JsonKey(name: "is-patch") bool isPatch,
@JsonKey(name: "is-compatible") bool isCompatible, @JsonKey(name: "is-compatible") bool isCompatible,
@JsonKey(name: "selected-map") Map<String, String> selectedMap, @JsonKey(name: "selected-map") Map<String, String> selectedMap,
@JsonKey(name: "override-dns") bool overrideDns,
@JsonKey(name: "test-url") String testUrl}); @JsonKey(name: "test-url") String testUrl});
} }
@@ -117,6 +126,7 @@ class __$$ConfigExtendedParamsImplCopyWithImpl<$Res>
Object? isPatch = null, Object? isPatch = null,
Object? isCompatible = null, Object? isCompatible = null,
Object? selectedMap = null, Object? selectedMap = null,
Object? overrideDns = null,
Object? testUrl = null, Object? testUrl = null,
}) { }) {
return _then(_$ConfigExtendedParamsImpl( return _then(_$ConfigExtendedParamsImpl(
@@ -132,6 +142,10 @@ class __$$ConfigExtendedParamsImplCopyWithImpl<$Res>
? _value._selectedMap ? _value._selectedMap
: selectedMap // ignore: cast_nullable_to_non_nullable : selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>, as Map<String, String>,
overrideDns: null == overrideDns
? _value.overrideDns
: overrideDns // ignore: cast_nullable_to_non_nullable
as bool,
testUrl: null == testUrl testUrl: null == testUrl
? _value.testUrl ? _value.testUrl
: testUrl // ignore: cast_nullable_to_non_nullable : testUrl // ignore: cast_nullable_to_non_nullable
@@ -148,6 +162,7 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
@JsonKey(name: "is-compatible") required this.isCompatible, @JsonKey(name: "is-compatible") required this.isCompatible,
@JsonKey(name: "selected-map") @JsonKey(name: "selected-map")
required final Map<String, String> selectedMap, required final Map<String, String> selectedMap,
@JsonKey(name: "override-dns") required this.overrideDns,
@JsonKey(name: "test-url") required this.testUrl}) @JsonKey(name: "test-url") required this.testUrl})
: _selectedMap = selectedMap; : _selectedMap = selectedMap;
@@ -169,13 +184,16 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
return EqualUnmodifiableMapView(_selectedMap); return EqualUnmodifiableMapView(_selectedMap);
} }
@override
@JsonKey(name: "override-dns")
final bool overrideDns;
@override @override
@JsonKey(name: "test-url") @JsonKey(name: "test-url")
final String testUrl; final String testUrl;
@override @override
String toString() { String toString() {
return 'ConfigExtendedParams(isPatch: $isPatch, isCompatible: $isCompatible, selectedMap: $selectedMap, testUrl: $testUrl)'; return 'ConfigExtendedParams(isPatch: $isPatch, isCompatible: $isCompatible, selectedMap: $selectedMap, overrideDns: $overrideDns, testUrl: $testUrl)';
} }
@override @override
@@ -188,13 +206,15 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
other.isCompatible == isCompatible) && other.isCompatible == isCompatible) &&
const DeepCollectionEquality() const DeepCollectionEquality()
.equals(other._selectedMap, _selectedMap) && .equals(other._selectedMap, _selectedMap) &&
(identical(other.overrideDns, overrideDns) ||
other.overrideDns == overrideDns) &&
(identical(other.testUrl, testUrl) || other.testUrl == testUrl)); (identical(other.testUrl, testUrl) || other.testUrl == testUrl));
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
int get hashCode => Object.hash(runtimeType, isPatch, isCompatible, int get hashCode => Object.hash(runtimeType, isPatch, isCompatible,
const DeepCollectionEquality().hash(_selectedMap), testUrl); const DeepCollectionEquality().hash(_selectedMap), overrideDns, testUrl);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@@ -218,6 +238,7 @@ abstract class _ConfigExtendedParams implements ConfigExtendedParams {
@JsonKey(name: "is-compatible") required final bool isCompatible, @JsonKey(name: "is-compatible") required final bool isCompatible,
@JsonKey(name: "selected-map") @JsonKey(name: "selected-map")
required final Map<String, String> selectedMap, required final Map<String, String> selectedMap,
@JsonKey(name: "override-dns") required final bool overrideDns,
@JsonKey(name: "test-url") required final String testUrl}) = @JsonKey(name: "test-url") required final String testUrl}) =
_$ConfigExtendedParamsImpl; _$ConfigExtendedParamsImpl;
@@ -234,6 +255,9 @@ abstract class _ConfigExtendedParams implements ConfigExtendedParams {
@JsonKey(name: "selected-map") @JsonKey(name: "selected-map")
Map<String, String> get selectedMap; Map<String, String> get selectedMap;
@override @override
@JsonKey(name: "override-dns")
bool get overrideDns;
@override
@JsonKey(name: "test-url") @JsonKey(name: "test-url")
String get testUrl; String get testUrl;
@override @override

View File

@@ -12,6 +12,7 @@ _$ConfigExtendedParamsImpl _$$ConfigExtendedParamsImplFromJson(
isPatch: json['is-patch'] as bool, isPatch: json['is-patch'] as bool,
isCompatible: json['is-compatible'] as bool, isCompatible: json['is-compatible'] as bool,
selectedMap: Map<String, String>.from(json['selected-map'] as Map), selectedMap: Map<String, String>.from(json['selected-map'] as Map),
overrideDns: json['override-dns'] as bool,
testUrl: json['test-url'] as String, testUrl: json['test-url'] as String,
); );
@@ -21,6 +22,7 @@ Map<String, dynamic> _$$ConfigExtendedParamsImplToJson(
'is-patch': instance.isPatch, 'is-patch': instance.isPatch,
'is-compatible': instance.isCompatible, 'is-compatible': instance.isCompatible,
'selected-map': instance.selectedMap, 'selected-map': instance.selectedMap,
'override-dns': instance.overrideDns,
'test-url': instance.testUrl, 'test-url': instance.testUrl,
}; };

View File

@@ -24,6 +24,7 @@ mixin _$Group {
List<Proxy> get all => throw _privateConstructorUsedError; List<Proxy> get all => throw _privateConstructorUsedError;
String? get now => throw _privateConstructorUsedError; String? get now => throw _privateConstructorUsedError;
bool? get hidden => throw _privateConstructorUsedError; bool? get hidden => throw _privateConstructorUsedError;
String get icon => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -41,6 +42,7 @@ abstract class $GroupCopyWith<$Res> {
List<Proxy> all, List<Proxy> all,
String? now, String? now,
bool? hidden, bool? hidden,
String icon,
String name}); String name});
} }
@@ -61,6 +63,7 @@ class _$GroupCopyWithImpl<$Res, $Val extends Group>
Object? all = null, Object? all = null,
Object? now = freezed, Object? now = freezed,
Object? hidden = freezed, Object? hidden = freezed,
Object? icon = null,
Object? name = null, Object? name = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
@@ -80,6 +83,10 @@ class _$GroupCopyWithImpl<$Res, $Val extends Group>
? _value.hidden ? _value.hidden
: hidden // ignore: cast_nullable_to_non_nullable : hidden // ignore: cast_nullable_to_non_nullable
as bool?, as bool?,
icon: null == icon
? _value.icon
: icon // ignore: cast_nullable_to_non_nullable
as String,
name: null == name name: null == name
? _value.name ? _value.name
: name // ignore: cast_nullable_to_non_nullable : name // ignore: cast_nullable_to_non_nullable
@@ -100,6 +107,7 @@ abstract class _$$GroupImplCopyWith<$Res> implements $GroupCopyWith<$Res> {
List<Proxy> all, List<Proxy> all,
String? now, String? now,
bool? hidden, bool? hidden,
String icon,
String name}); String name});
} }
@@ -118,6 +126,7 @@ class __$$GroupImplCopyWithImpl<$Res>
Object? all = null, Object? all = null,
Object? now = freezed, Object? now = freezed,
Object? hidden = freezed, Object? hidden = freezed,
Object? icon = null,
Object? name = null, Object? name = null,
}) { }) {
return _then(_$GroupImpl( return _then(_$GroupImpl(
@@ -137,6 +146,10 @@ class __$$GroupImplCopyWithImpl<$Res>
? _value.hidden ? _value.hidden
: hidden // ignore: cast_nullable_to_non_nullable : hidden // ignore: cast_nullable_to_non_nullable
as bool?, as bool?,
icon: null == icon
? _value.icon
: icon // ignore: cast_nullable_to_non_nullable
as String,
name: null == name name: null == name
? _value.name ? _value.name
: name // ignore: cast_nullable_to_non_nullable : name // ignore: cast_nullable_to_non_nullable
@@ -153,6 +166,7 @@ class _$GroupImpl implements _Group {
final List<Proxy> all = const [], final List<Proxy> all = const [],
this.now, this.now,
this.hidden, this.hidden,
this.icon = "",
required this.name}) required this.name})
: _all = all; : _all = all;
@@ -175,11 +189,14 @@ class _$GroupImpl implements _Group {
@override @override
final bool? hidden; final bool? hidden;
@override @override
@JsonKey()
final String icon;
@override
final String name; final String name;
@override @override
String toString() { String toString() {
return 'Group(type: $type, all: $all, now: $now, hidden: $hidden, name: $name)'; return 'Group(type: $type, all: $all, now: $now, hidden: $hidden, icon: $icon, name: $name)';
} }
@override @override
@@ -191,13 +208,14 @@ class _$GroupImpl implements _Group {
const DeepCollectionEquality().equals(other._all, _all) && const DeepCollectionEquality().equals(other._all, _all) &&
(identical(other.now, now) || other.now == now) && (identical(other.now, now) || other.now == now) &&
(identical(other.hidden, hidden) || other.hidden == hidden) && (identical(other.hidden, hidden) || other.hidden == hidden) &&
(identical(other.icon, icon) || other.icon == icon) &&
(identical(other.name, name) || other.name == name)); (identical(other.name, name) || other.name == name));
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
int get hashCode => Object.hash(runtimeType, type, int get hashCode => Object.hash(runtimeType, type,
const DeepCollectionEquality().hash(_all), now, hidden, name); const DeepCollectionEquality().hash(_all), now, hidden, icon, name);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@@ -219,6 +237,7 @@ abstract class _Group implements Group {
final List<Proxy> all, final List<Proxy> all,
final String? now, final String? now,
final bool? hidden, final bool? hidden,
final String icon,
required final String name}) = _$GroupImpl; required final String name}) = _$GroupImpl;
factory _Group.fromJson(Map<String, dynamic> json) = _$GroupImpl.fromJson; factory _Group.fromJson(Map<String, dynamic> json) = _$GroupImpl.fromJson;
@@ -232,6 +251,8 @@ abstract class _Group implements Group {
@override @override
bool? get hidden; bool? get hidden;
@override @override
String get icon;
@override
String get name; String get name;
@override @override
@JsonKey(ignore: true) @JsonKey(ignore: true)

View File

@@ -14,6 +14,7 @@ _$GroupImpl _$$GroupImplFromJson(Map<String, dynamic> json) => _$GroupImpl(
const [], const [],
now: json['now'] as String?, now: json['now'] as String?,
hidden: json['hidden'] as bool?, hidden: json['hidden'] as bool?,
icon: json['icon'] as String? ?? "",
name: json['name'] as String, name: json['name'] as String,
); );
@@ -23,6 +24,7 @@ Map<String, dynamic> _$$GroupImplToJson(_$GroupImpl instance) =>
'all': instance.all, 'all': instance.all,
'now': instance.now, 'now': instance.now,
'hidden': instance.hidden, 'hidden': instance.hidden,
'icon': instance.icon,
'name': instance.name, 'name': instance.name,
}; };

View File

@@ -3331,6 +3331,142 @@ abstract class _ProxyState implements ProxyState {
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
/// @nodoc
mixin _$HttpOverridesState {
bool get isStart => throw _privateConstructorUsedError;
int get port => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$HttpOverridesStateCopyWith<HttpOverridesState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $HttpOverridesStateCopyWith<$Res> {
factory $HttpOverridesStateCopyWith(
HttpOverridesState value, $Res Function(HttpOverridesState) then) =
_$HttpOverridesStateCopyWithImpl<$Res, HttpOverridesState>;
@useResult
$Res call({bool isStart, int port});
}
/// @nodoc
class _$HttpOverridesStateCopyWithImpl<$Res, $Val extends HttpOverridesState>
implements $HttpOverridesStateCopyWith<$Res> {
_$HttpOverridesStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isStart = null,
Object? port = null,
}) {
return _then(_value.copyWith(
isStart: null == isStart
? _value.isStart
: isStart // ignore: cast_nullable_to_non_nullable
as bool,
port: null == port
? _value.port
: port // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$HttpOverridesStateImplCopyWith<$Res>
implements $HttpOverridesStateCopyWith<$Res> {
factory _$$HttpOverridesStateImplCopyWith(_$HttpOverridesStateImpl value,
$Res Function(_$HttpOverridesStateImpl) then) =
__$$HttpOverridesStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({bool isStart, int port});
}
/// @nodoc
class __$$HttpOverridesStateImplCopyWithImpl<$Res>
extends _$HttpOverridesStateCopyWithImpl<$Res, _$HttpOverridesStateImpl>
implements _$$HttpOverridesStateImplCopyWith<$Res> {
__$$HttpOverridesStateImplCopyWithImpl(_$HttpOverridesStateImpl _value,
$Res Function(_$HttpOverridesStateImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isStart = null,
Object? port = null,
}) {
return _then(_$HttpOverridesStateImpl(
isStart: null == isStart
? _value.isStart
: isStart // ignore: cast_nullable_to_non_nullable
as bool,
port: null == port
? _value.port
: port // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
class _$HttpOverridesStateImpl implements _HttpOverridesState {
const _$HttpOverridesStateImpl({required this.isStart, required this.port});
@override
final bool isStart;
@override
final int port;
@override
String toString() {
return 'HttpOverridesState(isStart: $isStart, port: $port)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$HttpOverridesStateImpl &&
(identical(other.isStart, isStart) || other.isStart == isStart) &&
(identical(other.port, port) || other.port == port));
}
@override
int get hashCode => Object.hash(runtimeType, isStart, port);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$HttpOverridesStateImplCopyWith<_$HttpOverridesStateImpl> get copyWith =>
__$$HttpOverridesStateImplCopyWithImpl<_$HttpOverridesStateImpl>(
this, _$identity);
}
abstract class _HttpOverridesState implements HttpOverridesState {
const factory _HttpOverridesState(
{required final bool isStart,
required final int port}) = _$HttpOverridesStateImpl;
@override
bool get isStart;
@override
int get port;
@override
@JsonKey(ignore: true)
_$$HttpOverridesStateImplCopyWith<_$HttpOverridesStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc /// @nodoc
mixin _$ClashConfigState { mixin _$ClashConfigState {
int get mixedPort => throw _privateConstructorUsedError; int get mixedPort => throw _privateConstructorUsedError;
@@ -3344,6 +3480,7 @@ mixin _$ClashConfigState {
int get keepAliveInterval => throw _privateConstructorUsedError; int get keepAliveInterval => throw _privateConstructorUsedError;
bool get unifiedDelay => throw _privateConstructorUsedError; bool get unifiedDelay => throw _privateConstructorUsedError;
bool get tcpConcurrent => throw _privateConstructorUsedError; bool get tcpConcurrent => throw _privateConstructorUsedError;
Map<String, String> get hosts => throw _privateConstructorUsedError;
Tun get tun => throw _privateConstructorUsedError; Tun get tun => throw _privateConstructorUsedError;
Dns get dns => throw _privateConstructorUsedError; Dns get dns => throw _privateConstructorUsedError;
Map<String, String> get geoXUrl => throw _privateConstructorUsedError; Map<String, String> get geoXUrl => throw _privateConstructorUsedError;
@@ -3373,6 +3510,7 @@ abstract class $ClashConfigStateCopyWith<$Res> {
int keepAliveInterval, int keepAliveInterval,
bool unifiedDelay, bool unifiedDelay,
bool tcpConcurrent, bool tcpConcurrent,
Map<String, String> hosts,
Tun tun, Tun tun,
Dns dns, Dns dns,
Map<String, String> geoXUrl, Map<String, String> geoXUrl,
@@ -3380,6 +3518,7 @@ abstract class $ClashConfigStateCopyWith<$Res> {
String? globalRealUa}); String? globalRealUa});
$TunCopyWith<$Res> get tun; $TunCopyWith<$Res> get tun;
$DnsCopyWith<$Res> get dns;
} }
/// @nodoc /// @nodoc
@@ -3406,6 +3545,7 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState>
Object? keepAliveInterval = null, Object? keepAliveInterval = null,
Object? unifiedDelay = null, Object? unifiedDelay = null,
Object? tcpConcurrent = null, Object? tcpConcurrent = null,
Object? hosts = null,
Object? tun = null, Object? tun = null,
Object? dns = null, Object? dns = null,
Object? geoXUrl = null, Object? geoXUrl = null,
@@ -3457,6 +3597,10 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState>
? _value.tcpConcurrent ? _value.tcpConcurrent
: tcpConcurrent // ignore: cast_nullable_to_non_nullable : tcpConcurrent // ignore: cast_nullable_to_non_nullable
as bool, as bool,
hosts: null == hosts
? _value.hosts
: hosts // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
tun: null == tun tun: null == tun
? _value.tun ? _value.tun
: tun // ignore: cast_nullable_to_non_nullable : tun // ignore: cast_nullable_to_non_nullable
@@ -3487,6 +3631,14 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState>
return _then(_value.copyWith(tun: value) as $Val); return _then(_value.copyWith(tun: value) as $Val);
}); });
} }
@override
@pragma('vm:prefer-inline')
$DnsCopyWith<$Res> get dns {
return $DnsCopyWith<$Res>(_value.dns, (value) {
return _then(_value.copyWith(dns: value) as $Val);
});
}
} }
/// @nodoc /// @nodoc
@@ -3509,6 +3661,7 @@ abstract class _$$ClashConfigStateImplCopyWith<$Res>
int keepAliveInterval, int keepAliveInterval,
bool unifiedDelay, bool unifiedDelay,
bool tcpConcurrent, bool tcpConcurrent,
Map<String, String> hosts,
Tun tun, Tun tun,
Dns dns, Dns dns,
Map<String, String> geoXUrl, Map<String, String> geoXUrl,
@@ -3517,6 +3670,8 @@ abstract class _$$ClashConfigStateImplCopyWith<$Res>
@override @override
$TunCopyWith<$Res> get tun; $TunCopyWith<$Res> get tun;
@override
$DnsCopyWith<$Res> get dns;
} }
/// @nodoc /// @nodoc
@@ -3541,6 +3696,7 @@ class __$$ClashConfigStateImplCopyWithImpl<$Res>
Object? keepAliveInterval = null, Object? keepAliveInterval = null,
Object? unifiedDelay = null, Object? unifiedDelay = null,
Object? tcpConcurrent = null, Object? tcpConcurrent = null,
Object? hosts = null,
Object? tun = null, Object? tun = null,
Object? dns = null, Object? dns = null,
Object? geoXUrl = null, Object? geoXUrl = null,
@@ -3592,6 +3748,10 @@ class __$$ClashConfigStateImplCopyWithImpl<$Res>
? _value.tcpConcurrent ? _value.tcpConcurrent
: tcpConcurrent // ignore: cast_nullable_to_non_nullable : tcpConcurrent // ignore: cast_nullable_to_non_nullable
as bool, as bool,
hosts: null == hosts
? _value._hosts
: hosts // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
tun: null == tun tun: null == tun
? _value.tun ? _value.tun
: tun // ignore: cast_nullable_to_non_nullable : tun // ignore: cast_nullable_to_non_nullable
@@ -3631,12 +3791,14 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
required this.keepAliveInterval, required this.keepAliveInterval,
required this.unifiedDelay, required this.unifiedDelay,
required this.tcpConcurrent, required this.tcpConcurrent,
required final Map<String, String> hosts,
required this.tun, required this.tun,
required this.dns, required this.dns,
required final Map<String, String> geoXUrl, required final Map<String, String> geoXUrl,
required final List<String> rules, required final List<String> rules,
required this.globalRealUa}) required this.globalRealUa})
: _geoXUrl = geoXUrl, : _hosts = hosts,
_geoXUrl = geoXUrl,
_rules = rules; _rules = rules;
@override @override
@@ -3661,6 +3823,14 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
final bool unifiedDelay; final bool unifiedDelay;
@override @override
final bool tcpConcurrent; final bool tcpConcurrent;
final Map<String, String> _hosts;
@override
Map<String, String> get hosts {
if (_hosts is EqualUnmodifiableMapView) return _hosts;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_hosts);
}
@override @override
final Tun tun; final Tun tun;
@override @override
@@ -3686,7 +3856,7 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
@override @override
String toString() { String toString() {
return 'ClashConfigState(mixedPort: $mixedPort, allowLan: $allowLan, ipv6: $ipv6, geodataLoader: $geodataLoader, logLevel: $logLevel, externalController: $externalController, mode: $mode, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, geoXUrl: $geoXUrl, rules: $rules, globalRealUa: $globalRealUa)'; return 'ClashConfigState(mixedPort: $mixedPort, allowLan: $allowLan, ipv6: $ipv6, geodataLoader: $geodataLoader, logLevel: $logLevel, externalController: $externalController, mode: $mode, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, hosts: $hosts, tun: $tun, dns: $dns, geoXUrl: $geoXUrl, rules: $rules, globalRealUa: $globalRealUa)';
} }
@override @override
@@ -3714,6 +3884,7 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
other.unifiedDelay == unifiedDelay) && other.unifiedDelay == unifiedDelay) &&
(identical(other.tcpConcurrent, tcpConcurrent) || (identical(other.tcpConcurrent, tcpConcurrent) ||
other.tcpConcurrent == tcpConcurrent) && other.tcpConcurrent == tcpConcurrent) &&
const DeepCollectionEquality().equals(other._hosts, _hosts) &&
(identical(other.tun, tun) || other.tun == tun) && (identical(other.tun, tun) || other.tun == tun) &&
(identical(other.dns, dns) || other.dns == dns) && (identical(other.dns, dns) || other.dns == dns) &&
const DeepCollectionEquality().equals(other._geoXUrl, _geoXUrl) && const DeepCollectionEquality().equals(other._geoXUrl, _geoXUrl) &&
@@ -3736,6 +3907,7 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
keepAliveInterval, keepAliveInterval,
unifiedDelay, unifiedDelay,
tcpConcurrent, tcpConcurrent,
const DeepCollectionEquality().hash(_hosts),
tun, tun,
dns, dns,
const DeepCollectionEquality().hash(_geoXUrl), const DeepCollectionEquality().hash(_geoXUrl),
@@ -3763,6 +3935,7 @@ abstract class _ClashConfigState implements ClashConfigState {
required final int keepAliveInterval, required final int keepAliveInterval,
required final bool unifiedDelay, required final bool unifiedDelay,
required final bool tcpConcurrent, required final bool tcpConcurrent,
required final Map<String, String> hosts,
required final Tun tun, required final Tun tun,
required final Dns dns, required final Dns dns,
required final Map<String, String> geoXUrl, required final Map<String, String> geoXUrl,
@@ -3792,6 +3965,8 @@ abstract class _ClashConfigState implements ClashConfigState {
@override @override
bool get tcpConcurrent; bool get tcpConcurrent;
@override @override
Map<String, String> get hosts;
@override
Tun get tun; Tun get tun;
@override @override
Dns get dns; Dns get dns;

View File

@@ -15,6 +15,7 @@ class Group with _$Group {
@Default([]) List<Proxy> all, @Default([]) List<Proxy> all,
String? now, String? now,
bool? hidden, bool? hidden,
@Default("") String icon,
required String name, required String name,
}) = _Group; }) = _Group;

View File

@@ -4,7 +4,6 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lpinyin/lpinyin.dart';
part 'generated/selector.freezed.dart'; part 'generated/selector.freezed.dart';
@@ -162,8 +161,8 @@ extension PackageListSelectorStateExt on PackageListSelectorState {
return switch (sort) { return switch (sort) {
AccessSortType.none => 0, AccessSortType.none => 0,
AccessSortType.name => other.sortByChar( AccessSortType.name => other.sortByChar(
PinyinHelper.getPinyin(a.label), other.getPinyin(a.label),
PinyinHelper.getPinyin(b.label), other.getPinyin(b.label),
), ),
AccessSortType.time => AccessSortType.time =>
a.firstInstallTime.compareTo(b.firstInstallTime), a.firstInstallTime.compareTo(b.firstInstallTime),
@@ -215,6 +214,14 @@ class ProxyState with _$ProxyState {
}) = _ProxyState; }) = _ProxyState;
} }
@freezed
class HttpOverridesState with _$HttpOverridesState {
const factory HttpOverridesState({
required bool isStart,
required int port,
}) = _HttpOverridesState;
}
@freezed @freezed
class ClashConfigState with _$ClashConfigState { class ClashConfigState with _$ClashConfigState {
const factory ClashConfigState({ const factory ClashConfigState({
@@ -229,6 +236,7 @@ class ClashConfigState with _$ClashConfigState {
required int keepAliveInterval, required int keepAliveInterval,
required bool unifiedDelay, required bool unifiedDelay,
required bool tcpConcurrent, required bool tcpConcurrent,
required HostsMap hosts,
required Tun tun, required Tun tun,
required Dns dns, required Dns dns,
required GeoXMap geoXUrl, required GeoXMap geoXUrl,

View File

@@ -59,6 +59,7 @@ class GlobalState {
isPatch: isPatch, isPatch: isPatch,
isCompatible: true, isCompatible: true,
selectedMap: config.currentSelectedMap, selectedMap: config.currentSelectedMap,
overrideDns: config.overrideDns,
testUrl: config.testUrl, testUrl: config.testUrl,
), ),
), ),

View File

@@ -39,6 +39,7 @@ class _ClashContainerState extends State<ClashContainer>
tcpConcurrent: clashConfig.tcpConcurrent, tcpConcurrent: clashConfig.tcpConcurrent,
tun: clashConfig.tun, tun: clashConfig.tun,
dns: clashConfig.dns, dns: clashConfig.dns,
hosts: clashConfig.hosts,
geoXUrl: clashConfig.geoXUrl, geoXUrl: clashConfig.geoXUrl,
rules: clashConfig.rules, rules: clashConfig.rules,
globalRealUa: clashConfig.globalRealUa, globalRealUa: clashConfig.globalRealUa,
@@ -137,7 +138,7 @@ class _ClashContainerState extends State<ClashContainer>
if (log.logLevel == LogLevel.error) { if (log.logLevel == LogLevel.error) {
globalState.appController.showSnackBar(log.payload ?? ''); globalState.appController.showSnackBar(log.payload ?? '');
} }
debugPrint("$log"); // debugPrint("$log");
super.onLog(log); super.onLog(log);
} }

293
lib/widgets/input.dart Normal file
View File

@@ -0,0 +1,293 @@
import 'package:fl_clash/common/constant.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import '../common/app_localizations.dart';
import 'card.dart';
import 'float_layout.dart';
import 'list.dart';
class OptionsDialog<T> extends StatelessWidget {
final String title;
final List<T> options;
final T value;
final String Function(T value) textBuilder;
const OptionsDialog({
super.key,
required this.title,
required this.options,
required this.textBuilder,
required this.value,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(title),
contentPadding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 16,
),
content: SizedBox(
width: 250,
child: Wrap(
children: [
for (final option in options)
ListItem.radio(
delegate: RadioDelegate(
value: option,
groupValue: value,
onChanged: (T? value) {
Navigator.of(context).pop(value);
},
),
title: Text(
this.textBuilder(option),
),
),
],
),
),
);
}
}
class InputDialog extends StatefulWidget {
final String title;
final String value;
final String? suffixText;
const InputDialog({
super.key,
required this.title,
required this.value,
this.suffixText,
});
@override
State<InputDialog> createState() => _InputDialogState();
}
class _InputDialogState extends State<InputDialog> {
late TextEditingController textController;
String get value => widget.value;
String get title => widget.title;
String? get suffixText => widget.suffixText;
@override
void initState() {
super.initState();
textController = TextEditingController(
text: value,
);
}
_handleUpdate() async {
final text = textController.value.text;
if (text.isEmpty) return;
Navigator.of(context).pop<String>(text);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(title),
content: SizedBox(
width: 300,
child: Wrap(
runSpacing: 16,
children: [
TextField(
maxLines: 1,
minLines: 1,
controller: textController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
suffixText: suffixText,
),
),
],
),
),
actions: [
TextButton(
onPressed: _handleUpdate,
child: Text(appLocalizations.submit),
)
],
);
}
}
class UpdatePage<T> extends StatelessWidget {
final String title;
final Iterable<T> items;
final Widget Function(T item) titleBuilder;
final Widget Function(T item)? subtitleBuilder;
final Function(T item) onAdd;
final Function(T item) onRemove;
final bool isMap;
const UpdatePage({
super.key,
required this.title,
required this.items,
required this.titleBuilder,
required this.onRemove,
required this.onAdd,
this.isMap = false,
this.subtitleBuilder,
});
@override
Widget build(BuildContext context) {
return FloatLayout(
floatingWidget: FloatWrapper(
child: FloatingActionButton(
onPressed: () async {
final value = await globalState.showCommonDialog<T>(
child: AddDialog(
isMap: isMap,
title: title,
),
);
if (value == null) return;
onAdd(value);
},
child: const Icon(Icons.add),
),
),
child: ListView.builder(
padding: const EdgeInsets.only(
bottom: 16 + 64,
left: 16,
right: 16,
),
itemCount: items.length,
itemBuilder: (_, index) {
final e = items.toList()[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: CommonCard(
child: ListItem(
title: titleBuilder(e),
subtitle: subtitleBuilder != null ? subtitleBuilder!(e) : null,
trailing: IconButton(
icon: const Icon(Icons.delete_outline),
onPressed: () {
onRemove(e);
},
),
),
onPressed: () {},
),
);
},
),
);
}
}
class AddDialog extends StatefulWidget {
final String title;
final bool isMap;
const AddDialog({
super.key,
required this.title,
required this.isMap,
});
@override
State<AddDialog> createState() => _AddDialogState();
}
class _AddDialogState extends State<AddDialog> {
late TextEditingController keyController;
late TextEditingController valueController;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
keyController = TextEditingController();
valueController = TextEditingController();
}
_submit() {
if (!_formKey.currentState!.validate()) return;
if (widget.isMap) {
Navigator.of(context).pop<MapEntry<String, String>>(
MapEntry(
keyController.text,
valueController.text,
),
);
} else {
Navigator.of(context).pop<String>(
valueController.text,
);
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(widget.title),
content: Form(
key: _formKey,
child: SizedBox(
width: dialogCommonWidth,
child: Wrap(
runSpacing: 16,
children: [
if (widget.isMap)
TextFormField(
maxLines: 2,
minLines: 1,
controller: keyController,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.key),
border: const OutlineInputBorder(),
labelText: appLocalizations.key,
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.keyNotEmpty;
}
return null;
},
),
TextFormField(
maxLines: 3,
minLines: 1,
controller: valueController,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.label),
border: const OutlineInputBorder(),
labelText: appLocalizations.value,
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.valueNotEmpty;
}
return null;
},
),
],
),
),
),
actions: [
TextButton(
onPressed: _submit,
child: Text(appLocalizations.confirm),
)
],
);
}
}

View File

@@ -5,6 +5,7 @@ import 'package:fl_clash/widgets/open_container.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'card.dart'; import 'card.dart';
import 'input.dart';
import 'sheet.dart'; import 'sheet.dart';
import 'scaffold.dart'; import 'scaffold.dart';
@@ -48,11 +49,13 @@ class OpenDelegate extends Delegate {
final Widget widget; final Widget widget;
final String title; final String title;
final double? extendPageWidth; final double? extendPageWidth;
final bool isBlur;
const OpenDelegate({ const OpenDelegate({
required this.title, required this.title,
required this.widget, required this.widget,
this.extendPageWidth, this.extendPageWidth,
this.isBlur = true,
}); });
} }
@@ -68,6 +71,36 @@ class NextDelegate extends Delegate {
}); });
} }
class OptionsDelegate<T> extends Delegate {
final List<T> options;
final String title;
final T value;
final String Function(T value) textBuilder;
final Function(T? value) onChanged;
const OptionsDelegate({
required this.title,
required this.options,
required this.textBuilder,
required this.value,
required this.onChanged,
});
}
class InputDelegate extends Delegate {
final String title;
final String value;
final String? suffixText;
final Function(String? value) onChanged;
const InputDelegate({
required this.title,
required this.value,
this.suffixText,
required this.onChanged,
});
}
class ListItem<T> extends StatelessWidget { class ListItem<T> extends StatelessWidget {
final Widget? leading; final Widget? leading;
final Widget title; final Widget title;
@@ -106,6 +139,32 @@ class ListItem<T> extends StatelessWidget {
this.tileTitleAlignment = ListTileTitleAlignment.center, this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null; }) : onTap = null;
const ListItem.options({
super.key,
required this.title,
this.subtitle,
this.leading,
this.padding = const EdgeInsets.symmetric(horizontal: 16),
this.trailing,
required OptionsDelegate<T> this.delegate,
this.horizontalTitleGap,
this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null;
const ListItem.input({
super.key,
required this.title,
this.subtitle,
this.leading,
this.padding = const EdgeInsets.symmetric(horizontal: 16),
this.trailing,
required InputDelegate this.delegate,
this.horizontalTitleGap,
this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null;
const ListItem.next({ const ListItem.next({
super.key, super.key,
required this.title, required this.title,
@@ -125,7 +184,7 @@ class ListItem<T> extends StatelessWidget {
this.subtitle, this.subtitle,
this.leading, this.leading,
this.padding = const EdgeInsets.only(left: 16, right: 8), this.padding = const EdgeInsets.only(left: 16, right: 8),
required CheckboxDelegate this.delegate, required CheckboxDelegate<T> this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center, this.tileTitleAlignment = ListTileTitleAlignment.center,
@@ -138,7 +197,7 @@ class ListItem<T> extends StatelessWidget {
this.subtitle, this.subtitle,
this.leading, this.leading,
this.padding = const EdgeInsets.only(left: 16, right: 8), this.padding = const EdgeInsets.only(left: 16, right: 8),
required SwitchDelegate this.delegate, required SwitchDelegate<T> this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center, this.tileTitleAlignment = ListTileTitleAlignment.center,
@@ -227,6 +286,7 @@ class ListItem<T> extends StatelessWidget {
body: child, body: child,
title: openDelegate.title, title: openDelegate.title,
extendPageWidth: openDelegate.extendPageWidth, extendPageWidth: openDelegate.extendPageWidth,
isBlur: openDelegate.isBlur,
); );
return; return;
} }
@@ -247,6 +307,37 @@ class ListItem<T> extends StatelessWidget {
}, },
); );
} }
if (delegate is OptionsDelegate) {
final optionsDelegate = delegate as OptionsDelegate<T>;
return _buildListTile(
onTap: () async {
final value = await globalState.showCommonDialog<T>(
child: OptionsDialog<T>(
title: optionsDelegate.title,
options: optionsDelegate.options,
textBuilder: optionsDelegate.textBuilder,
value: optionsDelegate.value,
),
);
optionsDelegate.onChanged(value);
},
);
}
if (delegate is InputDelegate) {
final inputDelegate = delegate as InputDelegate;
return _buildListTile(
onTap: () async {
final value = await globalState.showCommonDialog<String>(
child: InputDialog(
title: inputDelegate.title,
value: inputDelegate.value,
suffixText: inputDelegate.suffixText,
),
);
inputDelegate.onChanged(value);
},
);
}
if (delegate is NextDelegate) { if (delegate is NextDelegate) {
final nextDelegate = delegate as NextDelegate; final nextDelegate = delegate as NextDelegate;
return _buildListTile( return _buildListTile(
@@ -423,5 +514,8 @@ Widget generateListView(List<Widget> items) {
return ListView.builder( return ListView.builder(
itemCount: items.length, itemCount: items.length,
itemBuilder: (_, index) => items[index], itemBuilder: (_, index) => items[index],
padding: const EdgeInsets.only(
bottom: 16,
),
); );
} }

View File

@@ -286,23 +286,6 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
final _FlippableTweenSequence<double> _closedOpacityTween; final _FlippableTweenSequence<double> _closedOpacityTween;
final _FlippableTweenSequence<double> _openOpacityTween; final _FlippableTweenSequence<double> _openOpacityTween;
late _FlippableTweenSequence<Color?> _colorTween; late _FlippableTweenSequence<Color?> _colorTween;
static final TweenSequence<Color?> _scrimFadeInTween = TweenSequence<Color?>(
<TweenSequenceItem<Color?>>[
TweenSequenceItem<Color?>(
tween: ColorTween(begin: Colors.transparent, end: Colors.black54),
weight: 1 / 5,
),
TweenSequenceItem<Color>(
tween: ConstantTween<Color>(Colors.black54),
weight: 4 / 5,
),
],
);
static final Tween<Color?> _scrimFadeOutTween = ColorTween(
begin: Colors.transparent,
end: Colors.black54,
);
final GlobalKey _openBuilderKey = GlobalKey(); final GlobalKey _openBuilderKey = GlobalKey();
final RectTween _rectTween = RectTween(); final RectTween _rectTween = RectTween();
@@ -486,20 +469,17 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
closedOpacityTween = _closedOpacityTween; closedOpacityTween = _closedOpacityTween;
openOpacityTween = _openOpacityTween; openOpacityTween = _openOpacityTween;
colorTween = _colorTween; colorTween = _colorTween;
scrimTween = _scrimFadeInTween;
break; break;
case AnimationStatus.reverse: case AnimationStatus.reverse:
if (_transitionWasInterrupted) { if (_transitionWasInterrupted) {
closedOpacityTween = _closedOpacityTween; closedOpacityTween = _closedOpacityTween;
openOpacityTween = _openOpacityTween; openOpacityTween = _openOpacityTween;
colorTween = _colorTween; colorTween = _colorTween;
scrimTween = _scrimFadeInTween;
break; break;
} }
closedOpacityTween = _closedOpacityTween.flipped; closedOpacityTween = _closedOpacityTween.flipped;
openOpacityTween = _openOpacityTween.flipped; openOpacityTween = _openOpacityTween.flipped;
colorTween = _colorTween.flipped; colorTween = _colorTween.flipped;
scrimTween = _scrimFadeOutTween;
break; break;
case AnimationStatus.completed: case AnimationStatus.completed:
assert(false); // Unreachable. assert(false); // Unreachable.
@@ -508,12 +488,9 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
assert(colorTween != null); assert(colorTween != null);
assert(closedOpacityTween != null); assert(closedOpacityTween != null);
assert(openOpacityTween != null); assert(openOpacityTween != null);
assert(scrimTween != null);
final Rect rect = _rectTween.evaluate(curvedAnimation)!; final Rect rect = _rectTween.evaluate(curvedAnimation)!;
return SizedBox.expand( return SizedBox.expand(
child: Container(
color: scrimTween!.evaluate(curvedAnimation),
child: Align( child: Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: Transform.translate( child: Transform.translate(
@@ -580,7 +557,6 @@ class _OpenContainerRoute<T> extends ModalRoute<T> {
), ),
), ),
), ),
),
); );
}, },
), ),

View File

@@ -11,6 +11,7 @@ showExtendPage(
required String title, required String title,
double? extendPageWidth, double? extendPageWidth,
bool forceNotSide = false, bool forceNotSide = false,
bool isBlur = true,
Widget? action, Widget? action,
}) { }) {
final NavigatorState navigator = Navigator.of(context); final NavigatorState navigator = Navigator.of(context);
@@ -21,6 +22,17 @@ showExtendPage(
); );
final isMobile = final isMobile =
globalState.appController.appState.viewMode == ViewMode.mobile; globalState.appController.appState.viewMode == ViewMode.mobile;
if (isMobile) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => CommonScaffold(
title: title,
body: uniqueBody,
),
),
);
return;
}
final isNotSide = isMobile || forceNotSide; final isNotSide = isMobile || forceNotSide;
navigator.push( navigator.push(
ModalSideSheetRoute( ModalSideSheetRoute(
@@ -46,7 +58,7 @@ showExtendPage(
); );
}, },
constraints: const BoxConstraints(), constraints: const BoxConstraints(),
filter: filter, filter: isBlur ? filter : null,
), ),
); );
} }

View File

@@ -74,36 +74,6 @@ class _TrayContainerState extends State<TrayContainer> with TrayListener {
); );
menuItems.add(startMenuItem); menuItems.add(startMenuItem);
menuItems.add(MenuItem.separator()); menuItems.add(MenuItem.separator());
// for (final group in state.groups) {
// List<MenuItem> subMenuItems = [];
// final isCurrentGroup = group.name == state.currentGroupName;
// for (final proxy in group.all) {
// final isCurrentProxy = proxy.name == state.currentProxyName;
// subMenuItems.add(
// MenuItem.checkbox(
// label: proxy.name,
// checked: isCurrentGroup && isCurrentProxy,
// onClick: (_) {
// final config = globalState.appController.config;
// config.currentProfile?.groupName = group.name;
// config.currentProfile?.proxyName = proxy.name;
// config.update();
// globalState.appController.changeProxy();
// }),
// );
// }
// menuItems.add(
// MenuItem.submenu(
// label: group.name,
// submenu: Menu(
// items: subMenuItems,
// ),
// ),
// );
// }
// if (state.groups.isNotEmpty) {
// menuItems.add(MenuItem.separator());
// }
for (final mode in Mode.values) { for (final mode in Mode.values) {
menuItems.add( menuItems.add(
MenuItem.checkbox( MenuItem.checkbox(
@@ -116,7 +86,6 @@ class _TrayContainerState extends State<TrayContainer> with TrayListener {
); );
} }
menuItems.add(MenuItem.separator()); menuItems.add(MenuItem.separator());
if (state.isStart) { if (state.isStart) {
menuItems.add( menuItems.add(
MenuItem.checkbox( MenuItem.checkbox(
@@ -142,7 +111,6 @@ class _TrayContainerState extends State<TrayContainer> with TrayListener {
); );
menuItems.add(MenuItem.separator()); menuItems.add(MenuItem.separator());
} }
final autoStartMenuItem = MenuItem.checkbox( final autoStartMenuItem = MenuItem.checkbox(
label: appLocalizations.autoLaunch, label: appLocalizations.autoLaunch,
onClick: (_) async { onClick: (_) async {

View File

@@ -0,0 +1,58 @@
import 'package:fl_clash/common/app_localizations.dart';
import 'package:fl_clash/models/config.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../common/function.dart';
class VpnContainer extends StatefulWidget {
final Widget child;
const VpnContainer({
super.key,
required this.child,
});
@override
State<VpnContainer> createState() => _VpnContainerState();
}
class _VpnContainerState extends State<VpnContainer> {
Function? vpnTipDebounce;
showTip() {
vpnTipDebounce ??= debounce<Function()>(() async {
WidgetsBinding.instance.addPostFrameCallback((_) {
final appState = globalState.appController.appState;
if (appState.isStart) {
globalState.showSnackBar(
context,
message: appLocalizations.vpnTip,
);
}
});
});
vpnTipDebounce!();
}
@override
Widget build(BuildContext context) {
return Selector<Config, VPNState>(
selector: (_, config) => VPNState(
accessControl: config.accessControl,
vpnProps: config.vpnProps,
),
shouldRebuild: (prev,next){
if(prev != next){
showTip();
}
return prev != next;
},
builder: (_, __, child) {
return child!;
},
child: widget.child,
);
}
}

View File

@@ -26,3 +26,5 @@ export 'text.dart';
export 'connection_item.dart'; export 'connection_item.dart';
export 'builder.dart'; export 'builder.dart';
export 'setting.dart'; export 'setting.dart';
export 'vpn_container.dart';
export 'input.dart';

View File

@@ -13,6 +13,7 @@ import package_info_plus
import path_provider_foundation import path_provider_foundation
import screen_retriever import screen_retriever
import shared_preferences_foundation import shared_preferences_foundation
import sqflite
import tray_manager import tray_manager
import url_launcher_macos import url_launcher_macos
import window_manager import window_manager
@@ -26,6 +27,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))

View File

@@ -129,6 +129,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.9.2" version: "8.9.2"
cached_network_image:
dependency: "direct main"
description:
name: cached_network_image
sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819"
url: "https://pub.dev"
source: hosted
version: "3.4.0"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@@ -197,18 +221,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.4+1" version: "0.3.4+2"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
name: crypto name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.5"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
@@ -221,18 +245,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: dio name: dio
sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.0+1" version: "5.6.0"
dio_web_adapter: dio_web_adapter:
dependency: transitive dependency: transitive
description: description:
name: dio_web_adapter name: dio_web_adapter
sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.1" version: "2.0.0"
dynamic_color: dynamic_color:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -261,10 +285,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: ffi name: ffi
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
ffigen: ffigen:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -285,10 +309,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: file_picker name: file_picker
sha256: "824f5b9f389bfc4dddac3dea76cd70c51092d9dff0b2ece7ef4f53db8547d258" sha256: "825aec673606875c33cd8d3c4083f1a3c3999015a84178b317b7ef396b7384f3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.0.6" version: "8.0.7"
file_selector_linux: file_selector_linux:
dependency: transitive dependency: transitive
description: description:
@@ -334,6 +358,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -351,10 +383,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.20" version: "2.0.22"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@@ -457,18 +489,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: a26dc9a03fe042440c1e4be554fb0fceae2bf6d887d7467fc48c688fa4a81889 sha256: "8c5abf0dcc24fe6e8e0b4a5c0b51a5cf30cefdf6407a3213dae61edc75a70f56"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.12+7" version: "0.8.12+12"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
name: image_picker_for_web name: image_picker_for_web
sha256: "5d6eb13048cd47b60dbf1a5495424dea226c5faf3950e20bf8120a58efb5b5f3" sha256: "65d94623e15372c5c51bebbcb820848d7bcb323836e12dfdba60b5d3a8b39e50"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.4" version: "3.0.5"
image_picker_ios: image_picker_ios:
dependency: transitive dependency: transitive
description: description:
@@ -665,10 +697,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: mobile_scanner name: mobile_scanner
sha256: b8c0e9afcfd52534f85ec666f3d52156f560b5e6c25b1e3d4fe2087763607926 sha256: "6ac2913ad98c83f558d2c8a55bc8f511bdcf28b86639701c04b04c16da1e9ee1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.1" version: "5.2.1"
nested: nested:
dependency: transitive dependency: transitive
description: description:
@@ -677,6 +709,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
octo_image:
dependency: transitive
description:
name: octo_image
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
@@ -697,10 +737,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: package_info_plus_platform_interface name: package_info_plus_platform_interface
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
path: path:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -713,18 +753,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.3" version: "2.1.4"
path_provider_android: path_provider_android:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e" sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.7" version: "2.2.10"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
@@ -789,6 +829,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.1" version: "1.5.1"
process_run:
dependency: "direct main"
description:
name: process_run
sha256: c917dfb5f7afad4c7485bc00a4df038621248fce046105020cea276d1a87c820
url: "https://pub.dev"
source: hosted
version: "1.1.0"
provider: provider:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -844,6 +892,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.3" version: "0.0.3"
rxdart:
dependency: transitive
description:
name: rxdart
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
url: "https://pub.dev"
source: hosted
version: "0.28.0"
screen_retriever: screen_retriever:
dependency: transitive dependency: transitive
description: description:
@@ -856,58 +912,58 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.3" version: "2.3.2"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.3" version: "2.3.2"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_foundation name: shared_preferences_foundation
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.5.2"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_linux name: shared_preferences_linux
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.4.1"
shared_preferences_platform_interface: shared_preferences_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_platform_interface name: shared_preferences_platform_interface
sha256: "034650b71e73629ca08a0bd789fd1d83cc63c2d1e405946f7cef7bc37432f93a" sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.4.1"
shared_preferences_web: shared_preferences_web:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_web name: shared_preferences_web
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.0" version: "2.4.2"
shared_preferences_windows: shared_preferences_windows:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_windows name: shared_preferences_windows
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.4.1"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@@ -961,6 +1017,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.10.0"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
url: "https://pub.dev"
source: hosted
version: "2.3.3+1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@@ -993,6 +1073,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
url: "https://pub.dev"
source: hosted
version: "3.1.0+1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@@ -1045,10 +1133,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: "95d8027db36a0e52caf55680f91e33ea6aa12a3ce608c90b06f4e429a21067ac" sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.5" version: "6.3.9"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
@@ -1061,10 +1149,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_linux name: url_launcher_linux
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.1" version: "3.2.0"
url_launcher_macos: url_launcher_macos:
dependency: transitive dependency: transitive
description: description:
@@ -1085,10 +1173,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.1" version: "2.3.3"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
@@ -1097,6 +1185,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
uuid:
dependency: transitive
description:
name: uuid
sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77
url: "https://pub.dev"
source: hosted
version: "4.5.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@@ -1157,18 +1253,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: win32 name: win32
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.1" version: "5.5.4"
win32_registry: win32_registry:
dependency: "direct main" dependency: "direct main"
description: description:
name: win32_registry name: win32_registry
sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.3" version: "1.1.4"
window_manager: window_manager:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -1,7 +1,7 @@
name: fl_clash name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free. description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none' publish_to: 'none'
version: 0.8.56+202408261 version: 0.8.57+202409021
environment: environment:
sdk: '>=3.1.0 <4.0.0' sdk: '>=3.1.0 <4.0.0'
flutter: 3.22.3 flutter: 3.22.3
@@ -45,6 +45,8 @@ dependencies:
archive: ^3.6.1 archive: ^3.6.1
lpinyin: ^2.0.3 lpinyin: ^2.0.3
emoji_regex: ^0.0.5 emoji_regex: ^0.0.5
process_run: ^1.1.0
cached_network_image: ^3.4.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
@@ -66,6 +68,9 @@ flutter:
- family: Twemoji - family: Twemoji
fonts: fonts:
- asset: assets/fonts/Twemoji.Mozilla.ttf - asset: assets/fonts/Twemoji.Mozilla.ttf
- family: Icons
fonts:
- asset: assets/fonts/Icons.ttf
ffigen: ffigen:
name: "ClashFFI" name: "ClashFFI"
output: 'lib/clash/generated/clash_ffi.dart' output: 'lib/clash/generated/clash_ffi.dart'

View File

@@ -6,11 +6,12 @@ import 'package:fl_clash/common/other.dart';
import 'package:lpinyin/lpinyin.dart'; import 'package:lpinyin/lpinyin.dart';
void main() { void main() {
print(PinyinHelper.getPinyin("ABC")); final res = const [
print(PinyinHelper.getPinyin("阿里巴巴")); "https://doh.pub/dns-query",
"https://dns.alidns.com/dns-query"
];
print('a'.compareTo('B')); List.from(res)..remove("https://doh.pub/dns-query");
print('A'.compareTo('B'));
} }
startService() async { startService() async {

View File

@@ -93,6 +93,9 @@ set(CLASH_DIR "../libclash/windows")
install(FILES "${CLASH_DIR}/libclash.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" install(FILES "${CLASH_DIR}/libclash.dll" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime) COMPONENT Runtime)
install(FILES "EnableLoopback.exe" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES) if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"

BIN
windows/EnableLoopback.exe Normal file

Binary file not shown.