Files
MWClash/lib/fragments/config/dns.dart
chen08209 e92900dbbd Fix tab delay view issues
Fix tray action issues

Fix get profile redirect client ua issues

Fix proxy card delay view issues

Add Russian, Japanese adaptation

Fix some issues
2025-03-07 23:49:27 +08:00

712 lines
20 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';
class OverrideItem extends ConsumerWidget {
const OverrideItem({super.key});
@override
Widget build(BuildContext context, ref) {
final override = ref.watch(overrideDnsProvider);
return ListItem.switchItem(
title: Text(appLocalizations.overrideDns),
subtitle: Text(appLocalizations.overrideDnsDesc),
delegate: SwitchDelegate(
value: override,
onChanged: (bool value) async {
ref.read(overrideDnsProvider.notifier).value = value;
},
),
);
}
}
class StatusItem extends ConsumerWidget {
const StatusItem({super.key});
@override
Widget build(BuildContext context, ref) {
final enable =
ref.watch(patchClashConfigProvider.select((state) => state.dns.enable));
return ListItem.switchItem(
title: Text(appLocalizations.status),
subtitle: Text(appLocalizations.statusDesc),
delegate: SwitchDelegate(
value: enable,
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(enable: value));
},
),
);
}
}
class ListenItem extends ConsumerWidget {
const ListenItem({super.key});
@override
Widget build(BuildContext context, ref) {
final listen =
ref.watch(patchClashConfigProvider.select((state) => state.dns.listen));
return ListItem.input(
title: Text(appLocalizations.listen),
subtitle: Text(listen),
delegate: InputDelegate(
title: appLocalizations.listen,
value: listen,
onChanged: (String? value) {
if (value == null) {
return;
}
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(listen: value));
},
),
);
}
}
class PreferH3Item extends ConsumerWidget {
const PreferH3Item({super.key});
@override
Widget build(BuildContext context, ref) {
final preferH3 = ref
.watch(patchClashConfigProvider.select((state) => state.dns.preferH3));
return ListItem.switchItem(
title: const Text("PreferH3"),
subtitle: Text(appLocalizations.preferH3Desc),
delegate: SwitchDelegate(
value: preferH3,
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(preferH3: value));
},
),
);
}
}
class IPv6Item extends ConsumerWidget {
const IPv6Item({super.key});
@override
Widget build(BuildContext context, ref) {
final ipv6 = ref.watch(
patchClashConfigProvider.select((state) => state.dns.ipv6),
);
return ListItem.switchItem(
title: const Text("IPv6"),
delegate: SwitchDelegate(
value: ipv6,
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(ipv6: value));
},
),
);
}
}
class RespectRulesItem extends ConsumerWidget {
const RespectRulesItem({super.key});
@override
Widget build(BuildContext context, ref) {
final respectRules = ref.watch(
patchClashConfigProvider.select((state) => state.dns.respectRules),
);
return ListItem.switchItem(
title: Text(appLocalizations.respectRules),
subtitle: Text(appLocalizations.respectRulesDesc),
delegate: SwitchDelegate(
value: respectRules,
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(respectRules: value));
},
),
);
}
}
class DnsModeItem extends ConsumerWidget {
const DnsModeItem({super.key});
@override
Widget build(BuildContext context, ref) {
final enhancedMode = ref.watch(
patchClashConfigProvider.select((state) => state.dns.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;
}
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(enhancedMode: value));
},
textBuilder: (dnsMode) => dnsMode.name,
value: enhancedMode,
),
);
}
}
class FakeIpRangeItem extends ConsumerWidget {
const FakeIpRangeItem({super.key});
@override
Widget build(BuildContext context, ref) {
final fakeIpRange = ref.watch(
patchClashConfigProvider.select((state) => state.dns.fakeIpRange),
);
return ListItem.input(
title: Text(appLocalizations.fakeipRange),
subtitle: Text(fakeIpRange),
delegate: InputDelegate(
title: appLocalizations.fakeipRange,
value: fakeIpRange,
onChanged: (String? value) {
if (value == null) {
return;
}
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(fakeIpRange: value));
},
),
);
}
}
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: Consumer(
builder: (_, ref, __) {
final fakeIpFilter = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fakeIpFilter),
);
return ListPage(
title: appLocalizations.fakeipFilter,
items: fakeIpFilter,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(
fakeIpFilter: List.from(items),
));
},
);
},
),
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: Consumer(builder: (_, ref, __) {
final defaultNameserver = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.defaultNameserver),
);
return ListPage(
title: appLocalizations.defaultNameserver,
items: defaultNameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
defaultNameserver: List.from(items),
),
);
},
);
}),
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: Consumer(builder: (_, ref, __) {
final nameserver = ref.watch(
patchClashConfigProvider.select((state) => state.dns.nameserver),
);
return ListPage(
title: "域名服务器",
items: nameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
nameserver: List.from(items),
),
);
},
);
}),
extendPageWidth: 360,
),
);
}
}
class UseHostsItem extends ConsumerWidget {
const UseHostsItem({super.key});
@override
Widget build(BuildContext context, ref) {
final useHosts = ref.watch(
patchClashConfigProvider.select((state) => state.dns.useHosts),
);
return ListItem.switchItem(
title: Text(appLocalizations.useHosts),
delegate: SwitchDelegate(
value: useHosts,
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(useHosts: value));
},
),
);
}
}
class UseSystemHostsItem extends ConsumerWidget {
const UseSystemHostsItem({super.key});
@override
Widget build(BuildContext context, ref) {
final useSystemHosts = ref.watch(
patchClashConfigProvider.select((state) => state.dns.useSystemHosts),
);
return ListItem.switchItem(
title: Text(appLocalizations.useSystemHosts),
delegate: SwitchDelegate(
value: useSystemHosts,
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns(
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: Consumer(builder: (_, ref, __) {
final nameserverPolicy = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.nameserverPolicy),
);
return ListPage(
title: appLocalizations.nameserverPolicy,
items: nameserverPolicy.entries,
titleBuilder: (item) => Text(item.key),
subtitleBuilder: (item) => Text(item.value),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
nameserverPolicy: Map.fromEntries(items),
),
);
},
);
}),
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: Consumer(
builder: (_, ref, __) {
final proxyServerNameserver = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.proxyServerNameserver),
);
return ListPage(
title: appLocalizations.proxyNameserver,
items: proxyServerNameserver,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
proxyServerNameserver: List.from(items),
),
);
},
);
},
),
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: Consumer(builder: (_, ref, __) {
final fallback = ref.watch(
patchClashConfigProvider.select((state) => state.dns.fallback),
);
return ListPage(
title: appLocalizations.fallback,
items: fallback,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns(
fallback: List.from(items),
),
);
},
);
}),
extendPageWidth: 360,
),
);
}
}
class GeoipItem extends ConsumerWidget {
const GeoipItem({super.key});
@override
Widget build(BuildContext context, ref) {
final geoip = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.geoip),
);
return ListItem.switchItem(
title: const Text("Geoip"),
delegate: SwitchDelegate(
value: geoip,
onChanged: (bool value) async {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns.fallbackFilter(
geoip: value,
));
},
),
);
}
}
class GeoipCodeItem extends ConsumerWidget {
const GeoipCodeItem({super.key});
@override
Widget build(BuildContext context, ref) {
final geoipCode = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.geoipCode),
);
return ListItem.input(
title: Text(appLocalizations.geoipCode),
subtitle: Text(geoipCode),
delegate: InputDelegate(
title: appLocalizations.geoipCode,
value: geoipCode,
onChanged: (String? value) {
if (value == null) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns.fallbackFilter(
geoipCode: value,
),
);
},
),
);
}
}
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: Consumer(builder: (_, ref, __) {
final geosite = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.geosite),
);
return ListPage(
title: "Geosite",
items: geosite,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns.fallbackFilter(
geosite: List.from(items),
),
);
},
);
}),
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: Consumer(builder: (_, ref, ___) {
final ipcidr = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.ipcidr),
);
return ListPage(
title: appLocalizations.ipcidr,
items: ipcidr,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref
.read(patchClashConfigProvider.notifier)
.updateState((state) => state.copyWith.dns.fallbackFilter(
ipcidr: List.from(items),
));
},
);
}),
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: Consumer(builder: (_, ref, __) {
final domain = ref.watch(
patchClashConfigProvider
.select((state) => state.dns.fallbackFilter.domain),
);
return ListPage(
title: appLocalizations.domain,
items: domain,
titleBuilder: (item) => Text(item),
onChange: (items) {
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith.dns.fallbackFilter(
domain: List.from(items),
),
);
},
);
}),
extendPageWidth: 360,
),
);
}
}
class DnsOptions extends StatelessWidget {
const DnsOptions({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: generateSection(
title: appLocalizations.options,
items: [
const StatusItem(),
const ListenItem(),
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 Column(
children: generateSection(
title: appLocalizations.fallbackFilter,
items: [
const GeoipItem(),
const GeoipCodeItem(),
const GeositeItem(),
const IpcidrItem(),
const DomainItem(),
],
),
);
}
}
const dnsItems = <Widget>[
OverrideItem(),
DnsOptions(),
FallbackFilterOptions(),
];
class DnsListView extends ConsumerWidget {
const DnsListView({super.key});
_initActions(BuildContext context, WidgetRef ref) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
context.commonScaffoldState?.actions = [
IconButton(
onPressed: () async {
final res = await globalState.showMessage(
title: appLocalizations.reset,
message: TextSpan(
text: appLocalizations.resetTip,
),
);
if (res != true) {
return;
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
dns: defaultDns,
),
);
},
tooltip: appLocalizations.reset,
icon: const Icon(
Icons.replay,
),
)
];
});
}
@override
Widget build(BuildContext context, ref) {
_initActions(context, ref);
return generateListView(
dnsItems,
);
}
}