Files
nj/app/lib/app.dart
iven 8331db63ba feat(app): 设置页 UI + Mood/成就/贴纸 BLoC 接入 API + B7 测试扩展
前端改动:
- 新建设置页面 (主题切换/关于/隐私政策/用户协议/儿童隐私保护)
- SettingsBloc 注册到 MultiRepositoryProvider 全局可访问
- MoodBloc 修复编译错误 + 接入 /diary/stats/mood API
- MoodPage 添加错误状态展示和重试按钮
- AchievementBloc + 页面改造接入 /diary/achievements API
- StickerBloc + 页面改造接入 /diary/sticker-packs API
- TemplateBloc + 页面改造接入 /diary/templates API
- ProfilePage 设置入口改为跳转 /settings
- 添加 /settings 路由

后端改动:
- 扩展 mood_stats_service 测试 (连续天数算法/心情计数/边界场景)
- 新增 class_service 测试 (班级码生成/唯一性/错误映射)
- 新增 achievement_service 测试 (DTO 结构/序列化/map 构建)
- 新增 sticker_service 测试 (DTO 序列化/错误处理)
- 扩展 dto.rs 测试 (achievement/mood_stats/sticker/template/notification)
- 清理 2 个 unused import warning

验证:
- cargo check 0 error 0 warning
- flutter analyze 0 error
2026-06-01 11:19:43 +08:00

94 lines
2.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.
// 暖记 App 根组件 — MaterialApp + BLoC Provider 注入
//
// 依赖注入结构:
// MultiRepositoryProvider
// ├─ ApiClient
// ├─ AuthRepository
// ├─ JournalRepository (RemoteJournalRepository)
// └─ ClassRepository
// └─ BlocProvider<AuthBloc>
// └─ MaterialApp.router
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'core/theme/app_theme.dart';
import 'core/routing/app_router.dart';
import 'data/remote/api_client.dart';
import 'data/repositories/auth_repository.dart';
import 'data/repositories/journal_repository.dart';
import 'data/repositories/remote_journal_repository.dart';
import 'data/repositories/class_repository.dart';
import 'features/auth/bloc/auth_bloc.dart';
import 'features/profile/bloc/settings_bloc.dart';
/// 暖记 App — 根组件
class NuanjiApp extends StatelessWidget {
const NuanjiApp({super.key});
@override
Widget build(BuildContext context) {
// 创建全局依赖App 生命周期内单例)
final apiClient = ApiClient();
final authRepository = AuthRepository(apiClient: apiClient);
final journalRepository = RemoteJournalRepository(api: apiClient);
final classRepository = ClassRepository(api: apiClient);
final settingsBloc = SettingsBloc();
final authBloc = AuthBloc(authRepository: authRepository);
// 启动时检查认证状态
authBloc.add(const AppStarted());
// 认证成功后注入 JWT token 到 ApiClient
authBloc.stream.listen((state) {
if (state is Authenticated) {
// TODO: 从 SecureStorage 读取 token 并设置
// apiClient.setToken(token);
} else {
apiClient.clearToken();
}
});
return MultiRepositoryProvider(
providers: [
RepositoryProvider<ApiClient>.value(value: apiClient),
RepositoryProvider<AuthRepository>.value(value: authRepository),
RepositoryProvider<JournalRepository>.value(value: journalRepository),
RepositoryProvider<ClassRepository>.value(value: classRepository),
RepositoryProvider<SettingsBloc>.value(value: settingsBloc),
],
child: BlocProvider<AuthBloc>.value(
value: authBloc,
child: ListenableBuilder(
listenable: settingsBloc,
builder: (context, _) => _AppView(
router: createAppRouter(authBloc),
themeMode: settingsBloc.state.themeMode,
),
),
),
);
}
}
/// App 视图 — MaterialApp.router 包装
class _AppView extends StatelessWidget {
final GoRouter router;
final ThemeMode themeMode;
const _AppView({required this.router, this.themeMode = ThemeMode.system});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: '暖记',
debugShowCheckedModeBanner: false,
theme: AppTheme.light(),
darkTheme: AppTheme.dark(),
themeMode: themeMode,
routerConfig: router,
);
}
}