From f3a163c95810a03499b4ba7f1f8b0a4c347113ab Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 9 Mar 2026 22:35:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E4=B8=AD=E5=BF=83=E6=95=B0=E6=8D=AE=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/app.dart | 2 +- lib/core/auth/auth_service.dart | 8 ++ lib/core/user/user_state.dart | 10 ++ lib/features/profile/profile_screen.dart | 115 +++++++++++++++++++---- 4 files changed, 118 insertions(+), 17 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index f38bbf7..52faccc 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -83,7 +83,7 @@ class _MainScaffold extends StatelessWidget { children: [ const HomeScreen(), GalleryScreen(isActive: currentTab == NavTab.gallery), - const ProfileScreen(), + ProfileScreen(isActive: currentTab == NavTab.profile), ], ), bottomNavigationBar: BottomNavBar( diff --git a/lib/core/auth/auth_service.dart b/lib/core/auth/auth_service.dart index 956fa7e..e397f42 100644 --- a/lib/core/auth/auth_service.dart +++ b/lib/core/auth/auth_service.dart @@ -118,6 +118,14 @@ class AuthService { UserState.setUserId(uid); _log('init: 已设置 userId'); } + final avatarUrl = data?['realm'] as String?; + if (avatarUrl != null && avatarUrl.isNotEmpty) { + UserState.setAvatar(avatarUrl); + } + final name = data?['terminal'] as String?; + if (name != null && name.isNotEmpty) { + UserState.setUserName(name); + } } else { _log('init: 登录失败'); } diff --git a/lib/core/user/user_state.dart b/lib/core/user/user_state.dart index a33b0ce..768fc5d 100644 --- a/lib/core/user/user_state.dart +++ b/lib/core/user/user_state.dart @@ -6,6 +6,8 @@ class UserState { static final ValueNotifier credits = ValueNotifier(null); static final ValueNotifier userId = ValueNotifier(null); + static final ValueNotifier avatar = ValueNotifier(null); + static final ValueNotifier userName = ValueNotifier(null); static void setCredits(int? value) { credits.value = value; @@ -15,6 +17,14 @@ class UserState { userId.value = value; } + static void setAvatar(String? value) { + avatar.value = value; + } + + static void setUserName(String? value) { + userName.value = value; + } + static String formatCredits(int? value) { if (value == null) return '--'; return value.toString().replaceAllMapped( diff --git a/lib/features/profile/profile_screen.dart b/lib/features/profile/profile_screen.dart index 0e275b7..932a488 100644 --- a/lib/features/profile/profile_screen.dart +++ b/lib/features/profile/profile_screen.dart @@ -1,14 +1,63 @@ import 'package:flutter/material.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import '../../core/api/api_config.dart'; +import '../../core/api/services/user_api.dart'; +import '../../core/auth/auth_service.dart'; import '../../core/theme/app_colors.dart'; import '../../core/user/user_state.dart'; import '../../core/theme/app_spacing.dart'; import '../../core/theme/app_typography.dart'; /// Profile screen - matches Pencil KXeow -class ProfileScreen extends StatelessWidget { - const ProfileScreen({super.key}); +class ProfileScreen extends StatefulWidget { + const ProfileScreen({super.key, required this.isActive}); + + final bool isActive; + + @override + State createState() => _ProfileScreenState(); +} + +class _ProfileScreenState extends State { + @override + void initState() { + super.initState(); + if (widget.isActive) _fetchAccount(); + } + + @override + void didUpdateWidget(covariant ProfileScreen oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.isActive && !oldWidget.isActive) { + _fetchAccount(); + } + } + + Future _fetchAccount() async { + final uid = UserState.userId.value; + if (uid == null || uid.isEmpty) return; + try { + await AuthService.loginComplete; + final res = await UserApi.getAccount( + sentinel: ApiConfig.appId, + asset: uid, + ); + if (!mounted) return; + if (res.isSuccess && res.data != null) { + final data = res.data as Map?; + final credits = data?['reveal'] as int?; + if (credits != null) UserState.setCredits(credits); + final avatarUrl = data?['realm'] as String?; + UserState.setAvatar( + avatarUrl != null && avatarUrl.isNotEmpty ? avatarUrl : null); + final name = data?['terminal'] as String?; + UserState.setUserName( + name != null && name.isNotEmpty ? name : null); + } + } catch (_) {} + } @override Widget build(BuildContext context) { @@ -23,9 +72,25 @@ class ProfileScreen extends StatelessWidget { ), child: Column( children: [ - _ProfileHeader( - userName: 'Alex Johnson', - uid: 'UID 84920133', + ValueListenableBuilder( + valueListenable: UserState.avatar, + builder: (_, avatarUrl, __) { + return ValueListenableBuilder( + valueListenable: UserState.userName, + builder: (_, userName, __) { + return ValueListenableBuilder( + valueListenable: UserState.userId, + builder: (_, userId, __) { + return _ProfileHeader( + avatarUrl: avatarUrl, + userName: userName, + uid: userId, + ); + }, + ); + }, + ); + }, ), const SizedBox(height: AppSpacing.xl), _BalanceCard( @@ -56,12 +121,14 @@ class ProfileScreen extends StatelessWidget { class _ProfileHeader extends StatelessWidget { const _ProfileHeader({ - required this.userName, - required this.uid, + this.avatarUrl, + this.userName, + this.uid, }); - final String userName; - final String uid; + final String? avatarUrl; + final String? userName; + final String? uid; @override Widget build(BuildContext context) { @@ -82,15 +149,31 @@ class _ProfileHeader extends StatelessWidget { width: 2, ), ), - child: const Icon( - LucideIcons.user, - size: 40, - color: AppColors.textSecondary, - ), + clipBehavior: Clip.antiAlias, + child: avatarUrl != null && avatarUrl!.isNotEmpty + ? CachedNetworkImage( + imageUrl: avatarUrl!, + fit: BoxFit.cover, + placeholder: (_, __) => Icon( + LucideIcons.user, + size: 40, + color: AppColors.textSecondary, + ), + errorWidget: (_, __, ___) => Icon( + LucideIcons.user, + size: 40, + color: AppColors.textSecondary, + ), + ) + : Icon( + LucideIcons.user, + size: 40, + color: AppColors.textSecondary, + ), ), const SizedBox(height: AppSpacing.lg), Text( - userName, + userName ?? 'Guest', style: AppTypography.bodyLarge.copyWith( color: AppColors.textPrimary, ), @@ -107,7 +190,7 @@ class _ProfileHeader extends StatelessWidget { border: Border.all(color: AppColors.border), ), child: Text( - uid, + uid != null && uid!.isNotEmpty ? 'UID $uid' : 'UID --', style: AppTypography.caption.copyWith( color: AppColors.textSecondary, ),