diff --git a/health_app/android/app/src/main/res/values-night/styles.xml b/health_app/android/app/src/main/res/values-night/styles.xml
index 06952be..51b3023 100644
--- a/health_app/android/app/src/main/res/values-night/styles.xml
+++ b/health_app/android/app/src/main/res/values-night/styles.xml
@@ -1,18 +1,11 @@
-
-
-
-
diff --git a/health_app/lib/app.dart b/health_app/lib/app.dart
index c15495a..6fb6537 100644
--- a/health_app/lib/app.dart
+++ b/health_app/lib/app.dart
@@ -1,3 +1,4 @@
+import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -5,7 +6,9 @@ import 'package:shadcn_ui/shadcn_ui.dart';
import 'core/app_router.dart';
import 'core/app_theme.dart';
import 'core/navigation_provider.dart';
+import 'pages/splash_page.dart';
import 'providers/auth_provider.dart';
+import 'providers/data_providers.dart';
/// 健康管家 App 根组件
class HealthApp extends ConsumerWidget {
@@ -25,9 +28,64 @@ class HealthApp extends ConsumerWidget {
supportedLocales: const [Locale('zh', 'CN'), Locale('zh')],
locale: const Locale('zh'),
home: const _RootNavigator(),
- // 注入 ShadTheme,让所有页面都能用 shadcn 组件
- builder: (context, child) =>
- ShadTheme(data: AppTheme.shadTheme, child: child!),
+ // 注入 ShadTheme + 启动闸门:Splash 盖在最上层,直到首页今日健康卡数据就绪
+ builder: (context, child) => ShadTheme(
+ data: AppTheme.shadTheme,
+ child: _BootGate(child: child!),
+ ),
+ );
+ }
+}
+
+/// 启动是否就绪:auth 判定完成,且(已登录的普通用户)今日健康卡数据已到位。
+/// 未登录或医生/管理员无需等待健康卡。
+final appReadyProvider = Provider((ref) {
+ final auth = ref.watch(authProvider);
+ if (auth.isLoading) return false; // 还在判登录态 → 继续盖 Splash
+ if (!auth.isLoggedIn) return true; // 未登录 → 就绪,露出登录页
+ final role = auth.user?.role ?? 'User';
+ if (role != 'User') return true; // 医生/管理员首页不依赖今日健康卡
+ // 普通用户:等今日健康卡片数据(成功或失败都算就绪,避免卡死)
+ final health = ref.watch(latestHealthProvider);
+ return health.hasValue || health.hasError;
+});
+
+/// 启动闸门——就绪前在最上层覆盖 Splash,盖住登录页闪现与首页对话流初始态。
+/// 带 8 秒安全兜底,避免离线时数据永不返回导致卡在启动页。
+class _BootGate extends ConsumerStatefulWidget {
+ final Widget child;
+ const _BootGate({required this.child});
+
+ @override
+ ConsumerState<_BootGate> createState() => _BootGateState();
+}
+
+class _BootGateState extends ConsumerState<_BootGate> {
+ bool _timedOut = false;
+ Timer? _timer;
+
+ @override
+ void initState() {
+ super.initState();
+ _timer = Timer(const Duration(seconds: 8), () {
+ if (mounted) setState(() => _timedOut = true);
+ });
+ }
+
+ @override
+ void dispose() {
+ _timer?.cancel();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final ready = ref.watch(appReadyProvider) || _timedOut;
+ return Stack(
+ children: [
+ widget.child,
+ if (!ready) const Positioned.fill(child: SplashPage()),
+ ],
);
}
}
diff --git a/health_app/lib/pages/splash_page.dart b/health_app/lib/pages/splash_page.dart
new file mode 100644
index 0000000..4ebee25
--- /dev/null
+++ b/health_app/lib/pages/splash_page.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/material.dart';
+
+/// 启动页——盖在整个 App 最上层,直到「今日健康卡片」数据就绪后才撤掉,
+/// 用于遮住启动期间的登录页闪现与首页对话流初始态。始终亮色白底。
+class SplashPage extends StatelessWidget {
+ const SplashPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return const ColoredBox(
+ color: Colors.white,
+ child: Center(
+ child: Image(
+ image: AssetImage('assets/branding/health_splash_master.png'),
+ fit: BoxFit.contain,
+ ),
+ ),
+ );
+ }
+}
diff --git a/health_app/lib/providers/auth_provider.dart b/health_app/lib/providers/auth_provider.dart
index ca2ae17..4c1163e 100644
--- a/health_app/lib/providers/auth_provider.dart
+++ b/health_app/lib/providers/auth_provider.dart
@@ -35,13 +35,18 @@ class AuthNotifier extends Notifier {
@override
AuthState build() {
_checkAuth();
- return const AuthState(isLoggedIn: false, isLoading: false);
+ // 初始为"加载中",让启动闸门显示 Splash 盖住登录页/首页初始态
+ return const AuthState(isLoggedIn: false, isLoading: true);
}
Future _checkAuth() async {
final db = ref.read(localDbProvider);
final refresh = await db.read('refresh_token');
- if (refresh == null) return; // 无token,保持 isLoggedIn: false
+ if (refresh == null) {
+ // 无 token:判定完成、未登录
+ state = const AuthState(isLoggedIn: false, isLoading: false);
+ return;
+ }
state = const AuthState(isLoading: true);
try {