feat(app): BLoC 集成 Repository + SettingsBloc 主题切换

全局依赖注入:
- app.dart 注入 JournalRepository + ClassRepository + SettingsBloc
- ApiClient token 自动注入(监听 AuthBloc 状态)

BLoC 重构 (占位数据 → Repository):
- CalendarBloc: 通过 JournalRepository 加载月度日记
- ClassBloc: 通过 ClassRepository + JournalRepository 加载班级数据
- 新增 ClassJoin 事件支持班级码加入
- HomeBloc: 加载最近日记 + 心情概览 + 连续天数 + 今日是否已写

设置系统:
- SettingsBloc: ThemeMode 切换 (system/light/dark)
- app.dart 通过 ListenableBuilder 响应主题变化
- HomeBloc 支持下拉刷新

首页增强:
- 连续天数徽章 + 今日已写标记 + 最常用心情高亮
- RefreshIndicator 下拉刷新
- 日记列表卡片显示日期

验证: flutter analyze 0 error
This commit is contained in:
iven
2026-06-01 10:32:20 +08:00
parent 263ddf31a6
commit 860e9e5d22
9 changed files with 630 additions and 315 deletions

View File

@@ -1,9 +1,13 @@
// 暖记 App 根组件 — MaterialApp + BLoC Provider 注入
//
// 依赖注入结构:
// RepositoryProvider<AuthRepository> — 认证仓库(全局唯一)
// BlocProvider<AuthBloc> — 认证 BLoC全局唯一
// └─ MaterialApp.router — 路由(使用 auth 状态守卫)
// MultiRepositoryProvider
// ApiClient
// ├─ AuthRepository
// ├─ JournalRepository (RemoteJournalRepository)
// └─ ClassRepository
// └─ BlocProvider<AuthBloc>
// └─ MaterialApp.router
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -13,7 +17,11 @@ 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 {
@@ -24,19 +32,40 @@ class NuanjiApp extends StatelessWidget {
// 创建全局依赖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),
],
child: BlocProvider<AuthBloc>.value(
value: authBloc,
child: _AppView(router: createAppRouter(authBloc)),
child: ListenableBuilder(
listenable: settingsBloc,
builder: (context, _) => _AppView(
router: createAppRouter(authBloc),
themeMode: settingsBloc.state.themeMode,
),
),
),
);
}
@@ -45,8 +74,9 @@ class NuanjiApp extends StatelessWidget {
/// App 视图 — MaterialApp.router 包装
class _AppView extends StatelessWidget {
final GoRouter router;
final ThemeMode themeMode;
const _AppView({required this.router});
const _AppView({required this.router, this.themeMode = ThemeMode.system});
@override
Widget build(BuildContext context) {
@@ -55,7 +85,7 @@ class _AppView extends StatelessWidget {
debugShowCheckedModeBanner: false,
theme: AppTheme.light(),
darkTheme: AppTheme.dark(),
themeMode: ThemeMode.system,
themeMode: themeMode,
routerConfig: router,
);
}