rwadurian/frontend/mobile-app/lib/app.dart

120 lines
3.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:go_router/go_router.dart';
import 'core/di/injection_container.dart';
import 'core/theme/app_theme.dart';
import 'core/services/auth_event_service.dart';
import 'core/providers/maintenance_provider.dart';
import 'routes/app_router.dart';
import 'routes/route_paths.dart';
class App extends ConsumerStatefulWidget {
const App({super.key});
@override
ConsumerState<App> createState() => _AppState();
}
class _AppState extends ConsumerState<App> {
StreamSubscription<AuthEvent>? _authEventSubscription;
@override
void initState() {
super.initState();
_listenToAuthEvents();
_initializeMaintenanceProvider();
}
/// 初始化维护状态提供者
void _initializeMaintenanceProvider() {
// 设置全局导航 Key以便在 App 生命周期事件中显示维护弹窗
ref.read(maintenanceProvider.notifier).setNavigatorKey(rootNavigatorKey);
}
@override
void dispose() {
_authEventSubscription?.cancel();
super.dispose();
}
/// 监听认证事件
void _listenToAuthEvents() {
final authEventService = ref.read(authEventServiceProvider);
_authEventSubscription = authEventService.events.listen((event) {
if (event.type == AuthEventType.tokenExpired) {
_handleTokenExpired(event.message);
}
});
}
/// 处理 token 过期
Future<void> _handleTokenExpired(String? message) async {
debugPrint('[App] !!!! Token expired 事件触发 !!!!');
debugPrint('[App] Token expired message: $message');
// ★ 切换账号期间忽略 tokenExpired 事件
// 原因:切换过程中会清除旧 tokenin-flight 的 API 请求收到 401 后触发此事件,
// 如果不拦截logoutCurrentAccount() 会把正在恢复或已恢复的新账号数据全部清掉
final multiAccountService = ref.read(multiAccountServiceProvider);
if (multiAccountService.isSwitchingAccount) {
debugPrint('[App] ★★★ 正在切换账号,忽略 tokenExpired 事件 ★★★');
return;
}
debugPrint('[App] 即将跳转登录页面');
// 打印调用栈,方便排查是谁触发了 token 过期事件
debugPrint('[App] Token expired stack: ${StackTrace.current}');
// 清除当前账号状态(但保留账号列表和向导页标识)
await multiAccountService.logoutCurrentAccount();
// 使用全局 Navigator Key 跳转到登录页面
final navigatorState = rootNavigatorKey.currentState;
if (navigatorState != null) {
// 显示提示消息
if (message != null) {
ScaffoldMessenger.of(navigatorState.context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.orange,
),
);
}
// 跳转到登录页面
navigatorState.context.go(RoutePaths.phoneLogin);
}
}
@override
Widget build(BuildContext context) {
final router = ref.watch(appRouterProvider);
return ScreenUtilInit(
designSize: const Size(360, 800), // 与 UIPro Figma 设计稿一致
minTextAdapt: true,
splitScreenMode: true,
useInheritedMediaQuery: true,
builder: (context, child) {
return MaterialApp.router(
title: '榴莲皇后',
debugShowCheckedModeBanner: false,
theme: AppTheme.light,
routerConfig: router,
builder: (context, widget) {
// 限制系统字体缩放,保持 UI 一致性
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.noScaling,
),
child: widget!,
);
},
);
},
);
}
}