新增:更新个人中心数据显示

This commit is contained in:
ivan 2026-03-09 22:35:33 +08:00
parent dedc03fc3c
commit f3a163c958
4 changed files with 118 additions and 17 deletions

View File

@ -83,7 +83,7 @@ class _MainScaffold extends StatelessWidget {
children: [ children: [
const HomeScreen(), const HomeScreen(),
GalleryScreen(isActive: currentTab == NavTab.gallery), GalleryScreen(isActive: currentTab == NavTab.gallery),
const ProfileScreen(), ProfileScreen(isActive: currentTab == NavTab.profile),
], ],
), ),
bottomNavigationBar: BottomNavBar( bottomNavigationBar: BottomNavBar(

View File

@ -118,6 +118,14 @@ class AuthService {
UserState.setUserId(uid); UserState.setUserId(uid);
_log('init: 已设置 userId'); _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 { } else {
_log('init: 登录失败'); _log('init: 登录失败');
} }

View File

@ -6,6 +6,8 @@ class UserState {
static final ValueNotifier<int?> credits = ValueNotifier<int?>(null); static final ValueNotifier<int?> credits = ValueNotifier<int?>(null);
static final ValueNotifier<String?> userId = ValueNotifier<String?>(null); static final ValueNotifier<String?> userId = ValueNotifier<String?>(null);
static final ValueNotifier<String?> avatar = ValueNotifier<String?>(null);
static final ValueNotifier<String?> userName = ValueNotifier<String?>(null);
static void setCredits(int? value) { static void setCredits(int? value) {
credits.value = value; credits.value = value;
@ -15,6 +17,14 @@ class UserState {
userId.value = value; userId.value = value;
} }
static void setAvatar(String? value) {
avatar.value = value;
}
static void setUserName(String? value) {
userName.value = value;
}
static String formatCredits(int? value) { static String formatCredits(int? value) {
if (value == null) return '--'; if (value == null) return '--';
return value.toString().replaceAllMapped( return value.toString().replaceAllMapped(

View File

@ -1,14 +1,63 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.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/theme/app_colors.dart';
import '../../core/user/user_state.dart'; import '../../core/user/user_state.dart';
import '../../core/theme/app_spacing.dart'; import '../../core/theme/app_spacing.dart';
import '../../core/theme/app_typography.dart'; import '../../core/theme/app_typography.dart';
/// Profile screen - matches Pencil KXeow /// Profile screen - matches Pencil KXeow
class ProfileScreen extends StatelessWidget { class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key}); const ProfileScreen({super.key, required this.isActive});
final bool isActive;
@override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
@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<void> _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<String, dynamic>?;
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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -23,9 +72,25 @@ class ProfileScreen extends StatelessWidget {
), ),
child: Column( child: Column(
children: [ children: [
_ProfileHeader( ValueListenableBuilder<String?>(
userName: 'Alex Johnson', valueListenable: UserState.avatar,
uid: 'UID 84920133', builder: (_, avatarUrl, __) {
return ValueListenableBuilder<String?>(
valueListenable: UserState.userName,
builder: (_, userName, __) {
return ValueListenableBuilder<String?>(
valueListenable: UserState.userId,
builder: (_, userId, __) {
return _ProfileHeader(
avatarUrl: avatarUrl,
userName: userName,
uid: userId,
);
},
);
},
);
},
), ),
const SizedBox(height: AppSpacing.xl), const SizedBox(height: AppSpacing.xl),
_BalanceCard( _BalanceCard(
@ -56,12 +121,14 @@ class ProfileScreen extends StatelessWidget {
class _ProfileHeader extends StatelessWidget { class _ProfileHeader extends StatelessWidget {
const _ProfileHeader({ const _ProfileHeader({
required this.userName, this.avatarUrl,
required this.uid, this.userName,
this.uid,
}); });
final String userName; final String? avatarUrl;
final String uid; final String? userName;
final String? uid;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -82,7 +149,23 @@ class _ProfileHeader extends StatelessWidget {
width: 2, width: 2,
), ),
), ),
child: const Icon( 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, LucideIcons.user,
size: 40, size: 40,
color: AppColors.textSecondary, color: AppColors.textSecondary,
@ -90,7 +173,7 @@ class _ProfileHeader extends StatelessWidget {
), ),
const SizedBox(height: AppSpacing.lg), const SizedBox(height: AppSpacing.lg),
Text( Text(
userName, userName ?? 'Guest',
style: AppTypography.bodyLarge.copyWith( style: AppTypography.bodyLarge.copyWith(
color: AppColors.textPrimary, color: AppColors.textPrimary,
), ),
@ -107,7 +190,7 @@ class _ProfileHeader extends StatelessWidget {
border: Border.all(color: AppColors.border), border: Border.all(color: AppColors.border),
), ),
child: Text( child: Text(
uid, uid != null && uid!.isNotEmpty ? 'UID $uid' : 'UID --',
style: AppTypography.caption.copyWith( style: AppTypography.caption.copyWith(
color: AppColors.textSecondary, color: AppColors.textSecondary,
), ),