Files
FlClash/lib/views/proxies/setting.dart
T
chen08209 491fae10f5 feat(views)!: set DNS, NTP, sniffer and TUN key by key
BREAKING CHANGE: DNS and NTP override toggles are removed; picked keys apply to every profile
Changelog: Per-key DNS, NTP, sniffer and TUN settings
2026-10-12 10:34:14 +08:00

342 lines
11 KiB
Dart

import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/icons/icons.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:material_ui/material_ui.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ProxiesSetting extends StatelessWidget {
const ProxiesSetting({super.key});
Glyph _getIconWithProxiesType(ProxiesType type) {
return switch (type) {
ProxiesType.tab => AppGlyphs.layoutTabs,
ProxiesType.list => AppGlyphs.layoutList,
};
}
Glyph _getIconWithProxiesSortType(ProxiesSortType type) {
return switch (type) {
ProxiesSortType.none => AppGlyphs.sort,
ProxiesSortType.delay => AppGlyphs.bolt,
ProxiesSortType.name => AppGlyphs.sortAlpha,
};
}
String _getStringProxiesSortType(BuildContext context, ProxiesSortType type) {
final appLocalizations = context.appLocalizations;
return switch (type) {
ProxiesSortType.none => appLocalizations.defaultText,
ProxiesSortType.delay => appLocalizations.delay,
ProxiesSortType.name => appLocalizations.name,
};
}
String getTextForProxiesLayout(
BuildContext context,
ProxiesLayout proxiesLayout,
) {
final appLocalizations = context.appLocalizations;
return switch (proxiesLayout) {
ProxiesLayout.tight => appLocalizations.tight,
ProxiesLayout.standard => appLocalizations.standard,
ProxiesLayout.loose => appLocalizations.loose,
};
}
String _getTextWithProxiesIconStyle(
BuildContext context,
ProxiesIconStyle style,
) {
final appLocalizations = context.appLocalizations;
return switch (style) {
ProxiesIconStyle.filled => appLocalizations.iconStyleFilled,
ProxiesIconStyle.plain => appLocalizations.iconStylePlain,
ProxiesIconStyle.hidden => appLocalizations.iconStyleHidden,
};
}
Glyph _getIconWithProxiesLayout(ProxiesLayout proxiesLayout) {
return switch (proxiesLayout) {
ProxiesLayout.tight => AppGlyphs.columnsThree,
ProxiesLayout.standard => AppGlyphs.columnsTwo,
ProxiesLayout.loose => AppGlyphs.columnsOne,
};
}
Glyph _getIconWithProxyCardType(ProxyCardType type) {
return switch (type) {
ProxyCardType.expand => AppGlyphs.cardLarge,
ProxyCardType.shrink => AppGlyphs.cardMedium,
ProxyCardType.min => AppGlyphs.cardSmall,
};
}
Glyph _getIconWithProxiesIconStyle(ProxiesIconStyle style) {
return switch (style) {
ProxiesIconStyle.filled => AppGlyphs.iconTile,
ProxiesIconStyle.plain => AppGlyphs.iconPlain,
ProxiesIconStyle.hidden => AppGlyphs.eyeOff,
};
}
List<Widget> _buildStyleSetting(BuildContext context) {
final appLocalizations = context.appLocalizations;
return generateSection(
isFirst: true,
title: appLocalizations.style,
items: [
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, _) {
final proxiesType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.type),
);
return Wrap(
spacing: 16,
children: [
for (final item in ProxiesType.values)
SettingInfoCard(
Info(
label: item.label,
glyph: _getIconWithProxiesType(item),
),
isSelected: proxiesType == item,
onPressed: () {
ref.read(proxiesStyleSettingProvider.notifier).update((
state,
) {
return state.copyWith(type: item);
});
},
),
],
);
},
),
),
],
);
}
List<Widget> _buildSortSetting(BuildContext context) {
final appLocalizations = context.appLocalizations;
return generateSection(
title: appLocalizations.sort,
items: [
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, _) {
final sortType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.sortType),
);
return Wrap(
spacing: 16,
children: [
for (final item in ProxiesSortType.values)
SettingInfoCard(
Info(
label: _getStringProxiesSortType(context, item),
glyph: _getIconWithProxiesSortType(item),
),
isSelected: sortType == item,
onPressed: () {
ref.read(proxiesStyleSettingProvider.notifier).update((
state,
) {
return state.copyWith(sortType: item);
});
},
),
],
);
},
),
),
],
);
}
List<Widget> _buildSizeSetting(BuildContext context) {
final appLocalizations = context.appLocalizations;
return generateSection(
title: appLocalizations.size,
items: [
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, _) {
final cardType = ref.watch(
proxiesStyleSettingProvider.select((state) => state.cardType),
);
return Wrap(
spacing: 16,
children: [
for (final item in ProxyCardType.values)
SettingInfoCard(
Info(
label: item.label,
glyph: _getIconWithProxyCardType(item),
),
isSelected: item == cardType,
onPressed: () {
ref.read(proxiesStyleSettingProvider.notifier).update((
state,
) {
return state.copyWith(cardType: item);
});
},
),
],
);
},
),
),
],
);
}
List<Widget> _buildLayoutSetting(BuildContext context) {
final appLocalizations = context.appLocalizations;
return generateSection(
title: appLocalizations.layout,
items: [
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, _) {
final layout = ref.watch(
proxiesStyleSettingProvider.select((state) => state.layout),
);
return Wrap(
spacing: 16,
children: [
for (final item in ProxiesLayout.values)
SettingInfoCard(
Info(
label: getTextForProxiesLayout(context, item),
glyph: _getIconWithProxiesLayout(item),
),
isSelected: item == layout,
onPressed: () {
ref.watch(proxiesStyleSettingProvider.notifier).update((
state,
) {
return state.copyWith(layout: item);
});
},
),
],
);
},
),
),
],
);
}
List<Widget> _buildGroupStyleSetting(BuildContext context) {
final appLocalizations = context.appLocalizations;
return generateSection(
title: appLocalizations.iconStyle,
items: [
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16),
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, _) {
final iconStyle = ref.watch(
proxiesStyleSettingProvider.select((state) => state.iconStyle),
);
return Wrap(
spacing: 16,
children: [
for (final item in ProxiesIconStyle.values)
SettingInfoCard(
Info(
label: _getTextWithProxiesIconStyle(context, item),
glyph: _getIconWithProxiesIconStyle(item),
),
isSelected: iconStyle == item,
onPressed: () {
ref.read(proxiesStyleSettingProvider.notifier).update((
state,
) {
return state.copyWith(iconStyle: item);
});
},
),
],
);
},
),
),
],
);
}
List<Widget> _buildFilterSetting(BuildContext context) {
final appLocalizations = context.appLocalizations;
return generateSection(
title: appLocalizations.filter,
items: [
ConfigToggleItem(
title: (l) => l.hideTimeoutProxies,
subtitle: (l) => l.hideTimeoutProxiesDesc,
selector: proxiesStyleSettingProvider.select(
(state) => state.hideTimeoutProxies,
),
onChanged: (ref, value) {
ref
.read(proxiesStyleSettingProvider.notifier)
.update((state) => state.copyWith(hideTimeoutProxies: value));
},
),
],
);
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: EdgeInsets.only(top: context.contentTopPadding, bottom: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
..._buildStyleSetting(context),
..._buildSortSetting(context),
..._buildLayoutSetting(context),
..._buildSizeSetting(context),
Consumer(
builder: (_, ref, child) {
final isList = ref.watch(
proxiesStyleSettingProvider.select(
(state) => state.type == ProxiesType.list,
),
);
if (isList) {
return child!;
}
return Container();
},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [..._buildGroupStyleSetting(context)],
),
),
..._buildFilterSetting(context),
],
),
);
}
}