import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:social_app/core/constants/app_constants.dart'; import 'package:social_app/core/di/injection.dart'; import 'package:social_app/core/router/app_routes.dart'; import 'package:social_app/core/theme/design_tokens.dart'; import 'package:social_app/shared/widgets/app_button.dart'; import 'package:social_app/shared/widgets/app_loading_indicator.dart'; import 'package:social_app/shared/widgets/app_pressable.dart'; import 'package:social_app/shared/widgets/destructive_action_sheet.dart'; import 'package:social_app/shared/widgets/toast/toast.dart'; import 'package:social_app/shared/widgets/toast/toast_type.dart'; import 'package:social_app/shared/utils/phone_display_formatter.dart'; import 'package:social_app/features/auth/presentation/bloc/auth_bloc.dart'; import 'package:social_app/features/auth/presentation/bloc/auth_event.dart'; import 'package:social_app/features/auth/presentation/bloc/auth_state.dart'; import 'package:social_app/features/friends/data/friends_api.dart'; import 'package:social_app/features/settings/data/settings_api.dart'; import 'package:social_app/features/settings/data/services/settings_user_cache.dart'; import 'package:social_app/features/users/data/models/user_response.dart'; import 'package:social_app/features/users/data/users_api.dart'; import 'package:social_app/features/home/ui/navigation/home_return_policy.dart'; import '../widgets/settings_page_scaffold.dart'; const settingsProfileEditButtonKey = ValueKey('settings_profile_edit_button'); const settingsLogoutButtonKey = ValueKey('settings_logout_button'); class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @override State createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { final UsersApi _usersApi = sl(); final FriendsApi _friendsApi = sl(); final SettingsUserCache _userCache = sl(); UserResponse? _user; bool _isLoading = true; int _friendsCount = 0; String? _firstFriendName; @override void initState() { super.initState(); final cachedUser = _userCache.cachedUser; if (cachedUser != null) { _user = cachedUser; _isLoading = false; } _loadData(); } Future _loadData() async { try { final user = await _userCache.getOrLoad(_usersApi.getMe); if (mounted) { setState(() { _user = user; _isLoading = false; }); } } catch (e) { if (mounted && _user == null) { setState(() { _isLoading = false; }); } } try { final friends = await _friendsApi.getFriends(); if (mounted) { setState(() { _friendsCount = friends.length; _firstFriendName = friends.isNotEmpty ? friends.first.friend.username : null; }); } } catch (e) { // Keep profile available even when contacts fail. } } @override Widget build(BuildContext context) { return SettingsPageScaffold( title: '设置', onBack: () => returnToHomePreserveState(context, forceGoHome: true), body: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildProfileHero(), const SizedBox(height: AppSpacing.lg), _buildQuickActions(context), const SizedBox(height: AppSpacing.lg), _buildSubscriptionCard(), const SizedBox(height: AppSpacing.lg), _buildMenuCard(context), const SizedBox(height: AppSpacing.xl), _buildLogoutAction(), ], ), ); } Widget _buildSectionLabel(String label) { return Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xs), child: Text( label, style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.slate500, ), ), ); } Widget _buildProfileHero() { if (_isLoading) { return Container( width: double.infinity, height: 120, padding: const EdgeInsets.all(AppSpacing.xl), decoration: BoxDecoration( color: AppColors.white, borderRadius: BorderRadius.circular(AppRadius.xxl), ), child: const Center(child: AppLoadingIndicator(size: 22)), ); } final username = _user?.username ?? '未设置'; final phone = _user?.phone == null ? '未设置' : formatPhoneForDisplay(_user?.phone); return Container( width: double.infinity, padding: const EdgeInsets.all(AppSpacing.xl), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppColors.white, AppColors.surfaceInfoLight], ), borderRadius: BorderRadius.circular(AppRadius.xxl), border: Border.all(color: AppColors.borderTertiary), boxShadow: [ BoxShadow( color: AppColors.blue100.withValues(alpha: 0.35), blurRadius: 14, offset: const Offset(0, 4), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( width: 64, height: 64, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppColors.blue100, AppColors.blue50], ), borderRadius: BorderRadius.circular(32), boxShadow: [ BoxShadow( color: Color.fromRGBO( AppColors.blue400.r.toInt(), AppColors.blue400.g.toInt(), AppColors.blue400.b.toInt(), 0.2, ), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: const Icon( Icons.person, size: 28, color: AppColors.blue600, ), ), const SizedBox(width: AppSpacing.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( username, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: AppColors.slate900, ), overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), Text( phone, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: AppColors.slate500, ), ), ], ), ), const SizedBox(width: AppSpacing.md), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ AppPressable( key: settingsProfileEditButtonKey, onTap: _onTapEditProfile, borderRadius: BorderRadius.circular(AppRadius.lg), child: SizedBox( width: AppSpacing.xl * 2, height: AppSpacing.xl * 2, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ const Icon( Icons.edit, size: 14, color: AppColors.slate500, ), const SizedBox(height: 3), Container( width: 12, height: 1.5, decoration: BoxDecoration( color: AppColors.slate400, borderRadius: BorderRadius.circular( AppRadius.full, ), ), ), ], ), ), ), const SizedBox(height: AppSpacing.sm), _buildFreeBadge(), ], ), ], ), ], ), ); } Widget _buildFreeBadge() { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( gradient: LinearGradient( colors: [AppColors.blue50, AppColors.surfaceInfoLight], ), borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.borderQuaternary), ), child: const Text( 'Free', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: AppColors.blue600, ), ), ); } String _buildFriendsSubtitle() { if (_friendsCount == 0) { return '暂无联系人'; } if (_friendsCount == 1) { return '已添加 1 位:$_firstFriendName'; } return '已添加 $_friendsCount 位联系人'; } Widget _buildQuickActions(BuildContext context) { return Row( children: [ Expanded( child: _buildActionCard( icon: Icons.people, iconColor: AppColors.blue500, title: '联系人', subtitle: _buildFriendsSubtitle(), onTap: () => context.push(AppRoutes.contactsList), ), ), const SizedBox(width: AppSpacing.md), Expanded( child: _buildActionCard( icon: Icons.auto_awesome, iconColor: AppColors.violet500, title: '周期计划', subtitle: '已启用:会议提醒', onTap: () => context.push(AppRoutes.settingsFeatures), ), ), ], ); } Widget _buildActionCard({ required IconData icon, required Color iconColor, required String title, required String subtitle, required VoidCallback onTap, }) { return AppPressable( onTap: onTap, borderRadius: BorderRadius.circular(AppRadius.xl), child: Container( constraints: const BoxConstraints(minHeight: 136), padding: const EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( color: AppColors.white, borderRadius: BorderRadius.circular(AppRadius.xl), border: Border.all(color: AppColors.borderSecondary), boxShadow: [ BoxShadow( color: AppColors.slate200.withValues(alpha: 0.45), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: AppColors.surfaceTertiary, borderRadius: BorderRadius.circular(10), ), child: Icon(icon, size: 18, color: iconColor), ), const Spacer(), Icon(Icons.chevron_right, size: 16, color: AppColors.slate300), ], ), const SizedBox(height: AppSpacing.md), Text( title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.slate900, ), ), const SizedBox(height: 2), Text( subtitle, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: AppColors.slate500, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), ); } Widget _buildSubscriptionCard() { return Container( padding: const EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppColors.white, AppColors.surfaceInfoLight], ), borderRadius: BorderRadius.circular(AppRadius.xl), border: Border.all(color: AppColors.borderTertiary), boxShadow: [ BoxShadow( color: AppColors.slate200.withValues(alpha: 0.4), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppColors.blue100, AppColors.blue50], ), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: AppColors.blue200.withValues(alpha: 0.45), blurRadius: 6, offset: const Offset(0, 1), ), ], ), child: const Icon( Icons.workspace_premium, size: 22, color: AppColors.blue600, ), ), const SizedBox(width: AppSpacing.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '升级到 Pro', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.slate900, ), ), const SizedBox(height: 2), Text( '解锁更多高级功能', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: AppColors.slate500, ), ), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( gradient: const LinearGradient( colors: [AppColors.blue500, AppColors.blue600], ), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: const Color(0x4D60A5FA), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: const Text( '升级', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.white, ), ), ), ], ), ); } Widget _buildMenuCard(BuildContext context) { return Container( decoration: BoxDecoration( color: AppColors.white, borderRadius: BorderRadius.circular(AppRadius.xl), border: Border.all(color: AppColors.borderSecondary), ), child: Column( children: [ _buildMenuItem( icon: Icons.notifications, title: '提醒设置', onTap: () {}, ), _buildDivider(), _buildMenuItem( icon: Icons.bookmark, title: '我的记忆', onTap: () => context.push(AppRoutes.settingsMemory), ), _buildDivider(), _buildMenuItem( icon: Icons.system_update, title: '检查更新', trailing: 'v${AppConstants.version}', onTap: _checkForUpdates, ), ], ), ); } Widget _buildMenuItem({ required IconData icon, required String title, String? trailing, required VoidCallback onTap, }) { return AppPressable( onTap: onTap, borderRadius: BorderRadius.circular(AppRadius.md), child: Container( height: 56, padding: const EdgeInsets.symmetric(horizontal: 14), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(icon, size: 20, color: AppColors.slate500), const SizedBox(width: 10), Text( title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w500, color: AppColors.slate900, ), ), ], ), Row( children: [ if (trailing != null) ...[ Text( trailing, style: const TextStyle( fontSize: 14, color: AppColors.slate400, ), ), const SizedBox(width: 6), ], const Icon( Icons.chevron_right, size: 18, color: AppColors.slate400, ), ], ), ], ), ), ); } Widget _buildDivider() { return Container( height: 1, margin: const EdgeInsets.symmetric(horizontal: 14), color: AppColors.slate100, ); } Future _onTapEditProfile() async { final changed = await context.push(AppRoutes.settingsEditProfile); if (changed == true && mounted) { final cached = _userCache.cachedUser; if (cached != null) { setState(() { _user = cached; }); } } } Future _onTapLogout() async { final confirmed = await showDestructiveActionSheet( context, title: '退出登录', message: '确定退出当前账户吗?', confirmText: '确认退出', ); if (!confirmed || !mounted) { return; } _userCache.invalidate(); final authBloc = context.read(); authBloc.add(AuthLoggedOut()); try { await authBloc.stream .firstWhere((state) => state is AuthUnauthenticated) .timeout(const Duration(seconds: 5)); } catch (_) { if (!mounted) return; Toast.show(context, '退出失败,请稍后重试', type: ToastType.error); return; } if (!mounted) return; context.go(AppRoutes.authLogin); } Future _checkForUpdates() async { try { final settingsApi = sl(); final result = await settingsApi.checkUpdates( currentVersionCode: AppConstants.build, currentVersionName: AppConstants.version, platform: 'android', ); if (!mounted) return; if (!result.hasUpdate) { Toast.show(context, '当前已是最新版本', type: ToastType.success); return; } final message = result.updateType == 'required' ? '有新版本可用 (${result.latestVersionName}),请立即更新' : '发现新版本 (${result.latestVersionName}),是否更新?'; final shouldUpdate = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('检查更新'), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('取消'), ), if (result.downloadUrl != null) TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('更新'), ), ], ), ); if (shouldUpdate == true && result.downloadUrl != null && mounted) { Toast.show( context, '下载链接: ${result.downloadUrl}', type: ToastType.info, ); } } catch (e) { if (!mounted) return; Toast.show(context, '检查更新失败', type: ToastType.error); } } Widget _buildLogoutAction() { return SizedBox( width: double.infinity, height: 52, child: AppButton( key: settingsLogoutButtonKey, text: '退出登录', isOutlined: true, onPressed: () => _onTapLogout(), ), ); } }