feat(client): settings 完整版(开关组) + 联系/设置整页 golden
- settings_page 补全对照 dapp.jsx DSettings: 功能开关组(开机自启/智能分流/ Kill Switch, 本地演示态) + 配置组补检查更新行; 改 ConsumerStatefulWidget 持开关态 - l10n 新增 autostart/smartRoute/killSwitch(+Sub) + checkUpdate(中英) - desktop 整页 golden 补 联系页 + 设置页(凑齐可截 5 页: 节点/统计/账户/联系/设置) 测试: flutter test 81 通过(+2 golden), analyze 0 error Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -86,6 +86,13 @@ abstract class AppText {
|
||||
String get followLight;
|
||||
String get protocol;
|
||||
String get settingsTitle;
|
||||
String get autostart;
|
||||
String get autostartSub;
|
||||
String get smartRoute;
|
||||
String get smartRouteSub;
|
||||
String get killSwitch;
|
||||
String get killSwitchSub;
|
||||
String get checkUpdate;
|
||||
|
||||
// ── 套餐选择 ──
|
||||
String get choosePlan;
|
||||
|
||||
@@ -125,6 +125,20 @@ class StringsEn extends AppText {
|
||||
String get protocol => 'Protocol';
|
||||
@override
|
||||
String get settingsTitle => 'Settings';
|
||||
@override
|
||||
String get autostart => 'Launch at startup';
|
||||
@override
|
||||
String get autostartSub => 'Run automatically on system start';
|
||||
@override
|
||||
String get smartRoute => 'Smart routing';
|
||||
@override
|
||||
String get smartRouteSub => 'Accelerate abroad, direct at home';
|
||||
@override
|
||||
String get killSwitch => 'Kill Switch';
|
||||
@override
|
||||
String get killSwitchSub => 'Block traffic if the link drops';
|
||||
@override
|
||||
String get checkUpdate => 'Check for updates';
|
||||
|
||||
@override
|
||||
String get choosePlan => 'Choose plan';
|
||||
|
||||
@@ -124,6 +124,20 @@ class StringsZh extends AppText {
|
||||
String get protocol => '协议';
|
||||
@override
|
||||
String get settingsTitle => '设置';
|
||||
@override
|
||||
String get autostart => '开机自启';
|
||||
@override
|
||||
String get autostartSub => '系统启动时自动运行';
|
||||
@override
|
||||
String get smartRoute => '智能分流';
|
||||
@override
|
||||
String get smartRouteSub => '海外应用走加速,国内直连';
|
||||
@override
|
||||
String get killSwitch => 'Kill Switch';
|
||||
@override
|
||||
String get killSwitchSub => '断线时阻断网络,防止泄露';
|
||||
@override
|
||||
String get checkUpdate => '检查更新';
|
||||
|
||||
@override
|
||||
String get choosePlan => '选择套餐';
|
||||
|
||||
@@ -10,17 +10,30 @@ import '../pangolin_theme.dart';
|
||||
import '../state/app_providers.dart';
|
||||
import '../widgets/pangolin_icons.dart';
|
||||
|
||||
class SettingsPage extends ConsumerWidget {
|
||||
class SettingsPage extends ConsumerStatefulWidget {
|
||||
const SettingsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<SettingsPage> createState() => _SettingsPageState();
|
||||
}
|
||||
|
||||
class _SettingsPageState extends ConsumerState<SettingsPage> {
|
||||
// 本地演示态(暂无后端绑定),默认开,对照 dapp.jsx DSettings。
|
||||
bool _autostart = true;
|
||||
bool _smartRoute = true;
|
||||
bool _killSwitch = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = context.pangolin;
|
||||
final t = ref.watch(appTextProvider);
|
||||
final lang = ref.watch(localeProvider);
|
||||
final mode = ref.watch(themeModeProvider);
|
||||
final isDark = mode == ThemeMode.dark;
|
||||
|
||||
Widget sw(bool v, ValueChanged<bool> on) =>
|
||||
Switch(value: v, activeThumbColor: c.accent, onChanged: on);
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(32, 16, 32, 24),
|
||||
child: ConstrainedBox(
|
||||
@@ -32,19 +45,23 @@ class SettingsPage extends ConsumerWidget {
|
||||
right: Icon(PangolinIcons.chevronRight, size: 18, color: c.fg3), onTap: () {}),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
// 功能开关组
|
||||
_Card(children: [
|
||||
_Row(title: t.autostart, sub: t.autostartSub, right: sw(_autostart, (v) => setState(() => _autostart = v))),
|
||||
_Row(title: t.smartRoute, sub: t.smartRouteSub, right: sw(_smartRoute, (v) => setState(() => _smartRoute = v))),
|
||||
_Row(title: t.killSwitch, sub: t.killSwitchSub, last: true, right: sw(_killSwitch, (v) => setState(() => _killSwitch = v))),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
// 配置组
|
||||
_Card(children: [
|
||||
_Row(title: t.language, right: _LangSwitch(lang: lang, onPick: (l) => ref.read(localeProvider.notifier).state = l)),
|
||||
_Row(
|
||||
title: t.darkAppearance,
|
||||
sub: isDark ? t.stateOn : t.followLight,
|
||||
right: Switch(
|
||||
value: isDark,
|
||||
activeThumbColor: c.accent,
|
||||
onChanged: (v) => ref.read(themeModeProvider.notifier).state = v ? ThemeMode.dark : ThemeMode.light,
|
||||
),
|
||||
right: sw(isDark, (v) => ref.read(themeModeProvider.notifier).state = v ? ThemeMode.dark : ThemeMode.light),
|
||||
),
|
||||
_Row(title: t.protocol, right: Text('WireGuard', style: PangolinText.mono.copyWith(fontSize: 13, color: c.fg3))),
|
||||
_Row(title: t.checkUpdate, right: Icon(PangolinIcons.chevronRight, size: 18, color: c.fg3), onTap: () {}),
|
||||
_Row(title: 'Version', last: true, right: Text('v1.0.0', style: PangolinText.mono.copyWith(fontSize: 13, color: c.fg3))),
|
||||
]),
|
||||
]),
|
||||
|
||||
@@ -59,4 +59,6 @@ void main() {
|
||||
testWidgets('desktop 节点页', (t) => _shoot(t, NavView.servers, 'desktop_servers'));
|
||||
testWidgets('desktop 统计页', (t) => _shoot(t, NavView.stats, 'desktop_stats'));
|
||||
testWidgets('desktop 账户页', (t) => _shoot(t, NavView.account, 'desktop_account'));
|
||||
testWidgets('desktop 联系页', (t) => _shoot(t, NavView.contact, 'desktop_contact'));
|
||||
testWidgets('desktop 设置页', (t) => _shoot(t, NavView.settings, 'desktop_settings'));
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Reference in New Issue
Block a user