Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion arb/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@
"show": "Show",
"exit": "Exit",
"systemProxy": "System Proxy",
"systemProxyPort": "System Proxy Port",
"systemProxyPortDesc": "Port used for the system HTTP/HTTPS proxy (1024–49151)",
"project": "Project",
"core": "Core",
"tabAnimation": "Tab Animation",
Expand Down Expand Up @@ -652,4 +654,3 @@
"ageKeyPairGeneratedSuccess": "X25519 Key pair generated, please keep it safe",
"agePrivateKeyRequired": "Please enter a correct Age private key first"
}

1 change: 1 addition & 0 deletions lib/models/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ abstract class VpnProps with _$VpnProps {
abstract class NetworkProps with _$NetworkProps {
const factory NetworkProps({
@Default(false) bool systemProxy,
@Default(7890) int systemProxyPort,
@Default(defaultBypassDomain) List<String> bypassDomain,
@Default(true) bool bypassPrivateRoute,
@Default(true) bool autoSetSystemDns,
Expand Down
114 changes: 105 additions & 9 deletions lib/views/config/network.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,110 @@ class SystemProxyItem extends ConsumerWidget {
}
}

class SystemProxyPortItem extends ConsumerWidget {
const SystemProxyPortItem({super.key});

Future<void> _showPortDialog(
BuildContext context,
WidgetRef ref,
int currentPort,
) async {
String inputValue = '$currentPort';
String? errorText;
final controller = TextEditingController(text: inputValue);

final result = await globalState.showCommonDialog<bool>(
child: StatefulBuilder(
builder: (context, setState) {
return CommonDialog(
title: appLocalizations.systemProxyPort,
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(appLocalizations.cancel),
),
TextButton(
onPressed: errorText == null && inputValue.isNotEmpty
? () => Navigator.of(context).pop(true)
: null,
child: Text(appLocalizations.confirm),
),
],
child: TextField(
autofocus: true,
keyboardType: TextInputType.number,
controller: controller,
decoration: InputDecoration(
labelText: appLocalizations.systemProxyPort,
errorText: errorText,
hintText: '1024-49151',
helperText: appLocalizations.systemProxyPortDesc,
),
onChanged: (value) {
inputValue = value;
if (value.isEmpty) {
setState(() {
errorText = appLocalizations.emptyTip(
appLocalizations.systemProxyPort,
);
});
} else {
final intValue = int.tryParse(value);
if (intValue == null) {
setState(() {
errorText = appLocalizations.numberTip(
appLocalizations.systemProxyPort,
);
});
} else if (intValue < 1024 || intValue > 49151) {
setState(() {
errorText = appLocalizations.portTip(
appLocalizations.systemProxyPort,
);
});
} else {
setState(() => errorText = null);
}
}
},
onSubmitted: (value) {
if (errorText == null && value.isNotEmpty) {
Navigator.of(context).pop(true);
}
},
),
);
},
),
);

controller.dispose();

if (result == true && inputValue.isNotEmpty) {
final intValue = int.tryParse(inputValue);
if (intValue != null && intValue >= 1024 && intValue <= 49151) {
ref
.read(networkSettingProvider.notifier)
.updateState((state) => state.copyWith(systemProxyPort: intValue));
}
}
}

@override
Widget build(BuildContext context, ref) {
final port = ref.watch(
networkSettingProvider.select((state) => state.systemProxyPort),
);

return ListItem(
title: Text(appLocalizations.systemProxyPort),
subtitle: Text('$port'),
trailing: const Icon(Icons.chevron_right),
onTap: () => _showPortDialog(context, ref, port),
);
}
}

class AutoSetSystemDnsItem extends ConsumerWidget {
const AutoSetSystemDnsItem({super.key});

Expand Down Expand Up @@ -206,8 +310,6 @@ class IcmpForwardingItem extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
// Note: inverted because disableIcmpForwarding=true means disabled
// UI shows "Enable ICMP forwarding"
final icmpForwarding = ref.watch(
patchClashConfigProvider.select(
(state) => !state.tun.disableIcmpForwarding,
Expand All @@ -220,7 +322,6 @@ class IcmpForwardingItem extends ConsumerWidget {
delegate: SwitchDelegate(
value: icmpForwarding,
onChanged: (value) async {
// Invert before passing to core
ref
.read(patchClashConfigProvider.notifier)
.updateState(
Expand Down Expand Up @@ -379,7 +480,6 @@ class MtuItem extends ConsumerWidget {
),
onChanged: (value) {
inputValue = value;
// Real-time validation
if (value.isEmpty) {
setState(() {
errorText = appLocalizations.emptyTip('MTU');
Expand Down Expand Up @@ -412,7 +512,6 @@ class MtuItem extends ConsumerWidget {
),
);

// Clean up controller
controller.dispose();

if (result == true && inputValue.isNotEmpty) {
Expand All @@ -432,7 +531,6 @@ class MtuItem extends ConsumerWidget {
patchClashConfigProvider.select((state) => state.tun.mtu),
);

// Preset options
final presetOptions = [1480, 4064, 9000];
final isCustom = !presetOptions.contains(mtu);

Expand All @@ -451,11 +549,9 @@ class MtuItem extends ConsumerWidget {
onChanged: (value) async {
if (value == null) return;

// If custom option selected
if (value == 'custom') {
await _showCustomMtuDialog(context, ref, mtu);
} else {
// Apply preset value directly
final intValue = int.parse(value);
ref
.read(patchClashConfigProvider.notifier)
Expand Down Expand Up @@ -565,7 +661,7 @@ final networkItems = [
if (system.isDesktop)
...generateSection(
title: appLocalizations.system,
items: [SystemProxyItem(), BypassDomainItem()],
items: [SystemProxyItem(), SystemProxyPortItem(), BypassDomainItem()],
),
...generateSection(
title: appLocalizations.options,
Expand Down
2 changes: 0 additions & 2 deletions macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import flutter_qjs
import hotkey_manager_macos
import mobile_scanner
import package_info_plus
import path_provider_foundation
import restart_app
import screen_retriever_macos
import shared_preferences_foundation
Expand All @@ -37,7 +36,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerMacosPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
RestartAppPlugin.register(with: registry.registrar(forPlugin: "RestartAppPlugin"))
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
Expand Down
Loading