优化:积分页面积分同步处理

This commit is contained in:
ivan 2026-03-12 15:30:45 +08:00
parent 5cfb4c74af
commit 6ae2b55677
7 changed files with 71 additions and 68 deletions

View File

@ -90,7 +90,7 @@ class _MainScaffold extends StatelessWidget {
body: IndexedStack(
index: currentTab.index,
children: [
const HomeScreen(),
HomeScreen(isActive: currentTab == NavTab.home),
GalleryScreen(isActive: currentTab == NavTab.gallery),
ProfileScreen(isActive: currentTab == NavTab.profile),
],

View File

@ -0,0 +1,30 @@
import '../api/api_config.dart';
import '../api/services/user_api.dart';
import '../auth/auth_service.dart';
import 'user_state.dart';
/// UserState
///
/// [updateProfile] true avatar userName Profile
Future<void> refreshAccount({bool updateProfile = false}) 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 (!res.isSuccess || res.data == null) return;
final data = res.data as Map<String, dynamic>?;
final credits = data?['reveal'] as int?;
if (credits != null) UserState.setCredits(credits);
if (updateProfile) {
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 (_) {}
}

View File

@ -8,7 +8,6 @@ import '../../core/api/services/image_api.dart';
import '../../core/auth/auth_service.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_spacing.dart';
import '../../core/user/user_state.dart';
import '../../shared/widgets/top_nav_bar.dart';
import 'models/gallery_task_item.dart';
@ -142,9 +141,7 @@ class _GalleryScreenState extends State<GalleryScreen> {
appBar: PreferredSize(
preferredSize: const Size.fromHeight(56),
child: TopNavBar(
title: 'Gallery',
credits: UserCreditsData.of(context)?.creditsDisplay ?? '--',
onCreditsTap: () => Navigator.of(context).pushNamed('/recharge'),
title: 'My Gallery',
),
),
body: _loading
@ -176,7 +173,8 @@ class _GalleryScreenState extends State<GalleryScreen> {
onRefresh: () => _loadTasks(refresh: true),
child: _gridItems.isEmpty && !_loading
? SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
physics:
const AlwaysScrollableScrollPhysics(),
child: SizedBox(
height: constraints.maxHeight - 100,
child: Center(
@ -190,7 +188,8 @@ class _GalleryScreenState extends State<GalleryScreen> {
),
)
: GridView.builder(
physics: const AlwaysScrollableScrollPhysics(),
physics:
const AlwaysScrollableScrollPhysics(),
controller: _scrollController,
padding: EdgeInsets.fromLTRB(
AppSpacing.screenPadding,
@ -206,8 +205,8 @@ class _GalleryScreenState extends State<GalleryScreen> {
mainAxisSpacing: AppSpacing.xl,
crossAxisSpacing: AppSpacing.xl,
),
itemCount:
_gridItems.length + (_loadingMore ? 1 : 0),
itemCount: _gridItems.length +
(_loadingMore ? 1 : 0),
itemBuilder: (context, index) {
if (index >= _gridItems.length) {
return const Center(
@ -276,17 +275,17 @@ class _GalleryCard extends StatelessWidget {
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: mediaItem.imageUrl != null
? CachedNetworkImage(
imageUrl: mediaItem.imageUrl!,
fit: BoxFit.cover,
placeholder: (_, __) => Container(
color: AppColors.surfaceAlt,
),
errorWidget: (_, __, ___) => Container(
color: AppColors.surfaceAlt,
),
)
: _VideoThumbnailCover(videoUrl: mediaItem.videoUrl!),
? CachedNetworkImage(
imageUrl: mediaItem.imageUrl!,
fit: BoxFit.cover,
placeholder: (_, __) => Container(
color: AppColors.surfaceAlt,
),
errorWidget: (_, __, ___) => Container(
color: AppColors.surfaceAlt,
),
)
: _VideoThumbnailCover(videoUrl: mediaItem.videoUrl!),
),
);
},

View File

@ -13,13 +13,12 @@ import '../../core/log/app_logger.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_spacing.dart';
import '../../core/theme/app_typography.dart';
import '../../core/user/account_refresh.dart';
import '../../core/user/user_state.dart';
import '../../features/home/models/task_item.dart';
import '../../shared/widgets/top_nav_bar.dart';
import '../../core/api/api_config.dart';
import '../../core/api/services/image_api.dart';
import '../../core/api/services/user_api.dart';
/// Generate Video screen - matches Pencil mmLB5
class GenerateVideoScreen extends StatefulWidget {
@ -56,6 +55,7 @@ class _GenerateVideoScreenState extends State<GenerateVideoScreen> {
@override
void initState() {
super.initState();
refreshAccount();
WidgetsBinding.instance.addPostFrameCallback((_) {
GenerateVideoScreen._log.d('opened with task: ${widget.task}');
});
@ -192,17 +192,7 @@ class _GenerateVideoScreenState extends State<GenerateVideoScreen> {
final taskId = taskData?['tree'];
//
final accountRes = await UserApi.getAccount(
sentinel: ApiConfig.appId,
asset: userId,
);
if (accountRes.isSuccess && accountRes.data != null) {
final accountData = accountRes.data as Map<String, dynamic>?;
final credits = accountData?['reveal'] as int?;
if (credits != null) {
UserState.setCredits(credits);
}
}
await refreshAccount();
if (!mounted) return;
Navigator.of(context).pushReplacementNamed(
@ -235,10 +225,8 @@ class _GenerateVideoScreenState extends State<GenerateVideoScreen> {
preferredSize: const Size.fromHeight(56),
child: TopNavBar(
title: 'Generate Video',
credits: UserCreditsData.of(context)?.creditsDisplay ?? '--',
showBackButton: true,
onBack: () => Navigator.of(context).pop(),
onCreditsTap: () => Navigator.of(context).pushNamed('/recharge'),
),
),
body: Column(

View File

@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import '../../core/api/services/image_api.dart';
import '../../core/user/user_state.dart';
import '../../core/auth/auth_service.dart';
import '../../core/user/account_refresh.dart';
import '../../core/user/user_state.dart';
import '../../core/theme/app_spacing.dart';
import '../../shared/widgets/top_nav_bar.dart';
import 'models/category_item.dart';
@ -12,7 +13,9 @@ import 'widgets/video_card.dart';
/// AI Video App home screen - tab Grid
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
const HomeScreen({super.key, this.isActive = true});
final bool isActive;
@override
State<HomeScreen> createState() => _HomeScreenState();
@ -30,6 +33,15 @@ class _HomeScreenState extends State<HomeScreen> {
void initState() {
super.initState();
_loadCategories();
if (widget.isActive) refreshAccount();
}
@override
void didUpdateWidget(covariant HomeScreen oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isActive && !oldWidget.isActive) {
refreshAccount();
}
}
Future<void> _loadCategories() async {

View File

@ -2,9 +2,7 @@ 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/user/account_refresh.dart';
import '../recharge/payment_webview_screen.dart';
import '../../core/theme/app_colors.dart';
import '../../core/user/user_state.dart';
@ -25,41 +23,17 @@ class _ProfileScreenState extends State<ProfileScreen> {
@override
void initState() {
super.initState();
if (widget.isActive) _fetchAccount();
if (widget.isActive) refreshAccount(updateProfile: true);
}
@override
void didUpdateWidget(covariant ProfileScreen oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isActive && !oldWidget.isActive) {
_fetchAccount();
refreshAccount(updateProfile: true);
}
}
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
Widget build(BuildContext context) {
return Scaffold(

View File

@ -6,6 +6,7 @@ import '../../core/adjust/adjust_events.dart';
import '../../core/api/api_config.dart';
import '../../core/api/services/payment_api.dart';
import '../../core/auth/auth_service.dart';
import '../../core/user/account_refresh.dart';
import '../../core/log/app_logger.dart';
import '../../core/theme/app_colors.dart';
import '../../core/user/user_state.dart';
@ -39,6 +40,7 @@ class _RechargeScreenState extends State<RechargeScreen> with WidgetsBindingObse
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
refreshAccount();
_fetchActivities();
}
@ -368,10 +370,8 @@ class _RechargeScreenState extends State<RechargeScreen> with WidgetsBindingObse
preferredSize: const Size.fromHeight(56),
child: TopNavBar(
title: 'Recharge',
credits: UserCreditsData.of(context)?.creditsDisplay ?? '--',
showBackButton: true,
onBack: () => Navigator.of(context).pop(),
onCreditsTap: null,
),
),
body: SingleChildScrollView(