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

Update flutter and pub dependencies
2025-08-27 16:28:34 +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);
}
}