Files
MWClash/lib/views/config/network.dart
chen08209 ed7868282a Add android separates the core process
Support core status check and force restart

Optimize proxies page and access page

Update flutter and pub dependencies

Update go version

Optimize more details
2025-09-23 15:23:58 +08:00

399 lines
11 KiB
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/providers/config.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
class VPNItem extends ConsumerWidget {
const VPNItem({super.key});
@override
Widget build(BuildContext context, ref) {
final enable = ref.watch(
vpnSettingProvider.select((state) => state.enable),
);
return ListItem.switchItem(
title: const Text('VPN'),
subtitle: Text(appLocalizations.vpnEnableDesc),
delegate: SwitchDelegate(
value: enable,
onChanged: (value) async {
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(enable: value));
},
),
);
}
}
class TUNItem extends ConsumerWidget {
const TUNItem({super.key});
@override
Widget build(BuildContext context, ref) {
final enable = ref.watch(
patchClashConfigProvider.select((state) => state.tun.enable),
);
return ListItem.switchItem(
title: Text(appLocalizations.tun),
subtitle: Text(appLocalizations.tunDesc),
delegate: SwitchDelegate(
value: enable,
onChanged: (value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.tun(enable: value));
},
),
);
}
}
class AllowBypassItem extends ConsumerWidget {
const AllowBypassItem({super.key});
@override
Widget build(BuildContext context, ref) {
final allowBypass = ref.watch(
vpnSettingProvider.select((state) => state.allowBypass),
);
return ListItem.switchItem(
title: Text(appLocalizations.allowBypass),
subtitle: Text(appLocalizations.allowBypassDesc),
delegate: SwitchDelegate(
value: allowBypass,
onChanged: (bool value) async {
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(allowBypass: value));
},
),
);
}
}
class VpnSystemProxyItem extends ConsumerWidget {
const VpnSystemProxyItem({super.key});
@override
Widget build(BuildContext context, ref) {
final systemProxy = ref.watch(
vpnSettingProvider.select((state) => state.systemProxy),
);
return ListItem.switchItem(
title: Text(appLocalizations.systemProxy),
subtitle: Text(appLocalizations.systemProxyDesc),
delegate: SwitchDelegate(
value: systemProxy,
onChanged: (bool value) async {
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(systemProxy: value));
},
),
);
}
}
class SystemProxyItem extends ConsumerWidget {
const SystemProxyItem({super.key});
@override
Widget build(BuildContext context, ref) {
final systemProxy = ref.watch(
networkSettingProvider.select((state) => state.systemProxy),
);
return ListItem.switchItem(
title: Text(appLocalizations.systemProxy),
subtitle: Text(appLocalizations.systemProxyDesc),
delegate: SwitchDelegate(
value: systemProxy,
onChanged: (bool value) async {
ref
.read(networkSettingProvider.notifier)
.updateState((state) => state.copyWith(systemProxy: value));
},
),
);
}
}
class Ipv6Item extends ConsumerWidget {
const Ipv6Item({super.key});
@override
Widget build(BuildContext context, ref) {
final ipv6 = ref.watch(vpnSettingProvider.select((state) => state.ipv6));
return ListItem.switchItem(
title: const Text('IPv6'),
subtitle: Text(appLocalizations.ipv6InboundDesc),
delegate: SwitchDelegate(
value: ipv6,
onChanged: (bool value) async {
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(ipv6: value));
},
),
);
}
}
class AutoSetSystemDnsItem extends ConsumerWidget {
const AutoSetSystemDnsItem({super.key});
@override
Widget build(BuildContext context, ref) {
final autoSetSystemDns = ref.watch(
networkSettingProvider.select((state) => state.autoSetSystemDns),
);
return ListItem.switchItem(
title: Text(appLocalizations.autoSetSystemDns),
delegate: SwitchDelegate(
value: autoSetSystemDns,
onChanged: (bool value) async {
ref
.read(networkSettingProvider.notifier)
.updateState((state) => state.copyWith(autoSetSystemDns: value));
},
),
);
}
}
class TunStackItem extends ConsumerWidget {
const TunStackItem({super.key});
@override
Widget build(BuildContext context, ref) {
final stack = ref.watch(
patchClashConfigProvider.select((state) => state.tun.stack),
);
return ListItem.options(
title: Text(appLocalizations.stackMode),
subtitle: Text(stack.name),
delegate: OptionsDelegate<TunStack>(
value: stack,
options: TunStack.values,
textBuilder: (value) => value.name,
onChanged: (value) {
if (value == null) {
return;
}
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.tun(stack: value));
},
title: appLocalizations.stackMode,
),
);
}
}
class BypassDomainItem extends StatelessWidget {
const BypassDomainItem({super.key});
@override
Widget build(BuildContext context) {
return ListItem.open(
title: Text(appLocalizations.bypassDomain),
subtitle: Text(appLocalizations.bypassDomainDesc),
delegate: OpenDelegate(
blur: false,
actions: [
Consumer(
builder: (_, ref, _) {
return IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(text: appLocalizations.resetTip),
);
if (res != true) {
return;
}
ref
.read(networkSettingProvider.notifier)
.updateState(
(state) =>
state.copyWith(bypassDomain: defaultBypassDomain),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(Icons.replay),
);
},
),
],
title: appLocalizations.bypassDomain,
widget: Consumer(
builder: (_, ref, _) {
final bypassDomain = ref.watch(
networkSettingProvider.select((state) => state.bypassDomain),
);
return ListInputPage(
title: appLocalizations.bypassDomain,
items: bypassDomain,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(networkSettingProvider.notifier)
.updateState(
(state) => state.copyWith(bypassDomain: List.from(items)),
);
},
);
},
),
),
);
}
}
class DNSHijackingItem extends ConsumerWidget {
const DNSHijackingItem({super.key});
@override
Widget build(BuildContext context, ref) {
final dnsHijacking = ref.watch(
vpnSettingProvider.select((state) => state.dnsHijacking),
);
return ListItem<RouteMode>.switchItem(
title: Text(appLocalizations.dnsHijacking),
delegate: SwitchDelegate(
value: dnsHijacking,
onChanged: (value) async {
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(dnsHijacking: value));
},
),
);
}
}
class RouteModeItem extends ConsumerWidget {
const RouteModeItem({super.key});
@override
Widget build(BuildContext context, ref) {
final routeMode = ref.watch(
networkSettingProvider.select((state) => state.routeMode),
);
return ListItem<RouteMode>.options(
title: Text(appLocalizations.routeMode),
subtitle: Text(Intl.message('routeMode_${routeMode.name}')),
delegate: OptionsDelegate<RouteMode>(
title: appLocalizations.routeMode,
options: RouteMode.values,
onChanged: (RouteMode? value) {
if (value == null) {
return;
}
ref
.read(networkSettingProvider.notifier)
.updateState((state) => state.copyWith(routeMode: value));
},
textBuilder: (routeMode) => Intl.message('routeMode_${routeMode.name}'),
value: routeMode,
),
);
}
}
class RouteAddressItem extends ConsumerWidget {
const RouteAddressItem({super.key});
@override
Widget build(BuildContext context, ref) {
final bypassPrivate = ref.watch(
networkSettingProvider.select(
(state) => state.routeMode == RouteMode.bypassPrivate,
),
);
if (bypassPrivate) {
return Container();
}
return ListItem.open(
title: Text(appLocalizations.routeAddress),
subtitle: Text(appLocalizations.routeAddressDesc),
delegate: OpenDelegate(
blur: false,
maxWidth: 360,
title: appLocalizations.routeAddress,
widget: Consumer(
builder: (_, ref, _) {
final routeAddress = ref.watch(
patchClashConfigProvider.select(
(state) => state.tun.routeAddress,
),
);
return ListInputPage(
title: appLocalizations.routeAddress,
items: routeAddress,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState(
(state) =>
state.copyWith.tun(routeAddress: List.from(items)),
);
},
);
},
),
),
);
}
}
final networkItems = [
if (system.isAndroid) const VPNItem(),
if (system.isAndroid)
...generateSection(
title: 'VPN',
items: [
const VpnSystemProxyItem(),
const BypassDomainItem(),
const AllowBypassItem(),
const Ipv6Item(),
const DNSHijackingItem(),
],
),
if (system.isDesktop)
...generateSection(
title: appLocalizations.system,
items: [SystemProxyItem(), BypassDomainItem()],
),
...generateSection(
title: appLocalizations.options,
items: [
if (system.isDesktop) const TUNItem(),
if (system.isMacOS) const AutoSetSystemDnsItem(),
const TunStackItem(),
if (!system.isDesktop) ...[
const RouteModeItem(),
const RouteAddressItem(),
],
],
),
];
class NetworkListView extends StatelessWidget {
const NetworkListView({super.key});
@override
Widget build(BuildContext context) {
return generateListView(networkItems);
}
}