gcx/frontend/genex-mobile/lib/features/profile/presentation/pages/settings_page.dart

332 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import '../../../../app/theme/app_colors.dart';
import '../../../../app/theme/app_typography.dart';
import '../../../../app/theme/app_spacing.dart';
import '../../../../app/i18n/app_localizations.dart';
import '../../../../app/i18n/locale_manager.dart';
/// 设置页面
///
/// 账号安全、通知、支付管理、语言、货币、关于
/// 货币选择影响交易页面中的计价货币符号
class SettingsPage extends StatefulWidget {
const SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
_CurrencyOption _selectedCurrency = _currencyOptions[0];
bool _notifyTrade = true;
bool _notifyExpiry = true;
bool _notifyMarket = false;
bool _notifyMarketing = false;
String get _currentLanguageDisplay {
final locale = LocaleManager.userLocale.value ??
Localizations.localeOf(context);
return LocaleManager.localeDisplayName(locale);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(context.t('settings.title'))),
body: ListView(
children: [
_buildSection(context.t('settings.accountSecurity'), [
_buildTile(context.t('settings.phone'),
subtitle: '138****8888', icon: Icons.phone_rounded),
_buildTile(context.t('settings.email'),
subtitle: 'u***@email.com', icon: Icons.email_rounded),
_buildTile(context.t('settings.changePassword'),
icon: Icons.lock_rounded),
_buildTile(context.t('settings.identity'),
subtitle: 'L1',
icon: Icons.verified_user_rounded, onTap: () {
Navigator.pushNamed(context, '/kyc');
}),
]),
_buildSection(context.t('settings.paymentManage'), [
_buildTile(context.t('settings.paymentMethod'),
subtitle: 'Visa •••• 4242',
icon: Icons.credit_card_rounded),
_buildTile(context.t('settings.bankAccount'),
subtitle: 'BoA •••• 6789',
icon: Icons.account_balance_rounded),
_buildTile(context.t('settings.paymentPassword'),
icon: Icons.password_rounded),
]),
_buildSection(context.t('settings.notifications'), [
_buildSwitchTile(context.t('settings.tradeNotify'), _notifyTrade,
(v) => setState(() => _notifyTrade = v)),
_buildSwitchTile(context.t('settings.expiryRemind'), _notifyExpiry,
(v) => setState(() => _notifyExpiry = v)),
_buildSwitchTile(context.t('settings.marketChange'), _notifyMarket,
(v) => setState(() => _notifyMarket = v)),
_buildSwitchTile(
context.t('settings.marketingPush'), _notifyMarketing,
(v) => setState(() => _notifyMarketing = v)),
]),
_buildSection(context.t('settings.general'), [
_buildTile(context.t('settings.language'),
subtitle: _currentLanguageDisplay,
icon: Icons.language_rounded,
onTap: () => _showLanguagePicker(context)),
_buildTile(context.t('settings.currency'),
subtitle:
'${_selectedCurrency.code} (${_selectedCurrency.symbol})',
icon: Icons.attach_money_rounded,
onTap: () => _showCurrencyPicker(context)),
_buildTile(context.t('settings.clearCache'),
icon: Icons.cleaning_services_rounded),
]),
_buildSection(context.t('settings.about'), [
_buildTile(context.t('settings.version'),
subtitle: 'v1.0.0', icon: Icons.info_outline_rounded),
_buildTile(context.t('settings.userAgreement'),
icon: Icons.description_rounded),
_buildTile(context.t('settings.privacyPolicy'),
icon: Icons.privacy_tip_rounded),
_buildTile(context.t('settings.helpCenter'),
icon: Icons.help_outline_rounded),
]),
Padding(
padding: const EdgeInsets.all(20),
child: OutlinedButton(
onPressed: () {
Navigator.of(context)
.pushNamedAndRemoveUntil('/', (_) => false);
},
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.error,
side: const BorderSide(color: AppColors.error),
minimumSize: const Size(double.infinity, 48),
),
child: Text(context.t('settings.logout')),
),
),
],
),
);
}
void _showCurrencyPicker(BuildContext context) {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (_) => Container(
decoration: const BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: const EdgeInsets.only(top: 12),
width: 36,
height: 4,
decoration: BoxDecoration(
color: AppColors.gray200,
borderRadius: AppSpacing.borderRadiusFull,
),
),
Padding(
padding: const EdgeInsets.all(16),
child: Text(context.t('settings.selectCurrency'),
style: AppTypography.h3),
),
const Divider(height: 1),
..._currencyOptions.map((option) {
final isSelected = _selectedCurrency.code == option.code;
return ListTile(
leading:
Text(option.flag, style: const TextStyle(fontSize: 24)),
title: Text(
'${option.name} (${option.code})',
style: AppTypography.bodyMedium.copyWith(
fontWeight:
isSelected ? FontWeight.w600 : FontWeight.w400,
color: isSelected
? AppColors.primary
: AppColors.textPrimary,
),
),
subtitle: Text(
'${context.t('settings.currencySymbol')}: ${option.symbol}',
style: AppTypography.caption,
),
trailing: isSelected
? const Icon(Icons.check_circle_rounded,
color: AppColors.primary, size: 22)
: null,
onTap: () {
setState(() => _selectedCurrency = option);
Navigator.pop(context);
},
);
}),
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
child: Text(
context.t('settings.currencyNote'),
style: AppTypography.caption.copyWith(
color: AppColors.textTertiary,
),
textAlign: TextAlign.center,
),
),
],
),
),
);
}
void _showLanguagePicker(BuildContext context) {
final languages = [
('简体中文', const Locale('zh', 'CN'), '🇨🇳'),
('繁體中文', const Locale('zh', 'TW'), '🇹🇼'),
('English', const Locale('en'), '🇺🇸'),
('日本語', const Locale('ja'), '🇯🇵'),
];
final currentLocale = LocaleManager.userLocale.value ??
Localizations.localeOf(context);
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (_) => Container(
decoration: const BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: const EdgeInsets.only(top: 12),
width: 36,
height: 4,
decoration: BoxDecoration(
color: AppColors.gray200,
borderRadius: AppSpacing.borderRadiusFull,
),
),
Padding(
padding: const EdgeInsets.all(16),
child: Text(context.t('settings.selectLanguage'),
style: AppTypography.h3),
),
const Divider(height: 1),
...languages.map((lang) {
final (name, locale, flag) = lang;
final isSelected =
currentLocale.languageCode == locale.languageCode &&
(locale.countryCode == null ||
currentLocale.countryCode == locale.countryCode);
return ListTile(
leading:
Text(flag, style: const TextStyle(fontSize: 24)),
title: Text(
name,
style: AppTypography.bodyMedium.copyWith(
fontWeight:
isSelected ? FontWeight.w600 : FontWeight.w400,
color: isSelected
? AppColors.primary
: AppColors.textPrimary,
),
),
trailing: isSelected
? const Icon(Icons.check_circle_rounded,
color: AppColors.primary, size: 22)
: null,
onTap: () {
LocaleManager.userLocale.value = locale;
setState(() {});
Navigator.pop(context);
},
);
}),
const SizedBox(height: 24),
],
),
),
);
}
Widget _buildSection(String title, List<Widget> children) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 8),
child: Text(title, style: AppTypography.labelSmall),
),
Container(
color: AppColors.surface,
child: Column(children: children),
),
],
);
}
Widget _buildTile(String title,
{String? subtitle, IconData? icon, VoidCallback? onTap}) {
return ListTile(
leading: icon != null
? Icon(icon, size: 22, color: AppColors.textSecondary)
: null,
title: Text(title, style: AppTypography.bodyMedium),
subtitle: subtitle != null
? Text(subtitle, style: AppTypography.caption)
: null,
trailing: const Icon(Icons.chevron_right_rounded,
size: 20, color: AppColors.textTertiary),
onTap: onTap ?? () {},
);
}
Widget _buildSwitchTile(
String title, bool value, ValueChanged<bool> onChanged) {
return SwitchListTile(
title: Text(title, style: AppTypography.bodyMedium),
value: value,
onChanged: onChanged,
activeColor: AppColors.primary,
);
}
}
class _CurrencyOption {
final String code;
final String symbol;
final String name;
final String flag;
const _CurrencyOption({
required this.code,
required this.symbol,
required this.name,
required this.flag,
});
}
const _currencyOptions = [
_CurrencyOption(code: 'USD', symbol: '\$', name: 'USD', flag: '🇺🇸'),
_CurrencyOption(code: 'CNY', symbol: '¥', name: 'CNY', flag: '🇨🇳'),
_CurrencyOption(code: 'EUR', symbol: '', name: 'EUR', flag: '🇪🇺'),
_CurrencyOption(code: 'GBP', symbol: '£', name: 'GBP', flag: '🇬🇧'),
_CurrencyOption(code: 'JPY', symbol: '¥', name: 'JPY', flag: '🇯🇵'),
_CurrencyOption(code: 'HKD', symbol: 'HK\$', name: 'HKD', flag: '🇭🇰'),
];