| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- import 'package:flutter/material.dart';
- import 'package:flutter_riverpod/flutter_riverpod.dart';
- import 'package:go_router/go_router.dart';
- import '../../../core/l10n/app_localizations.dart';
- import '../../../core/theme/app_colors.dart';
- import '../../../data/models/asset/account_auth.dart';
- import '../../../providers/security_provider.dart';
- class SecurityScreen extends ConsumerWidget {
- const SecurityScreen({super.key});
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final cs = Theme.of(context).colorScheme;
- final authAsync = ref.watch(securityAuthProvider);
- return Scaffold(
- appBar: AppBar(
- elevation: 0,
- leading: IconButton(
- icon: const Icon(Icons.chevron_left, size: 28),
- onPressed: () => context.pop(),
- ),
- title: Text(
- AppLocalizations.of(context)!.securitySettingsTitle,
- style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
- ),
- centerTitle: true,
- ),
- body: authAsync.when(
- loading: () => const Center(child: CircularProgressIndicator()),
- error: (e, _) => Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(AppLocalizations.of(context)!.loadFailed, style: TextStyle(color: cs.onSurface)),
- const SizedBox(height: 12),
- ElevatedButton(
- onPressed: () =>
- ref.read(securityAuthProvider.notifier).refresh(),
- child: Text(AppLocalizations.of(context)!.retry),
- ),
- ],
- ),
- ),
- data: (auth) => _SecurityBody(auth: auth),
- ),
- );
- }
- }
- // ── 安全设置主体 ─────────────────────────────────────────────
- class _SecurityBody extends ConsumerWidget {
- const _SecurityBody({required this.auth});
- final AccountAuth auth;
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final cs = Theme.of(context).colorScheme;
- final isDark = Theme.of(context).brightness == Brightness.dark;
- return Column(
- children: [
- // ── 安全提示 ─────────────────────────────────────
- const _WarningBanner(),
- const SizedBox(height: 16),
- // ── 安全选项列表 ──────────────────────────────────
- Container(
- margin: const EdgeInsets.symmetric(horizontal: 16),
- decoration: BoxDecoration(
- color: isDark ? AppColors.darkBgSecondary : AppColors.lightBgSecondary,
- borderRadius: BorderRadius.circular(12),
- ),
- child: Column(
- children: [
- // 谷歌认证
- _SecurityItem(
- icon: Icons.lock_outline,
- title: AppLocalizations.of(context)!.authenticator,
- subtitle: AppLocalizations.of(context)!.authenticatorDesc,
- statusLabel: auth.isGoogleVerified ? AppLocalizations.of(context)!.certified : AppLocalizations.of(context)!.notCertified,
- statusColor:
- auth.isGoogleVerified ? AppColors.rise : AppColors.fall,
- onTap: () async {
- if (auth.isGoogleVerified) {
- // 已绑定 → 提示联系客服
- _showContactDialog(context);
- } else {
- // 未绑定 → 进入绑定流程
- await context.push('/user/security/google-auth');
- // 返回后刷新状态
- ref.read(securityAuthProvider.notifier).refresh();
- }
- },
- ),
- _divider(cs),
- // 邮箱认证
- _SecurityItem(
- icon: Icons.mail_outline,
- title: AppLocalizations.of(context)!.emailAuth,
- subtitle: auth.email.isNotEmpty
- ? auth.maskedEmail
- : AppLocalizations.of(context)!.emailAuthDesc,
- statusLabel: auth.email.isNotEmpty ? AppLocalizations.of(context)!.certified : AppLocalizations.of(context)!.notCertified,
- statusColor:
- auth.email.isNotEmpty ? AppColors.rise : AppColors.fall,
- onTap: () {},
- ),
- _divider(cs),
- // 资金密码
- _SecurityItem(
- icon: Icons.vpn_key_outlined,
- title: AppLocalizations.of(context)!.fundPassword,
- subtitle: AppLocalizations.of(context)!.fundPasswordDesc,
- statusLabel: auth.isFundsVerified ? AppLocalizations.of(context)!.modifyAction : AppLocalizations.of(context)!.notSet,
- statusColor:
- auth.isFundsVerified ? AppColors.rise : AppColors.fall,
- onTap: () async {
- // fundsVerified=="1" → 修改模式,否则 → 首次设置
- await context.push(
- '/user/security/fund-password',
- extra: auth.isFundsVerified,
- );
- ref.read(securityAuthProvider.notifier).refresh();
- },
- ),
- _divider(cs),
- // 登录密码
- _SecurityItem(
- icon: Icons.lock_outline,
- title: AppLocalizations.of(context)!.loginPasswordMenu,
- subtitle: AppLocalizations.of(context)!.loginPasswordDesc,
- statusLabel: auth.isLoginVerified ? AppLocalizations.of(context)!.alreadySet : AppLocalizations.of(context)!.notSet,
- statusColor:
- auth.isLoginVerified ? AppColors.rise : AppColors.fall,
- onTap: () async {
- await context.push('/user/security/change-password');
- ref.read(securityAuthProvider.notifier).refresh();
- },
- isLast: true,
- ),
- ],
- ),
- ),
- ],
- );
- }
- void _showContactDialog(BuildContext context) {
- final cs = Theme.of(context).colorScheme;
- final isDark = Theme.of(context).brightness == Brightness.dark;
- showDialog<void>(
- context: context,
- builder: (ctx) => Dialog(
- backgroundColor: isDark ? AppColors.darkBgSecondary : AppColors.lightBgSecondary,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
- child: Padding(
- padding: const EdgeInsets.fromLTRB(24, 28, 24, 20),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Container(
- width: 48,
- height: 48,
- decoration: BoxDecoration(
- color: AppColors.brand.withAlpha(30),
- shape: BoxShape.circle,
- ),
- child: const Icon(Icons.headset_mic_outlined, color: AppColors.brand, size: 24),
- ),
- const SizedBox(height: 16),
- Text(
- AppLocalizations.of(context)!.hintTitle,
- style: TextStyle(
- color: cs.onSurface,
- fontSize: 17,
- fontWeight: FontWeight.w700,
- ),
- ),
- const SizedBox(height: 10),
- Text(
- AppLocalizations.of(context)!.contactServiceHint,
- textAlign: TextAlign.center,
- style: TextStyle(
- color: cs.onSurface.withAlpha(180),
- fontSize: 14,
- height: 1.5,
- ),
- ),
- const SizedBox(height: 24),
- SizedBox(
- width: double.infinity,
- child: ElevatedButton(
- onPressed: () => Navigator.of(ctx).pop(),
- style: ElevatedButton.styleFrom(
- backgroundColor: AppColors.brand,
- foregroundColor: Colors.black,
- elevation: 0,
- padding: const EdgeInsets.symmetric(vertical: 12),
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
- ),
- child: Text(AppLocalizations.of(context)!.gotIt, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)),
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
- Widget _divider(ColorScheme cs) => Divider(
- height: 1,
- indent: 52,
- color: cs.outline,
- );
- }
- // ── 安全提示横幅 ─────────────────────────────────────────────
- class _WarningBanner extends StatelessWidget {
- const _WarningBanner();
- @override
- Widget build(BuildContext context) {
- return Container(
- margin: const EdgeInsets.fromLTRB(16, 16, 16, 0),
- padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
- decoration: BoxDecoration(
- color: const Color(0xFF2D2500),
- border: Border.all(color: const Color(0xFF4A3A00), width: 1),
- borderRadius: BorderRadius.circular(10),
- ),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- const Icon(Icons.info_outline, color: AppColors.brand, size: 16),
- const SizedBox(width: 8),
- Expanded(
- child: Text(
- AppLocalizations.of(context)!.securityBannerTip,
- style: const TextStyle(color: AppColors.brand, fontSize: 13),
- ),
- ),
- ],
- ),
- );
- }
- }
- // ── 安全选项行 ───────────────────────────────────────────────
- class _SecurityItem extends StatelessWidget {
- const _SecurityItem({
- required this.icon,
- required this.title,
- required this.subtitle,
- required this.onTap,
- this.statusLabel,
- this.statusColor,
- this.isLast = false,
- });
- final IconData icon;
- final String title;
- final String subtitle;
- final VoidCallback onTap;
- final String? statusLabel;
- final Color? statusColor;
- final bool isLast;
- @override
- Widget build(BuildContext context) {
- final cs = Theme.of(context).colorScheme;
- return InkWell(
- onTap: onTap,
- borderRadius: isLast
- ? const BorderRadius.vertical(bottom: Radius.circular(12))
- : BorderRadius.zero,
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
- child: Row(
- children: [
- Container(
- width: 36,
- height: 36,
- decoration: BoxDecoration(
- color: cs.outline.withAlpha(30),
- borderRadius: BorderRadius.circular(8),
- ),
- child:
- Icon(icon, size: 18, color: cs.onSurface.withAlpha(153)),
- ),
- const SizedBox(width: 12),
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- title,
- style: TextStyle(
- color: cs.onSurface,
- fontSize: 14,
- fontWeight: FontWeight.w500,
- ),
- ),
- const SizedBox(height: 2),
- Text(
- subtitle,
- style: TextStyle(
- color: cs.onSurface.withAlpha(153),
- fontSize: 12,
- ),
- ),
- ],
- ),
- ),
- if (statusLabel != null) ...[
- Text(
- statusLabel!,
- style: TextStyle(
- color: statusColor,
- fontSize: 13,
- fontWeight: FontWeight.w500,
- ),
- ),
- const SizedBox(width: 4),
- ],
- Icon(Icons.chevron_right,
- size: 18, color: cs.onSurface.withAlpha(153)),
- ],
- ),
- ),
- );
- }
- }
|